Skip to content

Commit

Permalink
fixes handling of inline array when non literal indexes are used (#257)
Browse files Browse the repository at this point in the history
also fixes conversion to Span<T>; generated method was calling generic methods (as opposed to their instantiations)
  • Loading branch information
adrianoc committed Mar 29, 2024
1 parent 9beefe8 commit f3c547a
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 9 deletions.
3 changes: 2 additions & 1 deletion Cecilifier.Core.Tests/Tests/Unit/DefaultExpressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,10 @@ public void TestDefaultLiteralExpression(string type, string expected)
public void TestDefaultLiteralExpressionWithStructs(string code)
{
var expected = """
\s+//.+ v = default;
\s+//(?:.+ v = default)|(?:Foo v);
\s+var (l_v_\d+) = new VariableDefinition\((.+)\);
\s+m_topLevelStatements_\d+.Body.Variables.Add\(\1\);
(?:\s+//v = default;)?
\s+(il_topLevelMain_\d+\.Emit\(OpCodes\.)Ldloca, l_v_\d+\);
\s+\3Initobj, \2\);
""";
Expand Down
33 changes: 33 additions & 0 deletions Cecilifier.Core.Tests/Tests/Unit/InlineArrayTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public struct IntBuffer

var cecilifiedCode = result.GeneratedCode.ReadToEnd();
Assert.That(cecilifiedCode, Does.Match("""new TypeDefinition\("", "<PrivateImplementationDetails>", .+\)"""));
Assert.That(cecilifiedCode, Does.Match("""cls_privateImplementationDetails_\d+.Methods.Add\(m_inlineArrayAsSpan_\d+\);"""));
Assert.That(cecilifiedCode, Does.Match("""(m_inlineArrayAsSpan_\d+)_inst.Add\(_ = \1_il.Create\(OpCodes.Call, gi_unsafeAs_\d+\)\);"""));
Assert.That(cecilifiedCode, Does.Match("""(m_inlineArrayAsSpan_\d+)_inst.Add\(_ = \1_il.Create\(OpCodes.Call, gi_createSpan_\d+\)\);"""));
}

[Test]
Expand Down Expand Up @@ -231,6 +234,36 @@ public struct Buffer { private {{elementType}} _element0; }

Assert.That(cecilified, Does.Match(expectedIL));
}

[Test]
public void AccessToIndex_ThroughNonLiteral_UsesElementRefMethodAndIndex()
{
var result = RunCecilifier("""
class C
{
int M(int i)
{
Buffer b = new Buffer();
return b[i];
}
}
[System.Runtime.CompilerServices.InlineArray(5)]
public struct Buffer { private int _element0; }
""");

var cecilified = result.GeneratedCode.ReadToEnd();
Assert.That(cecilified, Does.Match("""
\s+//return b\[i\];
(\s+il_M_5.Emit\(OpCodes\.)Ldloca, l_b_7\);
\1Ldarg_1\);
"""));

Assert.That(cecilified, Does.Match("""
(\s+il_M_\d+\.Emit\(OpCodes\.)Call, gi_inlineArrayElementRef_\d+\);
\1Ldind_I4\);
"""));
}

[Test]
public void InlineArray_MemberAccess_OnIndex()
Expand Down
2 changes: 1 addition & 1 deletion Cecilifier.Core/AST/InlineArrayProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ internal static bool TryHandleIntIndexElementAccess(IVisitorContext context, str
}
else
{
context.EmitCilInstruction(ilVar, OpCodes.Ldc_I4, index);
ExpressionVisitor.Visit(context, ilVar, elementAccess.ArgumentList.Arguments[0].Expression);
method = InlineArrayElementRefMethodFor(context, inlineArrayType);
}
context.EmitCilInstruction(ilVar, OpCodes.Call, method);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace Cecilifier.Core.CodeGeneration;
Expand Down Expand Up @@ -71,21 +72,37 @@ internal static string GetOrEmmitInlineArrayAsSpanMethod(IVisitorContext context
return context.TypeResolver.Resolve(context.RoslynTypeSystem.SystemSpan).MakeGenericInstanceType(spanTypeParameter);
});

var tBufferVar = ResolveOwnedGenericParameter(context, "TBuffer", methodTypeQualifiedName);
var tElementVar = ResolveOwnedGenericParameter(context, "TElement", methodTypeQualifiedName);

var unsafeAsVar = context.Naming.SyntheticVariable("unsafeAs", ElementKind.GenericInstance);
var unsafeAsExps = GetUnsafeAsMethod(context).MethodResolverExpression(context).MakeGenericInstanceMethod(unsafeAsVar, [tBufferVar, tElementVar]);

var memoryMarshalCreateSpanVar = context.Naming.SyntheticVariable("createSpan", ElementKind.GenericInstance);
var memoryMarshalCreateSpanExps = GetMemoryMarshalCreateSpanMethod(context).MethodResolverExpression(context).MakeGenericInstanceMethod(memoryMarshalCreateSpanVar, [tElementVar]);

var methodBodyExpressions = CecilDefinitionsFactory.MethodBody(
methodVar,
[
OpCodes.Ldarg_0,
OpCodes.Call.WithOperand(GetUnsafeAsMethod(context).MethodResolverExpression(context)),
OpCodes.Call.WithOperand(unsafeAsVar),
OpCodes.Ldarg_1,
OpCodes.Call.WithOperand(GetMemoryMarshalCreateSpanMethod(context).MethodResolverExpression(context)),
OpCodes.Call.WithOperand(memoryMarshalCreateSpanVar),
OpCodes.Ret
]);

var finalExps = methodExpressions
.Append("")
.Append("// Unsafe.As() generic instance method")
.Concat(unsafeAsExps)
.Append("")
.Append("// MemoryMarshal.CreateSpan() generic instance method")
.Concat(memoryMarshalCreateSpanExps)
.Append("")
.Concat(methodBodyExpressions)
.Append($"{privateImplementationDetailsVar.VariableName}.Methods.Add({methodVar});");

foreach (var exp in methodExpressions.Concat(methodBodyExpressions))
{
context.WriteCecilExpression(exp);
context.WriteNewLine();
}
context.WriteCecilExpressions(finalExps);

return methodVar;

Expand Down

0 comments on commit f3c547a

Please sign in to comment.