private LocalSymbol DigForValueLocal(BoundSequence topSequence, BoundExpression value) { switch (value.Kind) { case BoundKind.Local: var local = (BoundLocal)value; var symbol = local.LocalSymbol; if (topSequence.Locals.Contains(symbol)) { return(symbol); } break; case BoundKind.Sequence: return(DigForValueLocal(topSequence, ((BoundSequence)value).Value)); case BoundKind.FieldAccess: var fieldAccess = (BoundFieldAccess)value; if (!fieldAccess.FieldSymbol.IsStatic) { var receiver = fieldAccess.ReceiverOpt; if (!receiver.Type.IsReferenceType) { return(DigForValueLocal(topSequence, receiver)); } } break; } return(null); }
private static IEnumerable <BoundSequence> ReadSequences(XmlReader reader, IEnumerable <MapLocation> locations) { var locationDictionary = locations.ToDictionary(x => x.LocationName); using (XmlReader sequenceReader = reader.ReadSubtree()) { while (sequenceReader.Read()) { if (sequenceReader.Name == "Sequence") { var sequence = new BoundSequence { WaitTime = int.Parse(sequenceReader.GetAttribute("WaitTime")) }; XmlReader sequenceLocationReader = sequenceReader.ReadSubtree(); while (sequenceLocationReader.Read()) { if (sequenceLocationReader.Name == "Location") { sequence.SetLocationState(locationDictionary[sequenceLocationReader.GetAttribute("LocationName")], sequenceLocationReader.GetAttribute("State")); } } yield return(sequence); } } } }
private void RemoveSequenceEventHandler(object sender, RoutedEventArgs e) { if (GlobalState.Sequences.Count > 1) { int idx = GlobalState.Sequences.IndexOf(CurrentSequence); GlobalState.Sequences.Remove(CurrentSequence); if (idx < GlobalState.Sequences.Count) { CurrentSequence = GlobalState.Sequences[idx]; } else { CurrentSequence = GlobalState.Sequences[idx - 1]; } } else { for (int i = 0; i < CurrentSequence.States.Count; i++) { CurrentSequence.States[CurrentSequence.States.ElementAt(i).Key] = "default"; CurrentSequence.UpdateLocationColors(); } } GlobalState.HasMadeChanges = true; mainWindow.RefreshTitle(); UpdateNavigation(); }
internal bool Parse(BoundSequence boundSequence) { base.Parse(boundSequence); ParseLocals(boundSequence.Locals, this.Locals); foreach (var sideEffect in boundSequence.SideEffects) { var expression = Deserialize(sideEffect) as Expression; Debug.Assert(expression != null); this.SideEffects.Add(expression); } var boundSequencePointExpression = boundSequence.Value as BoundSequencePointExpression; if (boundSequencePointExpression != null) { var boundLocal = boundSequencePointExpression.Expression as BoundLocal; if (boundLocal != null && boundSequence.SideEffects != null && boundSequence.SideEffects.Length == 1) { this.specialCaseSingleExpression = true; var boundAssignmentOperator = boundSequence.SideEffects.First() as BoundAssignmentOperator; if (boundAssignmentOperator != null) { this.Value = Deserialize(boundAssignmentOperator.Right) as Expression; return(true); } } } this.Value = Deserialize(boundSequence.Value) as Expression; return(true); }
/// <summary> /// May introduce a temp which it will return. (otherwise returns null) /// </summary> private LocalDefinition EmitSequenceAddress(BoundSequence sequence, AddressKind addressKind) { var hasLocals = !sequence.Locals.IsEmpty; if (hasLocals) { _builder.OpenLocalScope(); foreach (var local in sequence.Locals) { DefineLocal(local, sequence.Syntax); } } EmitSideEffects(sequence); var tempOpt = EmitAddress(sequence.Value, addressKind); // when a sequence is happened to be a byref receiver // we may need to extend the life time of the target until we are done accessing it // {.v ; v = Foo(); v}.Bar() // v should be released only after Bar() is done. LocalSymbol doNotRelease = null; if (tempOpt == null) { doNotRelease = DigForValueLocal(sequence); if (doNotRelease != null) { tempOpt = GetLocal(doNotRelease); } } FreeLocals(sequence, doNotRelease); return(tempOpt); }
public override BoundNode VisitSequence(BoundSequence node) { var newLocals = RewriteLocals(node.Locals); var newSideEffects = VisitList<BoundExpression>(node.SideEffects); var newValue = (BoundExpression)this.Visit(node.Value); var newType = this.VisitType(node.Type); return node.Update(newLocals, newSideEffects, newValue, newType); }
internal void AddSequence(SyntheticBoundNodeFactory F, BoundSequence sequence) { locals.AddRange(sequence.Locals); foreach (var sideEffect in sequence.SideEffects) { statements.Add(F.ExpressionStatement(sideEffect)); } }
public override BoundNode VisitSequence(BoundSequence node) { var oldScope = _currentScope; _currentScope = CreateOrReuseScope(node, node.Locals); var result = base.VisitSequence(node); _currentScope = oldScope; return(result); }
/// <summary> /// May introduce a temp which it will return. (otherwise returns null) /// </summary> private LocalDefinition EmitSequenceAddress(BoundSequence sequence, AddressKind addressKind) { DefineAndRecordLocals(sequence); EmitSideEffects(sequence); var result = EmitAddress(sequence.Value, addressKind); CloseScopeAndKeepLocals(sequence); return(result); }
private void SequenceNavigationLastButtonEventHandler(object sender, RoutedEventArgs e) { if (CurrentSequence.IsBlank()) { GlobalState.Sequences.Remove(CurrentSequence); } CurrentSequence = GlobalState.Sequences.Last(); UpdateNavigation(); }
private void SequenceNavigationPreviousButtonEventHandler(object sender, RoutedEventArgs e) { int seqIndex = GlobalState.Sequences.IndexOf(CurrentSequence); if (CurrentSequence.IsBlank()) { GlobalState.Sequences.Remove(CurrentSequence); } CurrentSequence = GlobalState.Sequences[seqIndex - 1]; UpdateNavigation(); }
internal void Parse(BoundSequence boundSequence) { base.Parse(boundSequence); ParseLocals(boundSequence.Locals, this.Locals); foreach (var sideEffect in boundSequence.SideEffects) { var expression = Deserialize(sideEffect) as Expression; Debug.Assert(expression != null); this.SideEffects.Add(expression); } this.Value = Deserialize(boundSequence.Value) as Expression; }
internal bool Parse(BoundSequence boundSequence) { base.Parse(boundSequence); if (boundSequence.SideEffects.Length > 2 || !boundSequence.SideEffects.All(se => se is BoundAssignmentOperator) || !boundSequence.Locals.Any()) { return(false); } var boundAssignmentOperator = boundSequence.SideEffects.First() as BoundAssignmentOperator; if (boundAssignmentOperator != null) { var boundExpression = FindValue(boundAssignmentOperator.Left, boundAssignmentOperator.Right); if (boundExpression == null) { return(false); } this.Value = Deserialize(boundExpression) as Expression; } var prefixUnaryExpressionSyntax = boundSequence.Syntax.Green as PrefixUnaryExpressionSyntax; if (prefixUnaryExpressionSyntax != null) { this.OperatorKind = prefixUnaryExpressionSyntax.OperatorToken.Kind; } else { var postfixUnaryExpressionSyntax = boundSequence.Syntax.Green as PostfixUnaryExpressionSyntax; if (postfixUnaryExpressionSyntax != null) { this.OperatorKind = postfixUnaryExpressionSyntax.OperatorToken.Kind; } } Debug.Assert(this.OperatorKind != SyntaxKind.None); if (this.Value.Type.OriginalDefinition != null && this.Value.Type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { return(false); } if (this.Value.Type.SpecialType == SpecialType.System_Decimal) { return(false); } return(true); }
public override object VisitSequence(BoundSequence node, object arg) { var sideEffects = node.SideEffects; if (!sideEffects.IsNullOrEmpty) { foreach (var se in sideEffects) { VisitExpression(se); } } VisitExpression(node.Value); return(null); }
private void SequenceNavigationNextButtonEventHandler(object sender, RoutedEventArgs e) { if (CurrentSequence == GlobalState.Sequences.Last()) { int wait = CurrentSequence.WaitTime; CurrentSequence = new BoundSequence(GlobalState.Locations, wait); GlobalState.Sequences.Add(CurrentSequence); } else if (CurrentSequence.IsBlank()) { int seqIndex = GlobalState.Sequences.IndexOf(CurrentSequence); GlobalState.Sequences.Remove(CurrentSequence); CurrentSequence = GlobalState.Sequences[seqIndex]; } else { CurrentSequence = GlobalState.Sequences[GlobalState.Sequences.IndexOf(CurrentSequence) + 1]; } UpdateNavigation(); }
private void SequenceNavigationInsertButtonEventHandler(object sender, RoutedEventArgs e) { int seqIndex = GlobalState.Sequences.IndexOf(CurrentSequence); int wait = CurrentSequence.WaitTime; if (CurrentSequence.IsBlank()) { GlobalState.Sequences.Remove(CurrentSequence); } CurrentSequence = new BoundSequence(); GlobalState.Sequences.Add(CurrentSequence); while (GlobalState.Sequences.IndexOf(CurrentSequence) > seqIndex) { int idx = GlobalState.Sequences.IndexOf(CurrentSequence); BoundSequence temp = GlobalState.Sequences[idx - 1]; GlobalState.Sequences[idx - 1] = CurrentSequence; GlobalState.Sequences[idx] = temp; } CurrentSequence.WaitTime = wait; UpdateNavigation(); }
private void PlayExplosionsEventHandler(object sender, EventArgs e) { if (GlobalState.Sequences.Count > 1 || (GlobalState.Sequences.Count > 0 && !CurrentSequence.IsBlank())) { if (CurrentSequence.IsBlank()) { GlobalState.Sequences.Remove(CurrentSequence); CurrentSequence = GlobalState.Sequences.First(); if (CurrentSequence == null) { CurrentSequence = new BoundSequence(); } } CurrentSequence.MakeExplosions(mainWindow.MapCanvas); int lapover = 42; if (CurrentSequence == GlobalState.Sequences.Last()) { lapover += 84; } SetWaitTime(CurrentSequence.WaitTime); if (CurrentSequence.WaitTime < 42) { lapover += 36; } SequenceCounterDisplay.Content = "Sequence #" + (GlobalState.Sequences.IndexOf(CurrentSequence) + 1); sequenceTimer.Interval = ((int)Math.Ceiling(CurrentSequence.WaitTime / 42.0)) * 42 + lapover; if (CurrentSequence != GlobalState.Sequences.Last()) { CurrentSequence = GlobalState.Sequences[GlobalState.Sequences.IndexOf(CurrentSequence) + 1]; } else { CurrentSequence = GlobalState.Sequences.First(); } } }
public override BoundNode VisitSequence(BoundSequence node) { ReadOnlyArray<BoundExpression> sideEffects = (ReadOnlyArray<BoundExpression>)this.VisitList(node.SideEffects); BoundExpression value = (BoundExpression)this.Visit(node.Value); TypeSymbol type = this.VisitType(node.Type); if (!RequiresSpill(sideEffects) && value.Kind != BoundKind.SpillSequence) { return node.Update(node.Locals, sideEffects, value, type); } var spillBuilder = new SpillBuilder(); spillBuilder.Locals.AddRange(node.Locals); foreach (var sideEffect in sideEffects) { spillBuilder.Statements.Add( (sideEffect.Kind == BoundKind.SpillSequence) ? RewriteSpillSequenceAsBlock((BoundSpillSequence)sideEffect) : F.ExpressionStatement(sideEffect)); } BoundExpression newValue; if (value.Kind == BoundKind.SpillSequence) { var awaitEffect = (BoundSpillSequence)value; spillBuilder.AddSpill(awaitEffect); newValue = awaitEffect.Value; } else { newValue = value; } return spillBuilder.BuildSequenceAndFree(F, newValue); }
private void EmitSequenceExpression(BoundSequence sequence, bool used) { var hasLocals = !sequence.Locals.IsEmpty; if (hasLocals) { builder.OpenLocalScope(); foreach (var local in sequence.Locals) { DefineLocal(local, sequence.Syntax); } } EmitSideEffects(sequence); // CONSIDER: LocalRewriter.RewriteNestedObjectOrCollectionInitializerExpression may create a bound sequence with an unused BoundTypeExpression as the value, // CONSIDER: which must be ignored by codegen. See comments in RewriteNestedObjectOrCollectionInitializerExpression for details and an example. // CONSIDER: We may want to instead consider making the Value field of BoundSequence node optional to allow a sequence with // CONSIDER: only side effects and no value. Note that VB's BoundSequence node has an optional value field. // CONSIDER: This will allow us to remove the below check before emitting the value. Debug.Assert(sequence.Value.Kind != BoundKind.TypeExpression || !used); if (sequence.Value.Kind != BoundKind.TypeExpression) { EmitExpression(sequence.Value, used); } if (hasLocals) { builder.CloseLocalScope(); foreach (var local in sequence.Locals) { FreeLocal(local); } } }
private void EmitSideEffects(BoundSequence sequence) { var sideEffects = sequence.SideEffects; if (!sideEffects.IsDefaultOrEmpty) { foreach (var se in sideEffects) { EmitExpression(se, false); } } }
private void DefineLocals(BoundSequence sequence) { if (sequence.Locals.IsEmpty) { return; } _builder.OpenLocalScope(); foreach (var local in sequence.Locals) { DefineLocal(local, sequence.Syntax); } }
public override BoundNode VisitSequence(BoundSequence node) { BoundSpillSequence2 ss2 = null; var value = VisitExpression(ref ss2, node.Value); BoundSpillSequence2 ss1 = null; var sideEffects = VisitExpressionList(ref ss1, node.SideEffects, forceSpill: ss2 != null, sideEffectsOnly: true); if (ss1 == null && ss2 == null) { return node.Update(node.Locals, sideEffects, value, node.Type); } if (ss1 == null) ss1 = new BoundSpillSequence2(); // possible if sideEffects is empty ss1.AddRange(sideEffects, MakeExpressionStatement); ss1.AddRange(node.Locals); ss1.IncludeSequence(ss2); return ss1.Update(value); }
// here we have a case of indirect assignment: *t1 = expr; // normally we would need to push t1 and that will cause spilling of t2 // // TODO: an interesting case arises in unused x[i]++ and ++x[i] : // we have trees that look like: // // t1 = &(x[0]) // t2 = *t1 // *t1 = t2 + 1 // // t1 = &(x[0]) // t2 = *t1 + 1 // *t1 = t2 // // in these cases, we could keep t2 on stack (dev10 does). // we are dealing with exactly 2 locals and access them in strict order // t1, t2, t1, t2 and we are not using t2 after that. // We may consider detecting exactly these cases and pretend that we do not need // to push either t1 or t2 in this case. // public override BoundNode VisitSequence(BoundSequence node) { // Normally we can only use stack for local scheduling if stack is not used for evaluation. // In a context of a regular block that simply means that eval stack must be empty. // Sequences, can be entered on a nonempty evaluation stack // Ex: // a.b = Seq{var y, y = 1, y} // a is on the stack for the duration of the sequence. // // However evaluation stack at the entry cannot be used inside the sequence, so such stack // works as effective "empty" for locals declared in sequence. // Therefore sequence locals can be stack scheduled at same stack as at the entry to the sequence. // it may seem attractive to relax the stack requirement to be: // "all uses must agree on stack depth". // The following example illustrates a case where x is safely used at "declarationStack + 1" // Ex: // Seq{var x; y.a = Seq{x = 1; x}; y} // x is used while y is on the eval stack // // It is, however not safe assumption in general since eval stack may be accessed between usages. // Ex: // Seq{var x; y.a = Seq{x = 1; x}; y.z = x; y} // x blocks access to y // // There is one case where we want to tweak the "use at declaration stack" rule - in the case of // compound assignment that involves ByRef operand captures (like: x[y]++ ) . // // Those cases produce specific sequences of the shapes: // // prefix: Seq{var temp, ref operand; operand initializers; *operand = Seq{temp = (T)(operand + 1); temp;} result: temp} // postfix: Seq{var temp, ref operand; operand initializers; *operand = Seq{temp = operand; ; (T)(temp + 1);} result: temp} // // 1) temp is used as the result of the sequence (and that is the only reason why it is declared in the outer sequence). // 2) all side-effects except the last one do not use the temp. // 3) last side-effect is an indirect assignment of a sequence (and target does not involve the temp). // // Note that in a case of side-effects context, the result value will be ignored and therefore // all usages of the nested temp will be confined to the nested sequence that is executed at +1 stack. // // We will detect such case and indicate +1 as the desired stack depth at local accesses. // var declarationStack = StackDepth(); var locals = node.Locals; if (!locals.IsDefaultOrEmpty) { if (_context == ExprContext.Sideeffects) { foreach (var local in locals) { if (IsNestedLocalOfCompoundOperator(local, node)) { // special case DeclareLocal(local, declarationStack + 1); } else { DeclareLocal(local, declarationStack); } } } else { DeclareLocals(locals, declarationStack); } } // rewrite operands var origContext = _context; var sideeffects = node.SideEffects; ArrayBuilder<BoundExpression> rewrittenSideeffects = null; if (!sideeffects.IsDefault) { for (int i = 0; i < sideeffects.Length; i++) { var sideeffect = sideeffects[i]; var rewrittenSideeffect = this.VisitExpression(sideeffect, ExprContext.Sideeffects); if (rewrittenSideeffects == null && rewrittenSideeffect != sideeffect) { rewrittenSideeffects = ArrayBuilder<BoundExpression>.GetInstance(); rewrittenSideeffects.AddRange(sideeffects, i); } if (rewrittenSideeffects != null) { rewrittenSideeffects.Add(rewrittenSideeffect); } } } var value = this.VisitExpression(node.Value, origContext); return node.Update(node.Locals, rewrittenSideeffects != null ? rewrittenSideeffects.ToImmutableAndFree() : sideeffects, value, node.Type); }
public override BoundNode VisitSequence(BoundSequence node) { AddVariables(node.Locals); return base.VisitSequence(node); }
public override BoundNode VisitSequence(BoundSequence node) { // Spilled local temps do not appear here in a sequence expression, because any temps in a // sequence expression that need to be spilled would have been moved up to the // statement level by the AwaitLiftingRewriter. if (!node.Locals.IsDefaultOrEmpty) { foreach (var local in node.Locals) { Debug.Assert(!VariablesCaptured.Contains(local) || proxies.ContainsKey(local)); } } return base.VisitSequence(node); }
/// <summary> /// Process tempStores and add them as sideeffects to arguments where needed. The return /// value tells how many temps are actually needed. For unnecesary temps the corresponding /// temp store will be cleared. /// </summary> private static int MergeArgumentsAndSideEffects( ArrayBuilder <BoundAssignmentOperator> tempStores, BoundExpression[] arguments) { Debug.Assert(tempStores != null); Debug.Assert(arguments != null); int tempsRemainedInUse = tempStores.Count; // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E() // and arguments: t0, t2, t1, t4, 10, t5 // We wish to produce arguments list: A(), SEQ(t2=B(), C()), t2, D(), 10, E() // // Our algorithm essentially finds temp stores that must happen before given argument load, // and if there are any they become sideefects of the given load // // Constraints: // Stores must happen before corresponding loads. Casuality. // Stores cannot move relative to other stores. If arg was movable it would not need a temp. // So for each argument: // t0: emit t0 = A(), t0. ===> A() // t2: emit t1 = B(), t2 = C(), t2. ===> SEQ{ B(), C(), t2 } // t1: emit t1; ===> t1 //all the dependencies of t1 must be already emitted // t4: emit t4 = D(), t4. ===> D() // t5: emit t5 = E(), t5. ===> E() int firstUnclaimedStore = 0; for (int a = 0; a < arguments.Length; ++a) { var argument = arguments[a]; // if argument is a load, search for corresponding store. if store is found, extract // the actual expression we were storing and add it as an argument - this one does // not need a temp. if there are any unclaimed stores before the found one, add them // as sideeffects that preceed this arg, they cannot happen later. if (argument.Kind == BoundKind.Local) { var correspondingStore = -1; for (int i = firstUnclaimedStore; i < tempStores.Count; i++) { if (tempStores[i].Left == argument) { correspondingStore = i; break; } } // store found? if (correspondingStore != -1) { var value = tempStores[correspondingStore].Right; // the matched store will not need to go into sideffects, only ones before it will // remove the store to signal that we are not using its temp. tempStores[correspondingStore] = null; tempsRemainedInUse--; // no need for sideeffects? // just combine store and load if (correspondingStore == firstUnclaimedStore) { arguments[a] = value; } else { var sideffects = new BoundExpression[correspondingStore - firstUnclaimedStore]; for (int s = 0; s < sideffects.Length; s++) { sideffects[s] = tempStores[firstUnclaimedStore + s]; } arguments[a] = new BoundSequence( null, null, // this sequence does not own locals. Note that temps that // we use for the rewrite are stored in one arg and loaded // in another so they must live in a scope above. ReadOnlyArray <LocalSymbol> .Empty, sideffects.AsReadOnlyWrap(), value, value.Type); } firstUnclaimedStore = correspondingStore + 1; } } } Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all sideeffects were claimed"); return(tempsRemainedInUse); }
public override BoundNode VisitObjectCreationExpression(BoundObjectCreationExpression node) { Debug.Assert(node != null); // Rewrite the arguments. // NOTE: We may need additional argument rewriting such as generating a params array, // re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. var rewrittenArguments = VisitList(node.Arguments); // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray<LocalSymbol> temps; ImmutableArray<RefKind> argumentRefKindsOpt = node.ArgumentRefKindsOpt; rewrittenArguments = MakeArguments(node.Syntax, rewrittenArguments, node.Constructor, node.Constructor, node.Expanded, node.ArgsToParamsOpt, ref argumentRefKindsOpt, out temps); BoundExpression rewrittenObjectCreation; if (_inExpressionLambda) { if (!temps.IsDefaultOrEmpty) { throw ExceptionUtilities.UnexpectedValue(temps.Length); } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, MakeObjectCreationInitializerForExpressionTree(node.InitializerExpressionOpt), changeTypeOpt: node.Constructor.ContainingType); if (node.Type.IsInterfaceType()) { Debug.Assert(rewrittenObjectCreation.Type == ((NamedTypeSymbol)node.Type).ComImportCoClass); rewrittenObjectCreation = MakeConversion(rewrittenObjectCreation, node.Type, false, false); } return rewrittenObjectCreation; } rewrittenObjectCreation = node.UpdateArgumentsAndInitializer(rewrittenArguments, newInitializerExpression: null, changeTypeOpt: node.Constructor.ContainingType); // replace "new S()" with a default struct ctor with "default(S)" if (node.Constructor.IsDefaultValueTypeConstructor()) { rewrittenObjectCreation = new BoundDefaultOperator(rewrittenObjectCreation.Syntax, rewrittenObjectCreation.Type); } if (!temps.IsDefaultOrEmpty) { rewrittenObjectCreation = new BoundSequence( node.Syntax, temps, ImmutableArray<BoundExpression>.Empty, rewrittenObjectCreation, node.Type); } if (node.Type.IsInterfaceType()) { Debug.Assert(rewrittenObjectCreation.Type == ((NamedTypeSymbol)node.Type).ComImportCoClass); rewrittenObjectCreation = MakeConversion(rewrittenObjectCreation, node.Type, false, false); } if (node.InitializerExpressionOpt == null || node.InitializerExpressionOpt.HasErrors) { return rewrittenObjectCreation; } return MakeObjectCreationWithInitializer(node.Syntax, rewrittenObjectCreation, node.InitializerExpressionOpt, node.Type); }
private void FreeLocals(BoundSequence sequence) { if (sequence.Locals.IsEmpty) { return; } builder.CloseLocalScope(); foreach (var local in sequence.Locals) { FreeLocal(local); } }
/// <summary> /// Process tempStores and add them as side-effects to arguments where needed. The return /// value tells how many temps are actually needed. For unnecessary temps the corresponding /// temp store will be cleared. /// </summary> private static int MergeArgumentsAndSideEffects( BoundExpression[] arguments, ArrayBuilder<RefKind> refKinds, ArrayBuilder<BoundAssignmentOperator> tempStores) { Debug.Assert(arguments != null); Debug.Assert(refKinds != null); Debug.Assert(tempStores != null); int tempsRemainedInUse = tempStores.Count; // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E() // and arguments: t0, t2, t1, t4, 10, t5 // // We wish to produce arguments list: A(), SEQ(t1=B(), C()), t1, D(), 10, E() // // Our algorithm essentially finds temp stores that must happen before given argument // load, and if there are any they become side effects of the given load. // Stores immediately followed by loads of the same thing can be eliminated. // // Constraints: // Stores must happen before corresponding loads. // Stores cannot move relative to other stores. If arg was movable it would not need a temp. int firstUnclaimedStore = 0; for (int a = 0; a < arguments.Length; ++a) { var argument = arguments[a]; // if argument is a load, search for corresponding store. if store is found, extract // the actual expression we were storing and add it as an argument - this one does // not need a temp. if there are any unclaimed stores before the found one, add them // as side effects that precede this arg, they cannot happen later. // NOTE: missing optional parameters are not filled yet and therefore nulls - no need to do anything for them if (argument?.Kind == BoundKind.Local) { var correspondingStore = -1; for (int i = firstUnclaimedStore; i < tempStores.Count; i++) { if (tempStores[i].Left == argument) { correspondingStore = i; break; } } // store found? if (correspondingStore != -1) { var value = tempStores[correspondingStore].Right; // When we created the temp, we dropped the argument RefKind // since the local contained its own RefKind. Since we're removing // the temp, the argument RefKind needs to be restored. refKinds[a] = ((BoundLocal)argument).LocalSymbol.RefKind; // the matched store will not need to go into side-effects, only ones before it will // remove the store to signal that we are not using its temp. tempStores[correspondingStore] = null; tempsRemainedInUse--; // no need for side-effects? // just combine store and load if (correspondingStore == firstUnclaimedStore) { arguments[a] = value; } else { var sideeffects = new BoundExpression[correspondingStore - firstUnclaimedStore]; for (int s = 0; s < sideeffects.Length; s++) { sideeffects[s] = tempStores[firstUnclaimedStore + s]; } arguments[a] = new BoundSequence( value.Syntax, // this sequence does not own locals. Note that temps that // we use for the rewrite are stored in one arg and loaded // in another so they must live in a scope above. ImmutableArray<LocalSymbol>.Empty, sideeffects.AsImmutableOrNull(), value, value.Type); } firstUnclaimedStore = correspondingStore + 1; } } } Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all side-effects were claimed"); return tempsRemainedInUse; }
// Omit ref feature for COM interop: We can pass arguments by value for ref parameters if we are calling a method/property on an instance of a COM imported type. // We should have ignored the 'ref' on the parameter during overload resolution for the given method call. // If we had any ref omitted argument for the given call, we create a temporary local and // replace the argument with the following BoundSequence: { side-effects: { temp = argument }, value = { ref temp } } // NOTE: The temporary local must be scoped to live across the entire BoundCall node, // otherwise the codegen optimizer might re-use the same temporary for multiple ref-omitted arguments for this call. private void RewriteArgumentsForComCall( ImmutableArray<ParameterSymbol> parameters, BoundExpression[] actualArguments, //already re-ordered to match parameters ArrayBuilder<RefKind> argsRefKindsBuilder, ArrayBuilder<LocalSymbol> temporariesBuilder) { Debug.Assert(actualArguments != null); Debug.Assert(actualArguments.Length == parameters.Length); Debug.Assert(argsRefKindsBuilder == null || argsRefKindsBuilder.Count == parameters.Length); var argsCount = actualArguments.Length; for (int argIndex = 0; argIndex < argsCount; ++argIndex) { RefKind paramRefKind = parameters[argIndex].RefKind; RefKind argRefKind = argsRefKindsBuilder[argIndex]; // Rewrite only if the argument was passed with no ref/out and the // parameter was declared ref. if (argRefKind != RefKind.None || paramRefKind != RefKind.Ref) { continue; } var argument = actualArguments[argIndex]; if (argument.Kind == BoundKind.Local) { var localRefKind = ((BoundLocal)argument).LocalSymbol.RefKind; if (localRefKind == RefKind.Ref) { // Already passing an address from the ref local. continue; } Debug.Assert(localRefKind == RefKind.None); } BoundAssignmentOperator boundAssignmentToTemp; BoundLocal boundTemp = _factory.StoreToTemp(argument, out boundAssignmentToTemp); actualArguments[argIndex] = new BoundSequence( argument.Syntax, locals: ImmutableArray<LocalSymbol>.Empty, sideEffects: ImmutableArray.Create<BoundExpression>(boundAssignmentToTemp), value: boundTemp, type: boundTemp.Type); argsRefKindsBuilder[argIndex] = RefKind.Ref; temporariesBuilder.Add(boundTemp.LocalSymbol); } }
// if sequence value is a local scoped to the sequence, return that local private LocalSymbol DigForValueLocal(BoundSequence topSequence) { return(DigForValueLocal(topSequence, topSequence.Value)); }
public override BoundNode VisitSequence(BoundSequence node) { var newLocals = RewriteLocals(node.Locals); var newSideEffects = VisitList<BoundExpression>(node.SideEffects); var newValue = (BoundExpression)this.Visit(node.Value); var newType = this.VisitType(node.Type); return node.Update(newLocals, newSideEffects, newValue, newType); }
private void EmitSequenceCondBranch(BoundSequence sequence, ref object dest, bool sense) { var hasLocals = !sequence.Locals.IsEmpty; if (hasLocals) { builder.OpenLocalScope(); foreach (var local in sequence.Locals) { DefineLocal(local, sequence.Syntax); } } EmitSideEffects(sequence); EmitCondBranch(sequence.Value, ref dest, sense); if (hasLocals) { builder.CloseLocalScope(); foreach (var local in sequence.Locals) { FreeLocal(local); } } }
// if sequence value is a local scoped to the sequence, return that local private LocalSymbol DigForValueLocal(BoundSequence topSequence) { return DigForValueLocal(topSequence, topSequence.Value); }
private void EmitSequenceCondBranch(BoundSequence sequence, ref object dest, bool sense) { DefineLocals(sequence); EmitSideEffects(sequence); EmitCondBranch(sequence.Value, ref dest, sense); // sequence is used as a value, can release all locals FreeLocals(sequence, doNotRelease: null); }
private LocalSymbol DigForValueLocal(BoundSequence topSequence, BoundExpression value) { switch (value.Kind) { case BoundKind.Local: var local = (BoundLocal)value; var symbol = local.LocalSymbol; if (topSequence.Locals.Contains(symbol)) { return symbol; } break; case BoundKind.Sequence: return DigForValueLocal(topSequence, ((BoundSequence)value).Value); case BoundKind.FieldAccess: var fieldAccess = (BoundFieldAccess)value; if (!fieldAccess.FieldSymbol.IsStatic) { var receiver = fieldAccess.ReceiverOpt; if (!receiver.Type.IsReferenceType) { return DigForValueLocal(topSequence, receiver); } } break; } return null; }
/// <summary> /// Process tempStores and add them as sideeffects to arguments where needed. The return /// value tells how many temps are actually needed. For unnecesary temps the corresponding /// temp store will be cleared. /// </summary> private static int MergeArgumentsAndSideEffects( ArrayBuilder<BoundAssignmentOperator> tempStores, BoundExpression[] arguments) { Debug.Assert(tempStores != null); Debug.Assert(arguments != null); int tempsRemainedInUse = tempStores.Count; // Suppose we've got temporaries: t0 = A(), t1 = B(), t2 = C(), t4 = D(), t5 = E() // and arguments: t0, t2, t1, t4, 10, t5 // We wish to produce arguments list: A(), SEQ(t2=B(), C()), t2, D(), 10, E() // // Our algorithm essentially finds temp stores that must happen before given argument load, // and if there are any they become sideefects of the given load // // Constraints: // Stores must happen before corresponding loads. Casuality. // Stores cannot move relative to other stores. If arg was movable it would not need a temp. // So for each argument: // t0: emit t0 = A(), t0. ===> A() // t2: emit t1 = B(), t2 = C(), t2. ===> SEQ{ B(), C(), t2 } // t1: emit t1; ===> t1 //all the dependencies of t1 must be already emitted // t4: emit t4 = D(), t4. ===> D() // t5: emit t5 = E(), t5. ===> E() int firstUnclaimedStore = 0; for (int a = 0; a < arguments.Length; ++a) { var argument = arguments[a]; // if argument is a load, search for corresponding store. if store is found, extract // the actual expression we were storing and add it as an argument - this one does // not need a temp. if there are any unclaimed stores before the found one, add them // as sideeffects that preceed this arg, they cannot happen later. if (argument.Kind == BoundKind.Local) { var correspondingStore = -1; for (int i = firstUnclaimedStore; i < tempStores.Count; i++) { if (tempStores[i].Left == argument) { correspondingStore = i; break; } } // store found? if (correspondingStore != -1) { var value = tempStores[correspondingStore].Right; // the matched store will not need to go into sideffects, only ones before it will // remove the store to signal that we are not using its temp. tempStores[correspondingStore] = null; tempsRemainedInUse--; // no need for sideeffects? // just combine store and load if (correspondingStore == firstUnclaimedStore) { arguments[a] = value; } else { var sideffects = new BoundExpression[correspondingStore - firstUnclaimedStore]; for (int s = 0; s < sideffects.Length; s++) { sideffects[s] = tempStores[firstUnclaimedStore + s]; } arguments[a] = new BoundSequence( null, null, // this sequence does not own locals. Note that temps that // we use for the rewrite are stored in one arg and loaded // in another so they must live in a scope above. ReadOnlyArray<LocalSymbol>.Empty, sideffects.AsReadOnlyWrap(), value, value.Type); } firstUnclaimedStore = correspondingStore + 1; } } } Debug.Assert(firstUnclaimedStore == tempStores.Count, "not all sideeffects were claimed"); return tempsRemainedInUse; }
private BoundNode RewriteWithRefOperand( bool isPrefix, bool isChecked, ArrayBuilder<LocalSymbol> tempSymbols, ArrayBuilder<BoundExpression> tempInitializers, CSharpSyntaxNode syntax, BoundExpression operand, TypeSymbol operandType, BoundExpression boundTemp, BoundExpression newValue) { var tempValue = isPrefix ? newValue : MakeRValue(operand); var tempAssignment = MakeAssignmentOperator(syntax, boundTemp, tempValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false); var operandValue = isPrefix ? boundTemp : newValue; var tempAssignedAndOperandValue = new BoundSequence( syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray.Create<BoundExpression>(tempAssignment), operandValue, tempValue.Type); // prefix: operand = Seq{temp = (T)(operand + 1); temp;} // postfix: operand = Seq{temp = operand; ; (T)(temp + 1);} BoundExpression operandAssignment = MakeAssignmentOperator(syntax, operand, tempAssignedAndOperandValue, operandType, used: false, isChecked: isChecked, isCompoundAssignment: false); // prefix: Seq{operand initializers; operand = Seq{temp = (T)(operand + 1); temp;} result: temp} // postfix: Seq{operand initializers; operand = Seq{temp = operand; ; (T)(temp + 1);} result: temp} tempInitializers.Add(operandAssignment); return new BoundSequence( syntax: syntax, locals: tempSymbols.ToImmutableAndFree(), sideEffects: tempInitializers.ToImmutableAndFree(), value: boundTemp, type: operandType); }
// detect a pattern used in compound operators // where a temp is declared in the outer sequence // only because it must be returned, otherwise all uses are // confined to the nested sequence that is assigned indirectly of to an instance field (and therefore has +1 stack) // in such case the desired stack for this local is +1 private static bool IsNestedLocalOfCompoundOperator(LocalSymbol local, BoundSequence node) { var value = node.Value; // local must be used as the value of the sequence. if (value != null && value.Kind == BoundKind.Local && ((BoundLocal)value).LocalSymbol == local) { var sideeffects = node.SideEffects; var lastSideeffect = sideeffects.LastOrDefault(); if (lastSideeffect != null) { // last side-effect must be an indirect assignment of a sequence. if (lastSideeffect.Kind == BoundKind.AssignmentOperator) { var assignment = (BoundAssignmentOperator)lastSideeffect; if (IsIndirectOrInstanceFieldAssignment(assignment) && assignment.Right.Kind == BoundKind.Sequence) { // and no other side-effects should use the variable var localUsedWalker = new LocalUsedWalker(local); for (int i = 0; i < sideeffects.Length - 1; i++) { if (localUsedWalker.IsLocalUsedIn(sideeffects[i])) { return false; } } // and local is not used on the left of the assignment // (extra check, but better be safe) if (localUsedWalker.IsLocalUsedIn(assignment.Left)) { return false; } // it should be used somewhere Debug.Assert(localUsedWalker.IsLocalUsedIn(assignment.Right), "who assigns the temp?"); return true; } } } } return false; }
/// <summary> /// May introduce a temp which it will return. (otherwise returns null) /// </summary> private LocalDefinition EmitSequenceAddress(BoundSequence sequence, AddressKind addressKind) { var hasLocals = !sequence.Locals.IsEmpty; if (hasLocals) { builder.OpenLocalScope(); foreach (var local in sequence.Locals) { DefineLocal(local, sequence.Syntax); } } EmitSideEffects(sequence); var tempOpt = EmitAddress(sequence.Value, addressKind); // when a sequence is happened to be a byref receiver // we may need to extend the life time of the target until we are done accessing it // {.v ; v = Foo(); v}.Bar() // v should be released after Bar() is over. LocalSymbol doNotRelease = null; if (tempOpt == null) { BoundLocal referencedLocal = DigForLocal(sequence.Value); if (referencedLocal != null) { doNotRelease = referencedLocal.LocalSymbol; } } if (hasLocals) { builder.CloseLocalScope(); foreach (var local in sequence.Locals) { if (local != doNotRelease) { FreeLocal(local); } else { tempOpt = GetLocal(doNotRelease); } } } return tempOpt; }
private void EmitSequenceExpression(BoundSequence sequence, bool used) { DefineLocals(sequence); EmitSideEffects(sequence); // CONSIDER: LocalRewriter.RewriteNestedObjectOrCollectionInitializerExpression may create a bound sequence with an unused BoundTypeExpression as the value, // CONSIDER: which must be ignored by codegen. See comments in RewriteNestedObjectOrCollectionInitializerExpression for details and an example. // CONSIDER: We may want to instead consider making the Value field of BoundSequence node optional to allow a sequence with // CONSIDER: only side effects and no value. Note that VB's BoundSequence node has an optional value field. // CONSIDER: This will allow us to remove the below check before emitting the value. Debug.Assert(sequence.Value.Kind != BoundKind.TypeExpression || !used); if (sequence.Value.Kind != BoundKind.TypeExpression) { EmitExpression(sequence.Value, used); } // sequence is used as a value, can release all locals FreeLocals(sequence, doNotRelease: null); }
public override BoundNode VisitSequence(BoundSequence node) { if (node.Locals.IsDefaultOrEmpty) { // ignore blocks that declare no variables. return base.VisitSequence(node); } var previousBlock = PushBlock(node, node.Locals); var result = base.VisitSequence(node); PopBlock(previousBlock); return result; }
private void FreeLocals(BoundSequence sequence, LocalSymbol doNotRelease) { if (sequence.Locals.IsEmpty) { return; } _builder.CloseLocalScope(); foreach (var local in sequence.Locals) { if ((object)local != doNotRelease) { FreeLocal(local); } } }
private void EmitSequenceCondBranch(BoundSequence sequence, ref object dest, bool sense) { DefineLocals(sequence); EmitSideEffects(sequence); EmitCondBranch(sequence.Value, ref dest, sense); FreeLocals(sequence); }
private BoundExpression TrivialLiftedComparisonOperatorOptimizations( CSharpSyntaxNode syntax, BinaryOperatorKind kind, BoundExpression left, BoundExpression right, MethodSymbol method) { Debug.Assert(left != null); Debug.Assert(right != null); // Optimization #1: if both sides are null then the result // is either true (for equality) or false (for everything else.) bool leftAlwaysNull = NullableNeverHasValue(left); bool rightAlwaysNull = NullableNeverHasValue(right); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); if (leftAlwaysNull && rightAlwaysNull) { return MakeLiteral(syntax, ConstantValue.Create(kind.Operator() == BinaryOperatorKind.Equal), boolType); } // Optimization #2: If both sides are non-null then we can again eliminate the lifting entirely. BoundExpression leftNonNull = NullableAlwaysHasValue(left); BoundExpression rightNonNull = NullableAlwaysHasValue(right); if (leftNonNull != null && rightNonNull != null) { return MakeBinaryOperator( syntax: syntax, operatorKind: kind.Unlifted(), loweredLeft: leftNonNull, loweredRight: rightNonNull, type: boolType, method: method); } // Optimization #3: If one side is null and the other is definitely not, then we generate the side effects // of the non-null side and result in true (for not-equals) or false (for everything else.) BinaryOperatorKind operatorKind = kind.Operator(); if (leftAlwaysNull && rightNonNull != null || rightAlwaysNull && leftNonNull != null) { BoundExpression result = MakeLiteral(syntax, ConstantValue.Create(operatorKind == BinaryOperatorKind.NotEqual), boolType); BoundExpression nonNull = leftAlwaysNull ? rightNonNull : leftNonNull; if (ReadIsSideeffecting(nonNull)) { result = new BoundSequence( syntax: syntax, locals: ImmutableArray<LocalSymbol>.Empty, sideEffects: ImmutableArray.Create<BoundExpression>(nonNull), value: result, type: boolType); } return result; } // Optimization #4: If one side is null and the other is unknown, then we have three cases: // #4a: If we have x == null then that becomes !x.HasValue. // #4b: If we have x != null then that becomes x.HasValue. // #4c: If we have x OP null then that becomes side effects of x, result in false. if (leftAlwaysNull || rightAlwaysNull) { BoundExpression maybeNull = leftAlwaysNull ? right : left; if (operatorKind == BinaryOperatorKind.Equal || operatorKind == BinaryOperatorKind.NotEqual) { BoundExpression callHasValue = MakeNullableHasValue(syntax, maybeNull); BoundExpression result = operatorKind == BinaryOperatorKind.Equal ? MakeUnaryOperator(UnaryOperatorKind.BoolLogicalNegation, syntax, null, callHasValue, boolType) : callHasValue; return result; } else { BoundExpression falseExpr = MakeBooleanConstant(syntax, operatorKind == BinaryOperatorKind.NotEqual); return _factory.Sequence(maybeNull, falseExpr); } } return null; }
public override BoundNode VisitSequence(BoundSequence node) { AddVariables(node.Locals); return(base.VisitSequence(node)); }