| File: Lowering\LocalRewriter\LocalRewriter_StackAlloc.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Diagnostics; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { internal sealed partial class LocalRewriter { public override BoundNode VisitConvertedStackAllocExpression(BoundConvertedStackAllocExpression stackAllocNode) { return VisitStackAllocArrayCreationBase(stackAllocNode); } public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation stackAllocNode) { return VisitStackAllocArrayCreationBase(stackAllocNode); } private BoundNode VisitStackAllocArrayCreationBase(BoundStackAllocArrayCreationBase stackAllocNode) { var rewrittenCount = VisitExpression(stackAllocNode.Count); var type = stackAllocNode.Type; Debug.Assert(type is { }); if (rewrittenCount.ConstantValueOpt?.Int32Value == 0) { // either default(span) or nullptr return _factory.Default(type); } var elementType = stackAllocNode.ElementType; var initializerOpt = stackAllocNode.InitializerOpt; if (initializerOpt != null) { initializerOpt = initializerOpt.Update(VisitList(initializerOpt.Initializers)); } if (type.IsPointerType()) { var stackSize = RewriteStackAllocCountToSize(rewrittenCount, elementType); return new BoundConvertedStackAllocExpression(stackAllocNode.Syntax, elementType, stackSize, initializerOpt, type); } else if (TypeSymbol.Equals(type.OriginalDefinition, _compilation.GetWellKnownType(WellKnownType.System_Span_T), TypeCompareKind.ConsiderEverything2)) { var spanType = (NamedTypeSymbol)type; var sideEffects = ArrayBuilder<BoundExpression>.GetInstance(); var locals = ArrayBuilder<LocalSymbol>.GetInstance(); var countTemp = CaptureExpressionInTempIfNeeded(rewrittenCount, sideEffects, locals, SynthesizedLocalKind.Spill); var stackSize = RewriteStackAllocCountToSize(countTemp, elementType); stackAllocNode = new BoundConvertedStackAllocExpression( stackAllocNode.Syntax, elementType, stackSize, initializerOpt, _compilation.CreatePointerTypeSymbol(elementType)); BoundExpression constructorCall; if (TryGetWellKnownTypeMember(stackAllocNode.Syntax, WellKnownMember.System_Span_T__ctor_Pointer, out MethodSymbol? spanConstructor)) { constructorCall = _factory.New((MethodSymbol)spanConstructor.SymbolAsMember(spanType), stackAllocNode, countTemp); } else { constructorCall = new BoundBadExpression( syntax: stackAllocNode.Syntax, resultKind: LookupResultKind.NotInvocable, symbols: ImmutableArray<Symbol?>.Empty, childBoundNodes: ImmutableArray<BoundExpression>.Empty, type: ErrorTypeSymbol.UnknownResultType); } // The stackalloc instruction requires that the evaluation stack contains only its parameter when executed. // We arrange to clear the stack by wrapping it in a SpillSequence, which will cause pending computations // to be spilled, and also by storing the result in a temporary local, so that the result does not get // hoisted/spilled into some state machine. If that temp local needs to be spilled that will result in an // error. _needsSpilling = true; var tempAccess = _factory.StoreToTemp(constructorCall, out BoundAssignmentOperator tempAssignment, syntaxOpt: stackAllocNode.Syntax); sideEffects.Add(tempAssignment); locals.Add(tempAccess.LocalSymbol); return new BoundSpillSequence( syntax: stackAllocNode.Syntax, locals: locals.ToImmutableAndFree(), sideEffects: sideEffects.ToImmutableAndFree(), value: tempAccess, type: spanType); } else { throw ExceptionUtilities.UnexpectedValue(type); } } private BoundExpression RewriteStackAllocCountToSize(BoundExpression countExpression, TypeSymbol elementType) { // From ILGENREC::genExpr: // EDMAURER always perform a checked multiply regardless of the context. // localloc takes an unsigned native int. When a user specifies a negative // count of elements, per spec, the behavior is undefined. So convert element // count to unsigned. // NOTE: to match this special case logic, we're going to construct the multiplication // ourselves, rather than calling MakeSizeOfMultiplication (which inserts various checks // and conversions). TypeSymbol uintType = _factory.SpecialType(SpecialType.System_UInt32); TypeSymbol uintPtrType = _factory.SpecialType(SpecialType.System_UIntPtr); // Why convert twice? Because dev10 actually uses an explicit conv_u instruction and the normal conversion // from int32 to native uint is emitted as conv_i. The behavior we want to emulate is to re-interpret // (i.e. unchecked) an int32 as unsigned (i.e. uint32) and then convert it to a native uint *without* sign // extension. BoundExpression sizeOfExpression = _factory.Sizeof(elementType); var sizeConst = sizeOfExpression.ConstantValueOpt; if (sizeConst != null) { int size = sizeConst.Int32Value; Debug.Assert(size > 0); // common case: stackalloc int[123] var countConst = countExpression.ConstantValueOpt; if (countConst != null) { var count = countConst.Int32Value; long folded = unchecked((uint)count * size); if (folded < uint.MaxValue) { return _factory.Convert(uintPtrType, _factory.Literal((uint)folded), Conversion.IntegerToPointer); } } } BoundExpression convertedCount = _factory.Convert(uintType, countExpression, Conversion.ExplicitNumeric); convertedCount = _factory.Convert(uintPtrType, convertedCount, Conversion.IntegerToPointer); // another common case: stackalloc byte[x] if (sizeConst?.Int32Value == 1) { return convertedCount; } BinaryOperatorKind multiplicationKind = BinaryOperatorKind.Checked | BinaryOperatorKind.UIntMultiplication; //"UInt" just to make it unsigned BoundExpression product = _factory.Binary(multiplicationKind, uintPtrType, convertedCount, sizeOfExpression); return product; } } }