private void GenerateTest(BoundExpression test, BoundDecisionDagNode whenTrue, BoundDecisionDagNode whenFalse, BoundDecisionDagNode nextNode) { // Because we have already "optimized" away tests for a constant switch expression, the test should be nontrivial. _factory.Syntax = test.Syntax; Debug.Assert(test != null); if (nextNode == whenFalse) { _loweredDecisionDag.Add(_factory.ConditionalGoto(test, GetDagNodeLabel(whenTrue), jumpIfTrue: true)); // fall through to false path } else if (nextNode == whenTrue) { _loweredDecisionDag.Add(_factory.ConditionalGoto(test, GetDagNodeLabel(whenFalse), jumpIfTrue: false)); // fall through to true path } else { _loweredDecisionDag.Add(_factory.ConditionalGoto(test, GetDagNodeLabel(whenTrue), jumpIfTrue: true)); _loweredDecisionDag.Add(_factory.Goto(GetDagNodeLabel(whenFalse))); } }
/// <summary> /// Generate a switch dispatch for a contiguous sequence of dag nodes if applicable. /// Returns true if it was applicable. /// </summary> private bool GenerateSwitchDispatch(BoundDecisionDagNode node, HashSet <BoundDecisionDagNode> loweredNodes) { Debug.Assert(!loweredNodes.Contains(node)); if (!canGenerateSwitchDispatch(node)) { return(false); } var input = ((BoundTestDecisionDagNode)node).Test.Input; ValueDispatchNode n = GatherValueDispatchNodes(node, loweredNodes, input); LowerValueDispatchNode(n, _tempAllocator.GetTemp(input)); return(true); bool canGenerateSwitchDispatch(BoundDecisionDagNode node) { switch (node) { // These are the forms worth optimizing. case BoundTestDecisionDagNode { WhenFalse: BoundTestDecisionDagNode test2 } test1: return(canDispatch(test1, test2)); case BoundTestDecisionDagNode { WhenTrue: BoundTestDecisionDagNode test2 } test1: return(canDispatch(test1, test2)); default: // Other cases are just as well done with a single test. return(false); } bool canDispatch(BoundTestDecisionDagNode test1, BoundTestDecisionDagNode test2) { if (this._dagNodeLabels.ContainsKey(test2)) { return(false); } Debug.Assert(!loweredNodes.Contains(test2)); var t1 = test1.Test; var t2 = test2.Test; if (!(t1 is BoundDagValueTest || t1 is BoundDagRelationalTest)) { return(false); } if (!(t2 is BoundDagValueTest || t2 is BoundDagRelationalTest)) { return(false); } if (!t1.Input.Equals(t2.Input)) { return(false); } return(true); } } }
private void LowerDecisionDagCore(BoundDecisionDag decisionDag) { ImmutableArray <BoundDecisionDagNode> sortedNodes = decisionDag.TopologicallySortedNodes; var firstNode = sortedNodes[0]; switch (firstNode) { case BoundWhenDecisionDagNode _: case BoundLeafDecisionDagNode _: // If the first node is a leaf or when clause rather than the code for the // lowered decision dag, jump there to start. _loweredDecisionDag.Add(_factory.Goto(GetDagNodeLabel(firstNode))); break; } // Code for each when clause goes in the separate code section for its switch section. foreach (BoundDecisionDagNode node in sortedNodes) { if (node is BoundWhenDecisionDagNode w) { LowerWhenClause(w); } } ImmutableArray <BoundDecisionDagNode> nodesToLower = sortedNodes.WhereAsArray(n => n.Kind != BoundKind.WhenDecisionDagNode && n.Kind != BoundKind.LeafDecisionDagNode); var loweredNodes = PooledHashSet <BoundDecisionDagNode> .GetInstance(); for (int i = 0, length = nodesToLower.Length; i < length; i++) { BoundDecisionDagNode node = nodesToLower[i]; if (loweredNodes.Contains(node)) { Debug.Assert(!_dagNodeLabels.TryGetValue(node, out _)); continue; } if (this._dagNodeLabels.TryGetValue(node, out LabelSymbol label)) { _loweredDecisionDag.Add(_factory.Label(label)); } // If we can generate an IL switch instruction, do so if (GenerateSwitchDispatch(node, loweredNodes)) { continue; } // If we can generate a type test and cast more efficiently as an `is` followed by a null check, do so if (GenerateTypeTestAndCast(node, loweredNodes, nodesToLower, i)) { continue; } // We pass the node that will follow so we can permit a test to fall through if appropriate BoundDecisionDagNode nextNode = ((i + 1) < length) ? nodesToLower[i + 1] : null; if (nextNode != null && loweredNodes.Contains(nextNode)) { nextNode = null; } LowerDecisionDagNode(node, nextNode); } loweredNodes.Free(); }
protected ImmutableArray <BoundStatement> LowerDecisionDagCore(BoundDecisionDag decisionDag) { _loweredDecisionDag = ArrayBuilder <BoundStatement> .GetInstance(); ComputeLabelSet(decisionDag); ImmutableArray <BoundDecisionDagNode> sortedNodes = decisionDag.TopologicallySortedNodes; var firstNode = sortedNodes[0]; switch (firstNode) { case BoundWhenDecisionDagNode _: case BoundLeafDecisionDagNode _: // If the first node is a leaf or when clause rather than the code for the // lowered decision dag, jump there to start. _loweredDecisionDag.Add(_factory.Goto(GetDagNodeLabel(firstNode))); break; } // Code for each when clause goes in the separate code section for its switch section. foreach (BoundDecisionDagNode node in sortedNodes) { if (node is BoundWhenDecisionDagNode w) { LowerWhenClause(w); } } ImmutableArray <BoundDecisionDagNode> nodesToLower = sortedNodes.WhereAsArray(n => n.Kind != BoundKind.WhenDecisionDagNode && n.Kind != BoundKind.LeafDecisionDagNode); var loweredNodes = PooledHashSet <BoundDecisionDagNode> .GetInstance(); for (int i = 0, length = nodesToLower.Length; i < length; i++) { BoundDecisionDagNode node = nodesToLower[i]; // A node may have been lowered as part of a switch dispatch, but if it had a label, we'll need to lower it individually as well bool alreadyLowered = loweredNodes.Contains(node); if (alreadyLowered && !_dagNodeLabels.TryGetValue(node, out _)) { continue; } if (_dagNodeLabels.TryGetValue(node, out LabelSymbol label)) { _loweredDecisionDag.Add(_factory.Label(label)); } // If we can generate an IL switch instruction, do so if (!alreadyLowered && GenerateSwitchDispatch(node, loweredNodes)) { continue; } // If we can generate a type test and cast more efficiently as an `is` followed by a null check, do so if (GenerateTypeTestAndCast(node, loweredNodes, nodesToLower, i)) { continue; } // We pass the node that will follow so we can permit a test to fall through if appropriate BoundDecisionDagNode nextNode = ((i + 1) < length) ? nodesToLower[i + 1] : null; if (nextNode != null && loweredNodes.Contains(nextNode)) { nextNode = null; } LowerDecisionDagNode(node, nextNode); } loweredNodes.Free(); var result = _loweredDecisionDag.ToImmutableAndFree(); _loweredDecisionDag = null; return(result); }