-
Notifications
You must be signed in to change notification settings - Fork 4k
/
LocalRewriter_StackAlloc.cs
136 lines (115 loc) · 6.23 KB
/
LocalRewriter_StackAlloc.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license 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 VisitStackAllocArrayCreation(stackAllocNode);
}
public override BoundNode VisitStackAllocArrayCreation(BoundStackAllocArrayCreation stackAllocNode)
{
var rewrittenCount = VisitExpression(stackAllocNode.Count);
var type = stackAllocNode.Type;
if (rewrittenCount.ConstantValue?.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, stackAllocNode.Type);
}
else if (type.OriginalDefinition == _compilation.GetWellKnownType(WellKnownType.System_Span_T))
{
var spanType = (NamedTypeSymbol)stackAllocNode.Type;
var sideEffects = ArrayBuilder<BoundExpression>.GetInstance();
var locals = ArrayBuilder<LocalSymbol>.GetInstance();
var countTemp = CaptureExpressionInTempIfNeeded(rewrittenCount, sideEffects, locals);
var stackSize = RewriteStackAllocCountToSize(countTemp, elementType);
stackAllocNode = new BoundConvertedStackAllocExpression(stackAllocNode.Syntax, elementType, stackSize, initializerOpt, spanType);
BoundExpression constructorCall;
if (TryGetWellKnownTypeMember(stackAllocNode.Syntax, WellKnownMember.System_Span_T__ctor, 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);
}
return new BoundSequence(
syntax: stackAllocNode.Syntax,
locals: locals.ToImmutableAndFree(),
sideEffects: sideEffects.ToImmutableAndFree(),
value: constructorCall,
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.ConstantValue;
if (sizeConst != null)
{
int size = sizeConst.Int32Value;
Debug.Assert(size > 0);
// common case: stackalloc int[123]
var countConst = countExpression.ConstantValue;
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;
}
}
}