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)));
                }
            }
Ejemplo n.º 2
0
            /// <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);
            }