Skip to content

Commit

Permalink
Add array initialization optimization (#62392)
Browse files Browse the repository at this point in the history
* Add array initialization optimization fix and initial unit tests

* fix: ensure indices are emitted for multidimensional arrays; add additional test cases and finish updating broken unit tests

* Address pre-review comments

* Remove extraneous whitespace

* Avoid PEVerify bug for test IL output by changing test .NET version depending on execution context

* Address final review comments and hopefully fix CI

* Final changes to get CI to pass

* Address additional review feedback
  • Loading branch information
adamperlin committed Jul 15, 2022
1 parent ee6cb10 commit 26a134e
Show file tree
Hide file tree
Showing 11 changed files with 774 additions and 229 deletions.
56 changes: 39 additions & 17 deletions src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1534,37 +1534,36 @@ private void EmitDefaultValueTypeConstructorCallExpression(BoundCall call)
FreeOptTemp(tempOpt);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private void EmitStaticCallExpression(BoundCall call, UseKind useKind)
private void EmitStaticCall(MethodSymbol method, BoundExpression receiverOpt, ImmutableArray<BoundExpression> arguments, UseKind useKind, SyntaxNode syntax, ImmutableArray<RefKind> refKindsOpt)
{
var method = call.Method;
var receiver = call.ReceiverOpt;
var arguments = call.Arguments;

Debug.Assert(method.IsStatic);

EmitArguments(arguments, method.Parameters, call.ArgumentRefKindsOpt);
EmitArguments(arguments, method.Parameters, refKindsOpt);
int stackBehavior = GetCallStackBehavior(method, arguments);

if (method.IsAbstract || method.IsVirtual)
{
if (receiver is not BoundTypeExpression { Type: { TypeKind: TypeKind.TypeParameter } })
if (receiverOpt is not BoundTypeExpression { Type.TypeKind: TypeKind.TypeParameter })
{
throw ExceptionUtilities.Unreachable;
}

_builder.EmitOpCode(ILOpCode.Constrained);
EmitSymbolToken(receiver.Type, receiver.Syntax);
EmitSymbolToken(receiverOpt.Type, receiverOpt.Syntax);
}

_builder.EmitOpCode(ILOpCode.Call, stackBehavior);

EmitSymbolToken(method, call.Syntax,
method.IsVararg ? (BoundArgListOperator)arguments[arguments.Length - 1] : null);
EmitSymbolToken(method, syntax,
method.IsVararg ? (BoundArgListOperator)arguments[^1] : null);

EmitCallCleanup(call.Syntax, useKind, method);
EmitCallCleanup(syntax, useKind, method);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private void EmitStaticCallExpression(BoundCall call, UseKind useKind)
=> EmitStaticCall(call.Method, call.ReceiverOpt, call.Arguments, useKind, call.Syntax, call.ArgumentRefKindsOpt);

[MethodImpl(MethodImplOptions.NoInlining)]
private void EmitInstanceCallExpression(BoundCall call, UseKind useKind)
{
Expand Down Expand Up @@ -1917,19 +1916,42 @@ private void EmitArrayLength(BoundArrayLength expression, bool used)
EmitPopIfUnused(used);
}

private void EmitArrayCreationExpression(BoundArrayCreation expression, bool used)
private void EmitUninitializedArrayCreation(int initializerLength, SyntaxNode syntax, MethodSymbol allocUninitialized)
{
var arrayType = (ArrayTypeSymbol)expression.Type;
BoundExpression receiverOpt = null;

var arrLen = ConstantValue.Create(initializerLength);
var pinned = ConstantValue.Create(false);

var arg1 = new BoundLiteral(syntax, arrLen, _module.Compilation.GetSpecialType(SpecialType.System_Int32));
var arg2 = new BoundLiteral(syntax, pinned, _module.Compilation.GetSpecialType(SpecialType.System_Boolean));
var arguments = ImmutableArray.Create<BoundExpression>(arg1, arg2);

EmitArrayIndices(expression.Bounds);
EmitStaticCall(allocUninitialized, receiverOpt, arguments, UseKind.UsedAsValue, syntax,
ImmutableArray.Create(RefKind.None, RefKind.None));
}

private void EmitArrayCreationExpression(BoundArrayCreation expression, bool used)
{
var arrayType = (ArrayTypeSymbol)expression.Type;
if (arrayType.IsSZArray)
{
_builder.EmitOpCode(ILOpCode.Newarr);
EmitSymbolToken(arrayType.ElementType, expression.Syntax);
var allocUninitialized = _module.Compilation.GetWellKnownTypeMember(WellKnownMember.System_GC__AllocateUninitializedArray_T);
if (expression.InitializerOpt != null && allocUninitialized is MethodSymbol { } alloc)
{
var constructed = alloc.Construct(ImmutableArray.Create(arrayType.ElementType));
EmitUninitializedArrayCreation(expression.InitializerOpt.Initializers.Length, expression.Syntax, constructed);
}
else
{
EmitArrayIndices(expression.Bounds);
_builder.EmitOpCode(ILOpCode.Newarr);
EmitSymbolToken(arrayType.ElementType, expression.Syntax);
}
}
else
{
EmitArrayIndices(expression.Bounds);
_builder.EmitArrayCreation(_module.Translate(arrayType), expression.Syntax, _diagnostics);
}

Expand Down
Loading

0 comments on commit 26a134e

Please sign in to comment.