private BoundStatement UpdateStatement(BoundSpillSequenceBuilder builder, BoundStatement statement, bool substituteTemps) { if (builder == null) { // statement doesn't contain any await Debug.Assert(!substituteTemps || _tempSubstitution.Count == 0); Debug.Assert(statement != null); return(statement); } Debug.Assert(builder.Value == null); if (statement != null) { builder.AddStatement(statement); } var substituterOpt = (substituteTemps && _tempSubstitution.Count > 0) ? new LocalSubstituter(_tempSubstitution, RecursionDepth) : null; var result = _F.Block(builder.GetLocals(), builder.GetStatements(substituterOpt)); builder.Free(); return(result); }
public override BoundNode VisitSpillBlock(BoundSpillBlock node) { var newStatements = ArrayBuilder <BoundStatement> .GetInstance(); spillFieldAllocator.AllocateFields(node.SpillTemps); newStatements.AddRange(VisitList(node.Statements)); // Release references held by the spill temps: foreach (var spill in node.SpillTemps) { if (spill.Type.IsManagedType) { var field = spillFieldAllocator.GetField(spill); newStatements.Add(F.Assignment(F.Field(F.This(), field), F.NullOrDefault(field.Type))); } spillFieldAllocator.Free(spill); } return(F.Block(node.Locals, newStatements.ToReadOnlyAndFree())); }
private BoundStatement InitializeFixedStatementStringLocal( LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol stringTemp, out LocalSymbol localToClear) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; // intervening parens may have been skipped by the binder; find the declarator VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf<VariableDeclaratorSyntax>(); Debug.Assert(declarator != null); stringTemp = factory.SynthesizedLocal(initializerType, syntax: declarator, isPinned: true, kind: SynthesizedLocalKind.FixedString); // NOTE: we pin the string, not the pointer. Debug.Assert(stringTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); BoundStatement stringTempInit = factory.Assignment(factory.Local(stringTemp), initializerExpr); var convertedStringTemp = factory.Convert( localType, factory.Local(stringTemp), fixedInitializer.ElementPointerTypeConversion); BoundStatement localInit = AddLocalDeclarationSequencePointIfNecessary(declarator, localSymbol, factory.Assignment(factory.Local(localSymbol), convertedStringTemp)); BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, factory.Local(stringTemp), BinaryOperatorKind.NotEqual); BoundExpression helperCall; MethodSymbol offsetMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData, out offsetMethod)) { helperCall = factory.Call(receiver: null, method: offsetMethod); } else { helperCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray<BoundNode>.Empty, ErrorTypeSymbol.UnknownResultType); } BoundExpression addition = factory.Binary(BinaryOperatorKind.PointerAndIntAddition, localType, factory.Local(localSymbol), helperCall); BoundStatement conditionalAdd = factory.If(notNullCheck, factory.Assignment(factory.Local(localSymbol), addition)); localToClear = stringTemp; return factory.Block(stringTempInit, localInit, conditionalAdd); }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundStatement PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) { return(wrapped()); } var hoistedLocalsWithDebugScopes = ArrayBuilder <StateMachineFieldSymbol> .GetInstance(); foreach (var local in locals) { if (!NeedsProxy(local)) { continue; } // Ref synthesized variables have proxies that are allocated in VisitAssignmentOperator. if (local.RefKind != RefKind.None) { Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.Spill); continue; } CapturedSymbolReplacement proxy; bool reused = false; if (!proxies.TryGetValue(local, out proxy)) { proxy = new CapturedToStateMachineFieldReplacement(GetOrAllocateReusableHoistedField(TypeMap.SubstituteType(local.Type).Type, out reused, local), isReusable: true); proxies.Add(local, proxy); } // We need to produce hoisted local scope debug information for user locals as well as // lambda display classes, since Dev12 EE uses them to determine which variables are displayed // in Locals window. if ((local.SynthesizedKind == SynthesizedLocalKind.UserDefined && local.ScopeDesignatorOpt?.Kind() != SyntaxKind.SwitchSection) || local.SynthesizedKind == SynthesizedLocalKind.LambdaDisplayClass) { // NB: This is the case when the local backed by recycled field will not be visible in debugger. // It may be possible in the future, but for now a backing field can be mapped only to a single local. if (!reused) { hoistedLocalsWithDebugScopes.Add(((CapturedToStateMachineFieldReplacement)proxy).HoistedField); } } } var translatedStatement = wrapped(); var variableCleanup = ArrayBuilder <BoundAssignmentOperator> .GetInstance(); // produce cleanup code for all fields of locals defined by this block // as well as all proxies allocated by VisitAssignmentOperator within this block: foreach (var local in locals) { CapturedSymbolReplacement proxy; if (!proxies.TryGetValue(local, out proxy)) { continue; } var simpleProxy = proxy as CapturedToStateMachineFieldReplacement; if (simpleProxy != null) { AddVariableCleanup(variableCleanup, simpleProxy.HoistedField); if (proxy.IsReusable) { FreeReusableHoistedField(simpleProxy.HoistedField); } } else { foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedFields) { AddVariableCleanup(variableCleanup, field); if (proxy.IsReusable) { FreeReusableHoistedField(field); } } } } if (variableCleanup.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(variableCleanup.SelectAsArray((e, f) => (BoundStatement)f.ExpressionStatement(e), F))); } variableCleanup.Free(); // wrap the node in an iterator scope for debugging if (hoistedLocalsWithDebugScopes.Count != 0) { translatedStatement = MakeStateMachineScope(hoistedLocalsWithDebugScopes.ToImmutable(), translatedStatement); } hoistedLocalsWithDebugScopes.Free(); return(translatedStatement); }
/// <summary> /// fixed(char* ptr = stringVar){ ... } == becomes ===> /// /// pinned string pinnedTemp = stringVar; // pinning managed ref /// char* ptr = (char*)pinnedTemp; // unsafe cast to unmanaged ptr /// if (pinnedTemp != null) ptr += OffsetToStringData(); /// . . . /// </summary> private BoundStatement InitializeFixedStatementStringLocal( BoundLocalDeclaration localDecl, LocalSymbol localSymbol, BoundFixedLocalCollectionInitializer fixedInitializer, SyntheticBoundNodeFactory factory, out LocalSymbol pinnedTemp) { TypeSymbol localType = localSymbol.Type; BoundExpression initializerExpr = VisitExpression(fixedInitializer.Expression); TypeSymbol initializerType = initializerExpr.Type; // intervening parens may have been skipped by the binder; find the declarator VariableDeclaratorSyntax declarator = fixedInitializer.Syntax.FirstAncestorOrSelf <VariableDeclaratorSyntax>(); Debug.Assert(declarator != null); pinnedTemp = factory.SynthesizedLocal( initializerType, syntax: declarator, isPinned: true, kind: SynthesizedLocalKind.FixedReference); // NOTE: we pin the string, not the pointer. Debug.Assert(pinnedTemp.IsPinned); Debug.Assert(!localSymbol.IsPinned); BoundStatement stringTempInit = factory.Assignment(factory.Local(pinnedTemp), initializerExpr); // (char*)pinnedTemp; var addr = factory.Convert( fixedInitializer.ElementPointerType, factory.Local(pinnedTemp), Conversion.PinnedObjectToPointer); var convertedStringTemp = factory.Convert( localType, addr, fixedInitializer.ElementPointerTypeConversion); BoundStatement localInit = InstrumentLocalDeclarationIfNecessary(localDecl, localSymbol, factory.Assignment(factory.Local(localSymbol), convertedStringTemp)); BoundExpression notNullCheck = MakeNullCheck(factory.Syntax, factory.Local(localSymbol), BinaryOperatorKind.NotEqual); BoundExpression helperCall; MethodSymbol offsetMethod; if (TryGetWellKnownTypeMember(fixedInitializer.Syntax, WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__get_OffsetToStringData, out offsetMethod)) { helperCall = factory.Call(receiver: null, method: offsetMethod); } else { helperCall = new BoundBadExpression(fixedInitializer.Syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray <BoundExpression> .Empty, ErrorTypeSymbol.UnknownResultType); } BoundExpression addition = factory.Binary(BinaryOperatorKind.PointerAndIntAddition, localType, factory.Local(localSymbol), helperCall); BoundStatement conditionalAdd = factory.If(notNullCheck, factory.Assignment(factory.Local(localSymbol), addition)); return(factory.Block(stringTempInit, localInit, conditionalAdd)); }
private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node) { var expression = _localRewriter.VisitExpression(node.Expression); var result = ArrayBuilder <BoundStatement> .GetInstance(); // EnC: We need to insert a hidden sequence point to handle function remapping in case // the containing method is edited while methods invoked in the expression are being executed. if (!node.WasCompilerGenerated && _localRewriter.Instrument) { var instrumentedExpression = _localRewriter._instrumenter.InstrumentSwitchStatementExpression(node, expression, _factory); if (expression.ConstantValue == null) { expression = instrumentedExpression; } else { // If the expression is a constant, we leave it alone (the decision tree lowering code needs // to see that constant). But we add an additional leading statement with the instrumented expression. result.Add(_factory.ExpressionStatement(instrumentedExpression)); } } // output the decision tree part LowerPatternSwitch(expression, node, result); // if the endpoint is reachable, we exit the switch if (!node.IsComplete) { result.Add(_factory.Goto(node.BreakLabel)); } // at this point the end of result is unreachable. _declaredTemps.AddRange(node.InnerLocals); // output the sections of code foreach (var section in node.SwitchSections) { // Lifetime of these locals is expanded to the entire switch body. _declaredTemps.AddRange(section.Locals); // Start with the part of the decision tree that is in scope of the section variables. // Its endpoint is not reachable (it jumps back into the decision tree code). var sectionSyntax = (SyntaxNode)section.Syntax; var sectionBuilder = _switchSections[sectionSyntax]; // Add labels corresponding to the labels of the switch section. foreach (var label in section.SwitchLabels) { sectionBuilder.Add(_factory.Label(label.Label)); } // Add the translated body of the switch section sectionBuilder.AddRange(_localRewriter.VisitList(section.Statements)); sectionBuilder.Add(_factory.Goto(node.BreakLabel)); ImmutableArray <BoundStatement> statements = sectionBuilder.ToImmutableAndFree(); if (section.Locals.IsEmpty) { result.Add(_factory.StatementList(statements)); } else { result.Add(new BoundScope(section.Syntax, section.Locals, statements)); } // at this point the end of result is unreachable. } result.Add(_factory.Label(node.BreakLabel)); BoundStatement translatedSwitch = _factory.Block(_declaredTemps.ToImmutableArray(), node.InnerLocalFunctions, result.ToImmutableAndFree()); // Only add instrumentation (such as a sequence point) if the node is not compiler-generated. if (!node.WasCompilerGenerated && _localRewriter.Instrument) { translatedSwitch = _localRewriter._instrumenter.InstrumentPatternSwitchStatement(node, translatedSwitch); } return(translatedSwitch); }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundNode PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) { return(wrapped()); } var proxyFields = ArrayBuilder <SynthesizedFieldSymbolBase> .GetInstance(); foreach (var local in locals) { if (!VariablesCaptured.Contains(local)) { continue; } CapturedSymbolReplacement proxy; if (proxies.TryGetValue(local, out proxy)) { // All of the user-declared variables have pre-allocated proxies var field = proxy.HoistedField; Debug.Assert((object)field != null); Debug.Assert(local.DeclarationKind != LocalDeclarationKind.CompilerGenerated); // temps have lazily allocated proxies if (local.DeclarationKind != LocalDeclarationKind.CompilerGenerated) { proxyFields.Add(field); } } else { if (local.RefKind == RefKind.None) { SynthesizedFieldSymbolBase field = MakeHoistedTemp(local, TypeMap.SubstituteType(local.Type)); proxy = new CapturedToFrameSymbolReplacement(field); proxies.Add(local, proxy); } // ref temporary variables have proxies that are allocated on demand // See VisitAssignmentOperator. } } var translatedStatement = wrapped(); // produce code to free (e.g. mark as available for reuse) and clear (e.g. set to null) the proxies for any temps for these locals var clearTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(); foreach (var local in locals) { ArrayBuilder <SynthesizedFieldSymbolBase> frees; if (freeTempsMap.TryGetValue(local, out frees)) { Debug.Assert(local.DeclarationKind == LocalDeclarationKind.CompilerGenerated); // only temps are managed this way freeTempsMap.Remove(local); foreach (var field in frees) { if (MightContainReferences(field.Type)) { clearTemps.Add(F.AssignmentExpression(F.Field(F.This(), field), F.NullOrDefault(field.Type))); } FreeTemp(field); } frees.Free(); } } if (clearTemps.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(clearTemps.Select(e => F.ExpressionStatement(e)).AsImmutable <BoundStatement>()) ); } clearTemps.Free(); // wrap the node in an iterator scope for debugging if (proxyFields.Count != 0) { translatedStatement = new BoundIteratorScope(F.Syntax, proxyFields.ToImmutable(), translatedStatement); } proxyFields.Free(); return(translatedStatement); }
private BoundStatement LowerNonprimitiveSwitch(BoundSwitchStatement switchStatement) { // Here we handle "other" types, such as float, double, and decimal, by // lowering the BoundSwitchStatement. // We compare the constant values using value.Equals(input), using ordinary // overload resolution. Note that we cannot and do not rely on switching // on the hash code, as it may not be consistent with the behavior of Equals; // see https://github.com/dotnet/coreclr/issues/6237. Also, the hash code is // not guaranteed to be the same on the compilation platform as the runtime // platform. // CONSIDER: can we improve the quality of the code using comparisons, like // we do for other numeric types, by generating a series of tests // that use divide-and-conquer to efficiently find a matching value? // If so, we should use the BoundSwitchStatement and do that in emit. // Moreover, we should be able to use `==` rather than `.Equals` // for cases (such as non-NaN) where we know the result to be the same. var rewrittenSections = switchStatement.SwitchSections; var expression = switchStatement.Expression; var noValueMatches = switchStatement.BreakLabel; Debug.Assert(switchStatement.LoweredPreambleOpt == null); Debug.Assert(switchStatement.InnerLocals.IsDefaultOrEmpty); Debug.Assert(switchStatement.InnerLocalFunctions.IsDefaultOrEmpty); Debug.Assert(switchStatement.StringEquality == null); LabelSymbol nextLabel = null; var builder = ArrayBuilder <BoundStatement> .GetInstance(); foreach (var section in rewrittenSections) { foreach (var boundSwitchLabel in section.SwitchLabels) { if (nextLabel != null) { builder.Add(_factory.Label(nextLabel)); } nextLabel = _factory.GenerateLabel("failcase+" + section.SwitchLabels[0].ConstantValueOpt.Value); Debug.Assert(boundSwitchLabel.ConstantValueOpt != null); // generate (if (value.Equals(input)) goto label; var literal = LocalRewriter.MakeLiteral(_factory.Syntax, boundSwitchLabel.ConstantValueOpt, expression.Type); var condition = _factory.InstanceCall(literal, "Equals", expression); if (!condition.HasErrors && condition.Type.SpecialType != SpecialType.System_Boolean) { var call = (BoundCall)condition; // '{1} {0}' has the wrong return type _factory.Diagnostics.Add(ErrorCode.ERR_BadRetType, boundSwitchLabel.Syntax.GetLocation(), call.Method, call.Type); } builder.Add(_factory.ConditionalGoto(condition, boundSwitchLabel.Label, true)); builder.Add(_factory.Goto(nextLabel)); } foreach (var boundSwitchLabel in section.SwitchLabels) { builder.Add(_factory.Label(boundSwitchLabel.Label)); } builder.Add(_factory.Block(section.Statements)); // this location should not be reachable. } Debug.Assert(nextLabel != null); builder.Add(_factory.Label(nextLabel)); builder.Add(_factory.Label(noValueMatches)); return(_factory.Block(builder.ToImmutableAndFree())); }
private BoundStatement MakeLoweredForm(BoundPatternSwitchStatement node) { var expression = _localRewriter.VisitExpression(node.Expression); var result = ArrayBuilder <BoundStatement> .GetInstance(); // if the expression is "too complex", we copy it to a temp. LocalSymbol initialTemp = null; if (expression.ConstantValue == null) { initialTemp = _factory.SynthesizedLocal(expression.Type, expression.Syntax); result.Add(_factory.Assignment(_factory.Local(initialTemp), expression)); expression = _factory.Local(initialTemp); } // EnC: We need to insert a hidden sequence point to handle function remapping in case // the containing method is edited while methods invoked in the expression are being executed. if (!node.WasCompilerGenerated && _localRewriter.Instrument) { expression = _localRewriter._instrumenter.InstrumentSwitchStatementExpression(node, expression, _factory); } // output the decision tree part LowerPatternSwitch(expression, node, result); // if the endpoint is reachable, we exit the switch if (!node.IsComplete) { result.Add(_factory.Goto(node.BreakLabel)); } // at this point the end of result is unreachable. // output the sections of code foreach (var section in node.SwitchSections) { // Start with the part of the decision tree that is in scope of the section variables. // Its endpoint is not reachable (it jumps back into the decision tree code). var sectionSyntax = (SyntaxNode)section.Syntax; var sectionBuilder = _switchSections[sectionSyntax]; // Add labels corresponding to the labels of the switch section. foreach (var label in section.SwitchLabels) { sectionBuilder.Add(_factory.Label(label.Label)); } // Add the translated body of the switch section sectionBuilder.AddRange(_localRewriter.VisitList(section.Statements)); sectionBuilder.Add(_factory.Goto(node.BreakLabel)); result.Add(_factory.Block(section.Locals, sectionBuilder.ToImmutableAndFree())); // at this point the end of result is unreachable. } result.Add(_factory.Label(node.BreakLabel)); _declaredTemps.AddOptional(initialTemp); _declaredTemps.AddRange(node.InnerLocals); BoundStatement translatedSwitch = _factory.Block(_declaredTemps.ToImmutableArray(), node.InnerLocalFunctions, result.ToImmutableAndFree()); // Only add instrumentation (such as a sequence point) if the node is not compiler-generated. if (!node.WasCompilerGenerated && _localRewriter.Instrument) { translatedSwitch = _localRewriter._instrumenter.InstrumentPatternSwitchStatement(node, translatedSwitch); } return(translatedSwitch); }
/// <summary> /// Translate a statement that declares a given set of locals. Also allocates and frees hoisted temps as /// required for the translation. /// </summary> /// <param name="locals">The set of locals declared in the original version of this statement</param> /// <param name="wrapped">A delegate to return the translation of the body of this statement</param> private BoundStatement PossibleIteratorScope(ImmutableArray <LocalSymbol> locals, Func <BoundStatement> wrapped) { if (locals.IsDefaultOrEmpty) { return(wrapped()); } var hoistedUserDefinedLocals = ArrayBuilder <SynthesizedFieldSymbolBase> .GetInstance(); foreach (var local in locals) { if (!NeedsProxy(local)) { continue; } // Ref synthesized variables have proxies that are allocated in VisitAssignmentOperator. if (local.RefKind != RefKind.None) { Debug.Assert(local.SynthesizedLocalKind == SynthesizedLocalKind.AwaitSpill); continue; } Debug.Assert(local.SynthesizedLocalKind == SynthesizedLocalKind.None || local.SynthesizedLocalKind.IsLongLived()); CapturedSymbolReplacement proxy; if (!proxies.TryGetValue(local, out proxy)) { proxy = new CapturedToFrameSymbolReplacement(GetOrAllocateHoistedField(TypeMap.SubstituteType(local.Type)), isReusable: true); proxies.Add(local, proxy); } if (local.SynthesizedLocalKind == SynthesizedLocalKind.None) { hoistedUserDefinedLocals.Add(((CapturedToFrameSymbolReplacement)proxy).HoistedField); } } var translatedStatement = wrapped(); var variableCleanup = ArrayBuilder <BoundAssignmentOperator> .GetInstance(); // produce cleanup code for all fields of locals defined by this block // as well as all proxies allocated by VisitAssignmentOperator within this block: foreach (var local in locals) { CapturedSymbolReplacement proxy; if (!proxies.TryGetValue(local, out proxy)) { continue; } var simpleProxy = proxy as CapturedToFrameSymbolReplacement; if (simpleProxy != null) { AddVariableCleanup(variableCleanup, simpleProxy.HoistedField); if (proxy.IsReusable) { FreeHoistedField(simpleProxy.HoistedField); } } else { foreach (var field in ((CapturedToExpressionSymbolReplacement)proxy).HoistedFields) { AddVariableCleanup(variableCleanup, field); if (proxy.IsReusable) { FreeHoistedField(field); } } } } if (variableCleanup.Count != 0) { translatedStatement = F.Block( translatedStatement, F.Block(variableCleanup.SelectAsArray((e, f) => (BoundStatement)f.ExpressionStatement(e), F))); } variableCleanup.Free(); // wrap the node in an iterator scope for debugging if (hoistedUserDefinedLocals.Count != 0) { translatedStatement = F.Block(new BoundIteratorScope(F.Syntax, hoistedUserDefinedLocals.ToImmutable(), translatedStatement)); } hoistedUserDefinedLocals.Free(); return(translatedStatement); }