From cb790c6d3881dfd0401181110312dcee3d0fe321 Mon Sep 17 00:00:00 2001
From: Siegfried Pammer <siegfriedpammer@gmail.com>
Date: Sat, 3 May 2025 13:29:19 +0200
Subject: [PATCH 1/6] Allow variable splitting of compiler-generated
 temporaries for Span<T>/ROS<T>

---
 .../IL/Transforms/SplitVariables.cs           | 24 +++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs
index 4699f9f57..b6f336697 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs
@@ -151,11 +151,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 			IType returnType = (call is NewObj) ? call.Method.DeclaringType : call.Method.ReturnType;
 			if (returnType.IsByRefLike)
 			{
-				// If the address is returned from the method, it check whether it's consumed immediately.
-				// This can still be fine, as long as we also check the consumer's other arguments for 'stloc targetVar'.
-				if (DetermineAddressUse(call, targetVar) != AddressUse.Immediate)
-					return AddressUse.Unknown;
+				// We exclude Span<T>.Item[int index] and ReadOnlySpan<T>.Item[int index], because it is known that this
+				// or members of this cannot be returned by the method.
+				if (!IsSpanOfTIndexerAccessor(call.Method))
+				{
+					// If the address is returned from the method, it check whether it's consumed immediately.
+					// This can still be fine, as long as we also check the consumer's other arguments for 'stloc targetVar'.
+					if (DetermineAddressUse(call, targetVar) != AddressUse.Immediate)
+						return AddressUse.Unknown;
+				}
 			}
+
 			foreach (var p in call.Method.Parameters)
 			{
 				// catch "out Span<int>" and similar
@@ -174,6 +180,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 			return AddressUse.Immediate;
 		}
 
+		static bool IsSpanOfTIndexerAccessor(IMethod method)
+		{
+			var declaringType = method.DeclaringType;
+			if (!declaringType.IsKnownType(KnownTypeCode.SpanOfT)
+				&& !declaringType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
+				return false;
+			return method.AccessorOwner is IProperty { IsIndexer: true, Name: "Item", Parameters: [var param], ReturnType: ByReferenceType { ElementType: var rt } }
+				&& param.Type.IsKnownType(KnownTypeCode.Int32) && rt.Equals(declaringType.TypeArguments[0]);
+		}
+
 		/// <summary>
 		/// Given 'ldloc ref_local' and 'ldloca target; stloc ref_local', returns the ldloca.
 		/// This function must return a non-null LdLoca for every use of a SupportedRefLocal.

From 79f7a188b0946295c89c62df53c3e0b77c1d2440 Mon Sep 17 00:00:00 2001
From: Siegfried Pammer <siegfriedpammer@gmail.com>
Date: Sun, 4 May 2025 08:54:45 +0200
Subject: [PATCH 2/6] Add support for C# 12 inline array expressions

---
 .../ICSharpCode.Decompiler.Tests.csproj       |   1 +
 .../PrettyTestRunner.cs                       |   6 +
 .../TestCases/Pretty/InlineArrayTests.cs      | 138 +++++++++++
 ICSharpCode.Decompiler/CSharp/CallBuilder.cs  |  17 ++
 .../CSharp/ExpressionBuilder.cs               |  22 ++
 .../CSharp/Resolver/CSharpConversions.cs      |   2 +-
 ICSharpCode.Decompiler/DecompilerSettings.cs  |  21 +-
 .../ICSharpCode.Decompiler.csproj             |   1 +
 ICSharpCode.Decompiler/IL/Instructions.cs     | 149 +++++++++++
 ICSharpCode.Decompiler/IL/Instructions.tt     |   3 +
 .../IL/Transforms/ExpressionTransforms.cs     |   5 +
 .../IL/Transforms/ILInlining.cs               |  23 ++
 .../IL/Transforms/InlineArrayTransform.cs     | 234 ++++++++++++++++++
 .../Semantics/Conversion.cs                   |   5 +
 14 files changed, 625 insertions(+), 2 deletions(-)
 create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs
 create mode 100644 ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs

diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index 1a0d83379..110b3bb36 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -140,6 +140,7 @@
     <Compile Include="TestCases\ILPretty\MonoFixed.cs" />
     <Compile Include="TestCases\Pretty\Comparisons.cs" />
     <Compile Include="TestCases\Pretty\GloballyQualifiedTypeInStringInterpolation.cs" />
+    <Compile Include="TestCases\Pretty\InlineArrayTests.cs" />
     <Compile Include="TestCases\Pretty\Issue3406.cs" />
     <Compile Include="TestCases\Pretty\Issue3439.cs" />
     <Compile Include="TestCases\Pretty\Issue3442.cs" />
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index 25c5e0191..12020c28b 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -755,6 +755,12 @@ namespace ICSharpCode.Decompiler.Tests
 			await RunForLibrary(cscOptions: cscOptions);
 		}
 
+		[Test]
+		public async Task InlineArrayTests([ValueSource(nameof(roslyn4OrNewerOptions))] CompilerOptions cscOptions)
+		{
+			await RunForLibrary(cscOptions: cscOptions);
+		}
+
 		async Task RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, Action<DecompilerSettings> configureDecompiler = null)
 		{
 			await Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, configureDecompiler);
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs
new file mode 100644
index 000000000..79b800c39
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
+{
+	public class InlineArrayTests
+	{
+		[InlineArray(16)]
+		public struct Byte16
+		{
+			private byte elem;
+		}
+
+		[InlineArray(16)]
+		public struct Generic16<T>
+		{
+			private T elem;
+		}
+
+		public byte Byte0()
+		{
+			return GetByte16()[0];
+		}
+
+		public byte GenericByte0()
+		{
+			return GetGeneric<byte>()[0];
+		}
+
+		public byte Byte5()
+		{
+			return GetByte16()[5];
+		}
+
+		public byte GenericByte5()
+		{
+			return GetGeneric<byte>()[5];
+		}
+
+		public byte ByteN()
+		{
+			return GetByte16()[GetIndex()];
+		}
+
+		public byte GenericByteN()
+		{
+			return GetGeneric<byte>()[GetIndex()];
+		}
+
+		public byte Byte0(Byte16 array, byte value)
+		{
+			return array[0] = value;
+		}
+
+		public byte GenericByte0(Generic16<byte> array, byte value)
+		{
+			return array[0] = value;
+		}
+
+		public byte Byte5(Byte16 array, byte value)
+		{
+			return array[5] = value;
+		}
+
+		public byte GenericByte5(Generic16<byte> array, byte value)
+		{
+			return array[5] = value;
+		}
+
+		public byte ByteN(Byte16 array, byte value)
+		{
+			return array[GetIndex()] = value;
+		}
+
+		public byte GenericByteN(Generic16<byte> array, byte value)
+		{
+			return array[GetIndex()] = value;
+		}
+
+		public byte VariableSplitting(Byte16 array, byte value)
+		{
+			return array[GetIndex()] = (array[GetIndex() + 1] = value);
+		}
+
+		public void OverloadResolution()
+		{
+			Receiver(GetByte16());
+			Receiver((object)GetByte16());
+			Byte16 buffer = GetByte16();
+			Receiver((Span<byte>)buffer);
+			Byte16 buffer2 = GetByte16();
+			Receiver((ReadOnlySpan<byte>)buffer2);
+			Byte16 buffer3 = GetByte16();
+			ReceiverSpan((Span<byte>)buffer3);
+			Byte16 buffer4 = GetByte16();
+			ReceiverReadOnlySpan((ReadOnlySpan<byte>)buffer4);
+		}
+
+		public Byte16 GetByte16()
+		{
+			return default(Byte16);
+		}
+
+		public Generic16<T> GetGeneric<T>()
+		{
+			return default(Generic16<T>);
+		}
+
+		public int GetIndex()
+		{
+			return 0;
+		}
+
+		public void Receiver(Span<byte> span)
+		{
+		}
+
+		public void Receiver(ReadOnlySpan<byte> span)
+		{
+		}
+
+		public void Receiver(Byte16 span)
+		{
+		}
+
+		public void Receiver(object span)
+		{
+		}
+
+		public void ReceiverSpan(Span<byte> span)
+		{
+		}
+
+		public void ReceiverReadOnlySpan(ReadOnlySpan<byte> span)
+		{
+		}
+	}
+}
diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
index b8b6680e5..298adf7c4 100644
--- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
@@ -463,6 +463,23 @@ namespace ICSharpCode.Decompiler.CSharp
 				return HandleImplicitConversion(method, argumentList.Arguments[0]);
 			}
 
+			if (settings.InlineArrays
+				&& method is { DeclaringType.FullName: "<PrivateImplementationDetails>", Name: "InlineArrayAsSpan" or "InlineArrayAsReadOnlySpan" }
+				&& argumentList.Length == 2)
+			{
+				argumentList.CheckNoNamedOrOptionalArguments();
+				var argument = argumentList.Arguments[0];
+				var targetType = method.ReturnType;
+				if (argument.Expression is DirectionExpression { FieldDirection: FieldDirection.In or FieldDirection.Ref, Expression: var lvalueExpr })
+				{
+					// `(TargetType)(in arg)` is invalid syntax.
+					// Also, `f(in arg)` is invalid when there's an implicit conversion involved.
+					argument = argument.UnwrapChild(lvalueExpr);
+				}
+				return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression)
+					.WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, Conversion.InlineArrayConversion));
+			}
+
 			if (settings.LiftNullables && method.Name == "GetValueOrDefault"
 				&& method.DeclaringType.IsKnownType(KnownTypeCode.NullableOfT)
 				&& method.DeclaringType.TypeArguments[0].IsKnownType(KnownTypeCode.Boolean)
diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
index 13ec88dcc..43fd7bb78 100644
--- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
@@ -3130,6 +3130,28 @@ namespace ICSharpCode.Decompiler.CSharp
 				.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref));
 		}
 
+		protected internal override TranslatedExpression VisitLdElemaInlineArray(LdElemaInlineArray inst, TranslationContext context)
+		{
+			TranslatedExpression arrayExpr = TranslateTarget(
+				inst.Array,
+				nonVirtualInvocation: true,
+				memberStatic: false,
+				memberDeclaringType: inst.Type
+			);
+			var inlineArrayElementType = GetInlineArrayElementType(inst.Type);
+			IndexerExpression indexerExpr = new IndexerExpression(
+					arrayExpr, inst.Indices.Select(i => TranslateArrayIndex(i).Expression)
+			);
+			TranslatedExpression expr = indexerExpr.WithILInstruction(inst).WithRR(new ResolveResult(inlineArrayElementType));
+			return new DirectionExpression(FieldDirection.Ref, expr)
+				.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref));
+
+			IType GetInlineArrayElementType(IType arrayType)
+			{
+				return arrayType?.GetFields(f => !f.IsStatic).SingleOrDefault()?.Type ?? SpecialType.UnknownType;
+			}
+		}
+
 		TranslatedExpression TranslateArrayIndex(ILInstruction i)
 		{
 			var input = Translate(i);
diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
index 6b19a4762..f190a554f 100644
--- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
+++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
@@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
 				if (c != Conversion.None)
 					return c;
 			}
-			if (resolveResult is InterpolatedStringResolveResult isrr)
+			if (resolveResult is InterpolatedStringResolveResult)
 			{
 				if (toType.IsKnownType(KnownTypeCode.IFormattable) || toType.IsKnownType(KnownTypeCode.FormattableString))
 					return Conversion.ImplicitInterpolatedStringConversion;
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index 0ccfec2fa..bbf7a0055 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -164,12 +164,13 @@ namespace ICSharpCode.Decompiler
 			{
 				refReadOnlyParameters = false;
 				usePrimaryConstructorSyntaxForNonRecordTypes = false;
+				inlineArrays = false;
 			}
 		}
 
 		public CSharp.LanguageVersion GetMinimumRequiredVersion()
 		{
-			if (refReadOnlyParameters || usePrimaryConstructorSyntaxForNonRecordTypes)
+			if (refReadOnlyParameters || usePrimaryConstructorSyntaxForNonRecordTypes || inlineArrays)
 				return CSharp.LanguageVersion.CSharp12_0;
 			if (scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift || checkedOperators)
 				return CSharp.LanguageVersion.CSharp11_0;
@@ -2053,6 +2054,24 @@ namespace ICSharpCode.Decompiler
 			}
 		}
 
+		bool inlineArrays = true;
+
+		/// <summary>
+		/// Gets/Sets whether C# 12.0 inline array uses should be transformed.
+		/// </summary>
+		[Category("C# 12.0 / VS 2022.8")]
+		[Description("DecompilerSettings.InlineArrays")]
+		public bool InlineArrays {
+			get { return inlineArrays; }
+			set {
+				if (inlineArrays != value)
+				{
+					inlineArrays = value;
+					OnPropertyChanged();
+				}
+			}
+		}
+
 		bool separateLocalVariableDeclarations = false;
 
 		/// <summary>
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index 5e02ca705..7e2e19341 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -107,6 +107,7 @@
     <Compile Include="DecompilationProgress.cs" />
     <Compile Include="Disassembler\IEntityProcessor.cs" />
     <Compile Include="Disassembler\SortByNameProcessor.cs" />
+    <Compile Include="IL\Transforms\InlineArrayTransform.cs" />
     <Compile Include="IL\Transforms\RemoveUnconstrainedGenericReferenceTypeCheck.cs" />
     <Compile Include="Metadata\MetadataFile.cs" />
     <Compile Include="Metadata\ModuleReferenceMetadata.cs" />
diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs
index 4f3ef843d..4094f9c9e 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.cs
+++ b/ICSharpCode.Decompiler/IL/Instructions.cs
@@ -191,6 +191,8 @@ namespace ICSharpCode.Decompiler.IL
 		LdLen,
 		/// <summary>Load address of array element.</summary>
 		LdElema,
+		/// <summary>Load address of inline array element.</summary>
+		LdElemaInlineArray,
 		/// <summary>Retrieves a pinnable reference for the input object.
 		/// The input must be an object reference (O).
 		/// If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty.
@@ -4998,6 +5000,127 @@ namespace ICSharpCode.Decompiler.IL
 	}
 }
 namespace ICSharpCode.Decompiler.IL
+{
+	/// <summary>Load address of inline array element.</summary>
+	public sealed partial class LdElemaInlineArray : ILInstruction
+	{
+		public LdElemaInlineArray(IType type, ILInstruction array, params ILInstruction[] indices) : base(OpCode.LdElemaInlineArray)
+		{
+			this.type = type;
+			this.Array = array;
+			this.Indices = new InstructionCollection<ILInstruction>(this, 1);
+			this.Indices.AddRange(indices);
+		}
+		IType type;
+		/// <summary>Returns the type operand.</summary>
+		public IType Type {
+			get { return type; }
+			set { type = value; InvalidateFlags(); }
+		}
+		public static readonly SlotInfo ArraySlot = new SlotInfo("Array", canInlineInto: true);
+		ILInstruction array = null!;
+		public ILInstruction Array {
+			get { return this.array; }
+			set {
+				ValidateChild(value);
+				SetChildInstruction(ref this.array, value, 0);
+			}
+		}
+		public static readonly SlotInfo IndicesSlot = new SlotInfo("Indices", canInlineInto: true);
+		public InstructionCollection<ILInstruction> Indices { get; private set; }
+		protected sealed override int GetChildCount()
+		{
+			return 1 + Indices.Count;
+		}
+		protected sealed override ILInstruction GetChild(int index)
+		{
+			switch (index)
+			{
+				case 0:
+					return this.array;
+				default:
+					return this.Indices[index - 1];
+			}
+		}
+		protected sealed override void SetChild(int index, ILInstruction value)
+		{
+			switch (index)
+			{
+				case 0:
+					this.Array = value;
+					break;
+				default:
+					this.Indices[index - 1] = (ILInstruction)value;
+					break;
+			}
+		}
+		protected sealed override SlotInfo GetChildSlot(int index)
+		{
+			switch (index)
+			{
+				case 0:
+					return ArraySlot;
+				default:
+					return IndicesSlot;
+			}
+		}
+		public sealed override ILInstruction Clone()
+		{
+			var clone = (LdElemaInlineArray)ShallowClone();
+			clone.Array = this.array.Clone();
+			clone.Indices = new InstructionCollection<ILInstruction>(clone, 1);
+			clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone()));
+			return clone;
+		}
+		public override StackType ResultType { get { return StackType.Ref; } }
+		/// <summary>Gets whether the 'readonly' prefix was applied to this instruction.</summary>
+		public bool IsReadOnly { get; set; }
+		protected override InstructionFlags ComputeFlags()
+		{
+			return array.Flags | Indices.Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags) | InstructionFlags.MayThrow;
+		}
+		public override InstructionFlags DirectFlags {
+			get {
+				return InstructionFlags.MayThrow;
+			}
+		}
+		public override void WriteTo(ITextOutput output, ILAstWritingOptions options)
+		{
+			WriteILRange(output, options);
+			if (IsReadOnly)
+				output.Write("readonly.");
+			output.Write(OpCode);
+			output.Write(' ');
+			type.WriteTo(output);
+			output.Write('(');
+			this.array.WriteTo(output, options);
+			foreach (var indices in Indices)
+			{
+				output.Write(", ");
+				indices.WriteTo(output, options);
+			}
+			output.Write(')');
+		}
+		public override void AcceptVisitor(ILVisitor visitor)
+		{
+			visitor.VisitLdElemaInlineArray(this);
+		}
+		public override T AcceptVisitor<T>(ILVisitor<T> visitor)
+		{
+			return visitor.VisitLdElemaInlineArray(this);
+		}
+		public override T AcceptVisitor<C, T>(ILVisitor<C, T> visitor, C context)
+		{
+			return visitor.VisitLdElemaInlineArray(this, context);
+		}
+		protected internal override bool PerformMatch(ILInstruction? other, ref Patterns.Match match)
+		{
+			var o = other as LdElemaInlineArray;
+			return o != null && type.Equals(o.type) && this.array.PerformMatch(o.array, ref match) && Patterns.ListMatch.DoMatch(this.Indices, o.Indices, ref match) && IsReadOnly == o.IsReadOnly;
+		}
+	}
+}
+namespace ICSharpCode.Decompiler.IL
 {
 	/// <summary>Retrieves a pinnable reference for the input object.
 	/// The input must be an object reference (O).
@@ -7235,6 +7358,10 @@ namespace ICSharpCode.Decompiler.IL
 		{
 			Default(inst);
 		}
+		protected internal virtual void VisitLdElemaInlineArray(LdElemaInlineArray inst)
+		{
+			Default(inst);
+		}
 		protected internal virtual void VisitGetPinnableReference(GetPinnableReference inst)
 		{
 			Default(inst);
@@ -7641,6 +7768,10 @@ namespace ICSharpCode.Decompiler.IL
 		{
 			return Default(inst);
 		}
+		protected internal virtual T VisitLdElemaInlineArray(LdElemaInlineArray inst)
+		{
+			return Default(inst);
+		}
 		protected internal virtual T VisitGetPinnableReference(GetPinnableReference inst)
 		{
 			return Default(inst);
@@ -8047,6 +8178,10 @@ namespace ICSharpCode.Decompiler.IL
 		{
 			return Default(inst, context);
 		}
+		protected internal virtual T VisitLdElemaInlineArray(LdElemaInlineArray inst, C context)
+		{
+			return Default(inst, context);
+		}
 		protected internal virtual T VisitGetPinnableReference(GetPinnableReference inst, C context)
 		{
 			return Default(inst, context);
@@ -8223,6 +8358,7 @@ namespace ICSharpCode.Decompiler.IL
 			"sizeof",
 			"ldlen",
 			"ldelema",
+			"ldelema.inlinearray",
 			"get.pinnable.reference",
 			"string.to.int",
 			"expression.tree.cast",
@@ -8849,6 +8985,19 @@ namespace ICSharpCode.Decompiler.IL
 			array = default(ILInstruction);
 			return false;
 		}
+		public bool MatchLdElemaInlineArray([NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? array)
+		{
+			var inst = this as LdElemaInlineArray;
+			if (inst != null)
+			{
+				type = inst.Type;
+				array = inst.Array;
+				return true;
+			}
+			type = default(IType);
+			array = default(ILInstruction);
+			return false;
+		}
 		public bool MatchGetPinnableReference([NotNullWhen(true)] out ILInstruction? argument, out IMethod? method)
 		{
 			var inst = this as GetPinnableReference;
diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt
index 6f42205af..8c2a5bf5e 100644
--- a/ICSharpCode.Decompiler/IL/Instructions.tt
+++ b/ICSharpCode.Decompiler/IL/Instructions.tt
@@ -290,6 +290,9 @@
 			CustomClassName("LdElema"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true),
 			BoolFlag("WithSystemIndex"),
 			MayThrowIfNotDelayed, ResultType("Ref"), SupportsReadonlyPrefix),
+		new OpCode("ldelema.inlinearray", "Load address of inline array element.",
+			CustomClassName("LdElemaInlineArray"), HasTypeOperand, CustomChildren(new [] { new ArgumentInfo("array"), new ArgumentInfo("indices") { IsCollection = true } }, true),
+			MayThrow, ResultType("Ref"), SupportsReadonlyPrefix),
 		new OpCode("get.pinnable.reference", "Retrieves a pinnable reference for the input object." + Environment.NewLine
 				+ "The input must be an object reference (O)." + Environment.NewLine
 				+ "If the input is an array/string, evaluates to a reference to the first element/character, or to a null reference if the array is null or empty." + Environment.NewLine
diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
index 57b475694..90907922b 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
@@ -277,6 +277,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 
 		protected internal override void VisitCall(Call inst)
 		{
+			if (context.Settings.InlineArrays && InlineArrayTransform.RunOnExpression(inst, context))
+			{
+				return;
+			}
+
 			if (NullableLiftingTransform.MatchGetValueOrDefault(inst, out var nullableValue, out var fallback)
 				&& SemanticHelper.IsPure(fallback.Flags))
 			{
diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
index 2b92f6cc5..af52fd58b 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
@@ -333,6 +333,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 						throw new InvalidOperationException("invalid expression classification");
 				}
 			}
+			else if (loadInst.Parent is LdElemaInlineArray)
+			{
+				return true;
+			}
 			else if (IsPassedToReadOnlySpanOfCharCtor(loadInst))
 			{
 				// Always inlining is possible here, because it's an 'in' or 'ref readonly' parameter
@@ -344,6 +348,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 				// already inlined.
 				return true;
 			}
+			if (IsPassedToInlineArrayAsSpan(loadInst))
+			{
+				// Inlining is not allowed
+				return false;
+			}
 			else if (IsPassedToInParameter(loadInst))
 			{
 				if (options.HasFlag(InliningOptions.Aggressive))
@@ -377,6 +386,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 			}
 		}
 
+		private static bool IsPassedToInlineArrayAsSpan(LdLoca loadInst)
+		{
+			if (loadInst.Parent is not Call call)
+				return false;
+			var method = call.Method;
+			var declaringType = method.DeclaringType;
+			return declaringType.ReflectionName == "<PrivateImplementationDetails>"
+				&& method.Name is "InlineArrayAsReadOnlySpan" or "InlineArrayAsSpan"
+				&& method.Parameters is [var arg, var length]
+				&& (method.ReturnType.IsKnownType(KnownTypeCode.SpanOfT) || method.ReturnType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
+				&& arg.Type is ByReferenceType
+				&& length.Type.IsKnownType(KnownTypeCode.Int32);
+		}
+
 		internal static bool MethodRequiresCopyForReadonlyLValue(IMethod method, IType constrainedTo = null)
 		{
 			if (method == null)
diff --git a/ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs
new file mode 100644
index 000000000..8631b09eb
--- /dev/null
+++ b/ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs
@@ -0,0 +1,234 @@
+// Copyright (c) 2025 Siegfried Pammer
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+#nullable enable
+
+using System.Diagnostics.CodeAnalysis;
+
+using ICSharpCode.Decompiler.TypeSystem;
+
+namespace ICSharpCode.Decompiler.IL.Transforms
+{
+	static class InlineArrayTransform
+	{
+		internal static bool RunOnExpression(Call inst, StatementTransformContext context)
+		{
+			if (MatchSpanIndexerWithInlineArrayAsSpan(inst, out var type, out var addr, out var index, out bool isReadOnly))
+			{
+				if (isReadOnly)
+				{
+					context.Step("call get_Item(addressof System.ReadOnlySpan{T}(call InlineArrayAsReadOnlySpan(addr)), index) -> readonly.ldelema.inlinearray(addr, index)", inst);
+				}
+				else
+				{
+					context.Step("call get_Item(addressof System.Span{T}(call InlineArrayAsSpan(addr)), index) -> ldelema.inlinearray(addr, index)", inst);
+				}
+				inst.ReplaceWith(new LdElemaInlineArray(type, addr, index) { IsReadOnly = isReadOnly }.WithILRange(inst));
+				return true;
+			}
+
+			if (MatchInlineArrayElementRef(inst, out type, out addr, out index, out isReadOnly))
+			{
+				if (isReadOnly)
+				{
+					context.Step("call InlineArrayElementRefReadOnly(addr, index) -> readonly.ldelema.inlinearray(addr, index)", inst);
+				}
+				else
+				{
+					context.Step("call InlineArrayElementRef(addr, index) -> ldelema.inlinearray(addr, index)", inst);
+				}
+				inst.ReplaceWith(new LdElemaInlineArray(type, addr, index) { IsReadOnly = isReadOnly }.WithILRange(inst));
+				return true;
+			}
+
+			if (MatchInlineArrayFirstElementRef(inst, out type, out addr, out isReadOnly))
+			{
+				if (isReadOnly)
+				{
+					context.Step("call InlineArrayFirstElementRefReadOnly(addr) -> readonly.ldelema.inlinearray(addr, ldc.i4 0)", inst);
+				}
+				else
+				{
+					context.Step("call InlineArrayFirstElementRef(addr) -> ldelema.inlinearray(addr, ldc.i4 0)", inst);
+				}
+				inst.ReplaceWith(new LdElemaInlineArray(type, addr, new LdcI4(0)) { IsReadOnly = isReadOnly }.WithILRange(inst));
+				return true;
+			}
+
+			return false;
+		}
+
+		/// <summary>
+		/// Matches call get_Item(addressof System.(ReadOnly)Span[[T]](call InlineArrayAs(ReadOnly)Span(addr, length)), index)
+		/// </summary>
+		static bool MatchSpanIndexerWithInlineArrayAsSpan(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, [NotNullWhen(true)] out ILInstruction? index, out bool isReadOnly)
+		{
+			isReadOnly = false;
+			type = null;
+			addr = null;
+			index = null;
+
+			if (MatchSpanGetItem(inst.Method, "ReadOnlySpan"))
+			{
+				isReadOnly = true;
+
+				if (inst.Arguments is not [AddressOf { Value: Call targetInst, Type: var typeInfo }, var indexInst])
+					return false;
+
+				if (!MatchInlineArrayHelper(targetInst.Method, "InlineArrayAsReadOnlySpan", out var inlineArrayType))
+					return false;
+
+				if (targetInst.Arguments is not [var addrInst, LdcI4])
+					return false;
+
+				type = inlineArrayType;
+				addr = addrInst;
+				index = indexInst;
+
+				return true;
+			}
+			else if (MatchSpanGetItem(inst.Method, "Span"))
+			{
+				if (inst.Arguments is not [AddressOf { Value: Call targetInst, Type: var typeInfo }, var indexInst])
+					return false;
+
+				if (!MatchInlineArrayHelper(targetInst.Method, "InlineArrayAsSpan", out var inlineArrayType))
+					return false;
+
+				if (targetInst.Arguments is not [var addrInst, LdcI4])
+					return false;
+
+				type = inlineArrayType;
+				addr = addrInst;
+				index = indexInst;
+
+				return true;
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+		/// <summary>
+		/// Matches call InlineArrayElementRef(ReadOnly)(addr, index)
+		/// </summary>
+		static bool MatchInlineArrayElementRef(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, [NotNullWhen(true)] out ILInstruction? index, out bool isReadOnly)
+		{
+			type = null;
+			addr = null;
+			index = null;
+			isReadOnly = false;
+
+			if (inst.Arguments is not [var addrInst, var indexInst])
+				return false;
+
+			if (MatchInlineArrayHelper(inst.Method, "InlineArrayElementRef", out var inlineArrayType))
+			{
+				isReadOnly = false;
+				type = inlineArrayType;
+				addr = addrInst;
+				index = indexInst;
+				return true;
+			}
+
+			if (MatchInlineArrayHelper(inst.Method, "InlineArrayElementRefReadOnly", out inlineArrayType))
+			{
+				isReadOnly = true;
+				type = inlineArrayType;
+				addr = addrInst;
+				index = indexInst;
+				return true;
+			}
+
+			return false;
+		}
+
+		private static bool MatchInlineArrayFirstElementRef(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, out bool isReadOnly)
+		{
+			type = null;
+			addr = null;
+			isReadOnly = false;
+
+			if (inst.Arguments is not [var addrInst])
+				return false;
+
+			if (MatchInlineArrayHelper(inst.Method, "InlineArrayFirstElementRef", out var inlineArrayType))
+			{
+				isReadOnly = false;
+				type = inlineArrayType;
+				addr = addrInst;
+				return true;
+			}
+
+			if (MatchInlineArrayHelper(inst.Method, "InlineArrayFirstElementRefReadOnly", out inlineArrayType))
+			{
+				isReadOnly = true;
+				type = inlineArrayType;
+				addr = addrInst;
+				return true;
+			}
+
+			return false;
+		}
+
+		static bool MatchSpanGetItem(IMethod method, string typeName)
+		{
+			return method is {
+				IsStatic: false,
+				Name: "get_Item",
+				DeclaringType: { Namespace: "System", Name: string name, TypeParameterCount: 1, DeclaringType: null }
+			} && typeName == name;
+		}
+
+		static bool MatchInlineArrayHelper(IMethod method, string methodName, [NotNullWhen(true)] out IType? inlineArrayType)
+		{
+			inlineArrayType = null;
+			if (method is not {
+				IsStatic: true, Name: var name,
+				DeclaringType: { FullName: "<PrivateImplementationDetails>", TypeParameterCount: 0 },
+				TypeArguments: [var bufferType, _],
+				Parameters: var parameters
+			})
+			{
+				return false;
+			}
+
+			if (methodName != name)
+				return false;
+
+			if (methodName.Contains("FirstElement"))
+			{
+				if (parameters is not [{ Type: ByReferenceType { ElementType: var type } }])
+					return false;
+				if (!type.Equals(bufferType))
+					return false;
+			}
+			else
+			{
+				if (parameters is not [{ Type: ByReferenceType { ElementType: var type } }, { Type: var lengthOrIndexParameterType }])
+					return false;
+				if (!type.Equals(bufferType) || !lengthOrIndexParameterType.IsKnownType(KnownTypeCode.Int32))
+					return false;
+			}
+
+			inlineArrayType = bufferType;
+			return true;
+		}
+	}
+}
diff --git a/ICSharpCode.Decompiler/Semantics/Conversion.cs b/ICSharpCode.Decompiler/Semantics/Conversion.cs
index a92735b4f..4bcc1d766 100644
--- a/ICSharpCode.Decompiler/Semantics/Conversion.cs
+++ b/ICSharpCode.Decompiler/Semantics/Conversion.cs
@@ -87,6 +87,11 @@ namespace ICSharpCode.Decompiler.Semantics
 		/// </summary>
 		public static readonly Conversion ThrowExpressionConversion = new BuiltinConversion(true, 11);
 
+		/// <summary>
+		/// C# 12 inline array implicitly being converted to <see cref="System.Span{T}"/> or <see cref="System.ReadOnlySpan{T}"/>.
+		/// </summary>
+		public static readonly Conversion InlineArrayConversion = new BuiltinConversion(true, 12);
+
 		public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false)
 		{
 			if (operatorMethod == null)

From 667036c54ea8bc3f5f23e43bd5f4813b340f8c99 Mon Sep 17 00:00:00 2001
From: Siegfried Pammer <siegfriedpammer@gmail.com>
Date: Fri, 30 May 2025 20:05:42 +0200
Subject: [PATCH 3/6] Add support for InlineArrayConversion

---
 .../TestCases/Correctness/Conversions.cs      | 32 +++++++++++++++++++
 .../TestCases/Pretty/InlineArrayTests.cs      |  4 +--
 .../CSharp/Resolver/CSharpConversions.cs      | 10 +++++-
 .../Implementation/KnownAttributes.cs         |  7 +++-
 .../TypeSystem/TypeSystemExtensions.cs        | 10 ++++++
 5 files changed, 59 insertions(+), 4 deletions(-)

diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Conversions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Conversions.cs
index 01b76929d..59e94ed4b 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Conversions.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/Conversions.cs
@@ -19,6 +19,7 @@
 // #include "../../../ICSharpCode.Decompiler/Util/CSharpPrimitiveCast.cs"
 
 using System;
+using System.Runtime.CompilerServices;
 
 using ICSharpCode.Decompiler.Util;
 
@@ -111,6 +112,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
 
 			Console.WriteLine(ReadZeroTerminatedString("Hello World!".Length));
 			C1.Test();
+#if ROSLYN2 && !NET40
+			C3.Run();
+#endif
 		}
 
 		static void RunTest(bool checkForOverflow)
@@ -199,4 +203,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
 			return new C1();
 		}
 	}
+
+#if ROSLYN2 && !NET40
+	class C3
+	{
+		[InlineArray(4)] struct MyArray { private int elem; }
+
+		static void Foo(object o)
+		{
+			Console.WriteLine("Foo(object) called");
+		}
+
+		static void Foo(ReadOnlySpan<int> o)
+		{
+			Console.WriteLine("Foo(ReadOnlySpan<int>) called");
+		}
+
+		static void Test(MyArray arr)
+		{
+			Foo((object)arr);
+		}
+
+		public static void Run()
+		{
+			Console.WriteLine("C3.Run() called");
+			Test(default);
+		}
+	}
+#endif
 }
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs
index 79b800c39..886947d31 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs
@@ -91,9 +91,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
 			Byte16 buffer2 = GetByte16();
 			Receiver((ReadOnlySpan<byte>)buffer2);
 			Byte16 buffer3 = GetByte16();
-			ReceiverSpan((Span<byte>)buffer3);
+			ReceiverSpan(buffer3);
 			Byte16 buffer4 = GetByte16();
-			ReceiverReadOnlySpan((ReadOnlySpan<byte>)buffer4);
+			ReceiverReadOnlySpan(buffer4);
 		}
 
 		public Byte16 GetByte16()
diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
index f190a554f..0d1789ea7 100644
--- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
+++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
@@ -230,12 +230,20 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
 				if (c != Conversion.None)
 					return c;
 			}
+			if ((toType.IsKnownType(KnownTypeCode.SpanOfT) || toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
+				&& fromType.IsInlineArrayType())
+			{
+				var @field = fromType.GetFields(f => !f.IsStatic, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault();
+				var spanElementType = toType.TypeArguments[0];
+				if (field != null && IdentityConversion(field.ReturnType, spanElementType))
+					return Conversion.InlineArrayConversion;
+			}
 			return Conversion.None;
 		}
 
 		/// <summary>
 		/// Gets whether the type 'fromType' is convertible to 'toType'
-		/// using one of the conversions allowed when satisying constraints (§4.4.4)
+		/// using one of the conversions allowed when satisfying constraints (§4.4.4)
 		/// </summary>
 		public bool IsConstraintConvertible(IType fromType, IType toType)
 		{
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
index 59d2c19eb..a9e59e0d9 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
@@ -112,11 +112,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
 
 		// C# 11 attributes:
 		RequiredAttribute,
+
+		// C# 12 attributes:
+		InlineArray,
 	}
 
 	public static class KnownAttributes
 	{
-		internal const int Count = (int)KnownAttribute.RequiredAttribute + 1;
+		internal const int Count = (int)KnownAttribute.InlineArray + 1;
 
 		static readonly TopLevelTypeName[] typeNames = new TopLevelTypeName[Count]{
 			default,
@@ -186,6 +189,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
 			new TopLevelTypeName("System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute"),
 			// C# 11 attributes:
 			new TopLevelTypeName("System.Runtime.CompilerServices", "RequiredMemberAttribute"),
+			// C# 12 attributes:
+			new TopLevelTypeName("System.Runtime.CompilerServices", "InlineArrayAttribute"),
 		};
 
 		public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr)
diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
index d4009bb31..54cfaf9a4 100644
--- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
@@ -306,6 +306,16 @@ namespace ICSharpCode.Decompiler.TypeSystem
 			}
 		}
 
+		public static bool IsInlineArrayType(this IType type)
+		{
+			if (type.Kind != TypeKind.Struct)
+				return false;
+			var td = type.GetDefinition();
+			if (td == null)
+				return false;
+			return td.HasAttribute(KnownAttribute.InlineArray);
+		}
+
 		/// <summary>
 		/// Gets whether the type is the specified known type.
 		/// For generic known types, this returns true for any parameterization of the type (and also for the definition itself).

From 101ddf878396229d789ccb7b456c62a6fee69aa0 Mon Sep 17 00:00:00 2001
From: Siegfried Pammer <siegfriedpammer@gmail.com>
Date: Thu, 5 Jun 2025 21:57:53 +0200
Subject: [PATCH 4/6] Add support for constant slices of InlineArrays

---
 .../TestCases/Pretty/InlineArrayTests.cs      | 17 +++++++++++++++
 ICSharpCode.Decompiler/CSharp/CallBuilder.cs  | 21 ++++++++++++++++++-
 .../CSharp/ExpressionBuilder.cs               |  7 +------
 .../CSharp/Resolver/CSharpConversions.cs      |  4 ++--
 .../TypeSystem/TypeSystemExtensions.cs        | 16 ++++++++++++++
 5 files changed, 56 insertions(+), 9 deletions(-)

diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs
index 886947d31..ea7a74338 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineArrayTests.cs
@@ -77,6 +77,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
 			return array[GetIndex()] = value;
 		}
 
+		public void Slice(Byte16 array)
+		{
+			Receiver(array[..8]);
+			Receiver((ReadOnlySpan<byte>)array[..8]);
+			ReceiverSpan(array[..8]);
+			ReceiverReadOnlySpan(array[..8]);
+		}
+
+		// TODO
+		//public void Slice(Byte16 array, int end)
+		//{
+		//	Receiver(array[..end]);
+		//	Receiver((ReadOnlySpan<byte>)array[..end]);
+		//	ReceiverSpan(array[..end]);
+		//	ReceiverReadOnlySpan(array[..end]);
+		//}
+
 		public byte VariableSplitting(Byte16 array, byte value)
 		{
 			return array[GetIndex()] = (array[GetIndex() + 1] = value);
diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
index 298adf7c4..058eff4e3 100644
--- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs
@@ -468,16 +468,35 @@ namespace ICSharpCode.Decompiler.CSharp
 				&& argumentList.Length == 2)
 			{
 				argumentList.CheckNoNamedOrOptionalArguments();
+				var arrayType = method.TypeArguments[0];
+				var arrayLength = arrayType.GetInlineArrayLength();
+				var arrayElementType = arrayType.GetInlineArrayElementType();
 				var argument = argumentList.Arguments[0];
+				var spanLengthExpr = argumentList.Arguments[1];
 				var targetType = method.ReturnType;
+				var spanType = typeSystem.FindType(KnownTypeCode.SpanOfT);
 				if (argument.Expression is DirectionExpression { FieldDirection: FieldDirection.In or FieldDirection.Ref, Expression: var lvalueExpr })
 				{
 					// `(TargetType)(in arg)` is invalid syntax.
 					// Also, `f(in arg)` is invalid when there's an implicit conversion involved.
 					argument = argument.UnwrapChild(lvalueExpr);
 				}
-				return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression)
+				if (spanLengthExpr.ResolveResult.ConstantValue is int spanLength && spanLength <= arrayLength)
+				{
+					if (spanLength < arrayLength)
+					{
+						argument = new IndexerExpression(argument.Expression, new BinaryOperatorExpression {
+							Operator = BinaryOperatorType.Range,
+							Right = spanLengthExpr.Expression
+						}).WithRR(new ResolveResult(new ParameterizedType(spanType, arrayElementType))).WithoutILInstruction();
+						if (targetType.IsKnownType(KnownTypeCode.SpanOfT))
+						{
+							return argument;
+						}
+					}
+					return new CastExpression(expressionBuilder.ConvertType(targetType), argument.Expression)
 					.WithRR(new ConversionResolveResult(targetType, argument.ResolveResult, Conversion.InlineArrayConversion));
+				}
 			}
 
 			if (settings.LiftNullables && method.Name == "GetValueOrDefault"
diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
index 43fd7bb78..4fc23581c 100644
--- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
@@ -3138,18 +3138,13 @@ namespace ICSharpCode.Decompiler.CSharp
 				memberStatic: false,
 				memberDeclaringType: inst.Type
 			);
-			var inlineArrayElementType = GetInlineArrayElementType(inst.Type);
+			var inlineArrayElementType = inst.Type.GetInlineArrayElementType();
 			IndexerExpression indexerExpr = new IndexerExpression(
 					arrayExpr, inst.Indices.Select(i => TranslateArrayIndex(i).Expression)
 			);
 			TranslatedExpression expr = indexerExpr.WithILInstruction(inst).WithRR(new ResolveResult(inlineArrayElementType));
 			return new DirectionExpression(FieldDirection.Ref, expr)
 				.WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref));
-
-			IType GetInlineArrayElementType(IType arrayType)
-			{
-				return arrayType?.GetFields(f => !f.IsStatic).SingleOrDefault()?.Type ?? SpecialType.UnknownType;
-			}
 		}
 
 		TranslatedExpression TranslateArrayIndex(ILInstruction i)
diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
index 0d1789ea7..6acbeaa49 100644
--- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
+++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
@@ -233,9 +233,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
 			if ((toType.IsKnownType(KnownTypeCode.SpanOfT) || toType.IsKnownType(KnownTypeCode.ReadOnlySpanOfT))
 				&& fromType.IsInlineArrayType())
 			{
-				var @field = fromType.GetFields(f => !f.IsStatic, GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault();
+				var elementType = fromType.GetInlineArrayElementType();
 				var spanElementType = toType.TypeArguments[0];
-				if (field != null && IdentityConversion(field.ReturnType, spanElementType))
+				if (IdentityConversion(elementType, spanElementType))
 					return Conversion.InlineArrayConversion;
 			}
 			return Conversion.None;
diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
index 54cfaf9a4..6b9dcb352 100644
--- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs
@@ -316,6 +316,22 @@ namespace ICSharpCode.Decompiler.TypeSystem
 			return td.HasAttribute(KnownAttribute.InlineArray);
 		}
 
+		public static int? GetInlineArrayLength(this IType type)
+		{
+			if (type.Kind != TypeKind.Struct)
+				return null;
+			var td = type.GetDefinition();
+			if (td == null)
+				return null;
+			var attr = td.GetAttribute(KnownAttribute.InlineArray);
+			return attr?.FixedArguments.FirstOrDefault().Value as int?;
+		}
+
+		public static IType GetInlineArrayElementType(this IType arrayType)
+		{
+			return arrayType?.GetFields(f => !f.IsStatic).SingleOrDefault()?.Type ?? SpecialType.UnknownType;
+		}
+
 		/// <summary>
 		/// Gets whether the type is the specified known type.
 		/// For generic known types, this returns true for any parameterization of the type (and also for the definition itself).

From 47dd905ad026ab4360cbcc572e713ecf50c6f6e2 Mon Sep 17 00:00:00 2001
From: Siegfried Pammer <siegfriedpammer@gmail.com>
Date: Fri, 6 Jun 2025 19:46:10 +0200
Subject: [PATCH 5/6] Make InlineArrayTransform post-order

---
 .../IL/Transforms/ExpressionTransforms.cs              | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
index 90907922b..7d36fb9e2 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs
@@ -277,11 +277,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 
 		protected internal override void VisitCall(Call inst)
 		{
-			if (context.Settings.InlineArrays && InlineArrayTransform.RunOnExpression(inst, context))
-			{
-				return;
-			}
-
 			if (NullableLiftingTransform.MatchGetValueOrDefault(inst, out var nullableValue, out var fallback)
 				&& SemanticHelper.IsPure(fallback.Flags))
 			{
@@ -298,9 +293,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 			{
 				context.Step("TransformRuntimeHelpersCreateSpanInitialization: single-dim", inst);
 				inst.ReplaceWith(replacement2);
+				replacement2.AcceptVisitor(this);
 				return;
 			}
 			base.VisitCall(inst);
+			if (context.Settings.InlineArrays && InlineArrayTransform.RunOnExpression(inst, context))
+			{
+				return;
+			}
 			TransformAssignment.HandleCompoundAssign(inst, context);
 		}
 

From 298c247355eeafb687b7412b209e219d6235913e Mon Sep 17 00:00:00 2001
From: Siegfried Pammer <siegfriedpammer@gmail.com>
Date: Sun, 15 Jun 2025 18:16:19 +0200
Subject: [PATCH 6/6] InlineArrayTransform: Add more bounds checking

---
 .../IL/Transforms/InlineArrayTransform.cs     | 35 ++++++++++++-------
 1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs
index 8631b09eb..3bb7b0427 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/InlineArrayTransform.cs
@@ -93,7 +93,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 				if (!MatchInlineArrayHelper(targetInst.Method, "InlineArrayAsReadOnlySpan", out var inlineArrayType))
 					return false;
 
-				if (targetInst.Arguments is not [var addrInst, LdcI4])
+				if (targetInst.Arguments is not [var addrInst, LdcI4 { Value: var length }])
+					return false;
+
+				if (length < 0 || length > inlineArrayType.GetInlineArrayLength())
 					return false;
 
 				type = inlineArrayType;
@@ -110,7 +113,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 				if (!MatchInlineArrayHelper(targetInst.Method, "InlineArrayAsSpan", out var inlineArrayType))
 					return false;
 
-				if (targetInst.Arguments is not [var addrInst, LdcI4])
+				if (targetInst.Arguments is not [var addrInst, LdcI4 { Value: var length }])
+					return false;
+
+				if (length < 0 || length > inlineArrayType.GetInlineArrayLength())
 					return false;
 
 				type = inlineArrayType;
@@ -135,28 +141,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms
 			index = null;
 			isReadOnly = false;
 
-			if (inst.Arguments is not [var addrInst, var indexInst])
+			if (inst.Arguments is not [var addrInst, LdcI4 { Value: var indexValue } indexInst])
 				return false;
 
+			addr = addrInst;
+			index = indexInst;
+
 			if (MatchInlineArrayHelper(inst.Method, "InlineArrayElementRef", out var inlineArrayType))
 			{
 				isReadOnly = false;
 				type = inlineArrayType;
-				addr = addrInst;
-				index = indexInst;
-				return true;
 			}
-
-			if (MatchInlineArrayHelper(inst.Method, "InlineArrayElementRefReadOnly", out inlineArrayType))
+			else if (MatchInlineArrayHelper(inst.Method, "InlineArrayElementRefReadOnly", out inlineArrayType))
 			{
 				isReadOnly = true;
 				type = inlineArrayType;
-				addr = addrInst;
-				index = indexInst;
-				return true;
+			}
+			else
+			{
+				return false;
 			}
 
-			return false;
+			if (indexValue < 0 || indexValue >= inlineArrayType.GetInlineArrayLength())
+			{
+				return false;
+			}
+
+			return true;
 		}
 
 		private static bool MatchInlineArrayFirstElementRef(Call inst, [NotNullWhen(true)] out IType? type, [NotNullWhen(true)] out ILInstruction? addr, out bool isReadOnly)