Example #1
0
        private void EmitSwitchBucket(SwitchBucket switchBucket, object bucketFallThroughLabel)
        {
            if (switchBucket.LabelsCount == 1)
            {
                var c = switchBucket[0];
                //  if(key == constant)
                //      goto caseLabel;
                ConstantValue constant  = c.Key;
                object        caseLabel = c.Value;
                this.EmitEqBranchForSwitch(constant, caseLabel);
            }
            else
            {
                //  Emit key normalized to startConstant (i.e. key - startConstant)
                //  switch (N, label1, label2... labelN)
                //  goto fallThroughLabel;

                this.EmitNormalizedSwitchKey(switchBucket.StartConstant, switchBucket.EndConstant, bucketFallThroughLabel);

                // Create the labels array for emitting a switch instruction for the bucket
                object[] labels = this.CreateBucketLabels(switchBucket);

                // Emit the switch instruction
                _builder.EmitSwitch(labels);
            }

            _builder.EmitBranch(ILOpCode.Br, bucketFallThroughLabel);
        }
Example #2
0
        private object[] CreateBucketLabels(SwitchBucket switchBucket)
        {
            //  switch (N, t1, t2... tN)
            //      IL ==> ILOpCode.Switch < unsigned int32 > < int32 >... < int32 >

            //  For example: given a switch bucket [1, 3, 5] and FallThrough Label,
            //  we create the following labels array:
            //  { CaseLabel1, FallThrough, CaseLabel3, FallThrough, CaseLabel5 }

            ConstantValue startConstant         = switchBucket.StartConstant;
            bool          hasNegativeCaseLabels = startConstant.IsNegativeNumeric;

            int   nextCaseIndex = 0;
            ulong nextCaseLabelNormalizedValue = 0;

            ulong bucketSize = switchBucket.BucketSize;

            object[] labels = new object[bucketSize];

            for (ulong i = 0; i < bucketSize; ++i)
            {
                if (i == nextCaseLabelNormalizedValue)
                {
                    labels[i] = switchBucket[nextCaseIndex].Value;
                    nextCaseIndex++;

                    if (nextCaseIndex >= switchBucket.LabelsCount)
                    {
                        Debug.Assert(i == bucketSize - 1);
                        break;
                    }

                    ConstantValue caseLabelConstant = switchBucket[nextCaseIndex].Key;
                    if (hasNegativeCaseLabels)
                    {
                        var nextCaseLabelValue = caseLabelConstant.Int64Value;
                        Debug.Assert(nextCaseLabelValue > startConstant.Int64Value);
                        nextCaseLabelNormalizedValue = (ulong)(
                            nextCaseLabelValue - startConstant.Int64Value
                            );
                    }
                    else
                    {
                        var nextCaseLabelValue = caseLabelConstant.UInt64Value;
                        Debug.Assert(nextCaseLabelValue > startConstant.UInt64Value);
                        nextCaseLabelNormalizedValue =
                            nextCaseLabelValue - startConstant.UInt64Value;
                    }
                    continue;
                }

                labels[i] = _fallThroughLabel;
            }

            Debug.Assert(nextCaseIndex >= switchBucket.LabelsCount);
            return(labels);
        }
Example #3
0
            /// <summary>
            /// Try to merge with the nextBucket.
            /// If merge results in a better bucket than two original ones, merge and return true.
            /// Else don't merge and return false.
            /// </summary>
            internal bool TryMergeWith(SwitchBucket prevBucket)
            {
                Debug.Assert(prevBucket._endLabelIndex + 1 == _startLabelIndex);
                if (MergeIsAdvantageous(prevBucket, this))
                {
                    this = new SwitchBucket(_allLabels, prevBucket._startLabelIndex, _endLabelIndex);
                    return(true);
                }

                return(false);
            }
Example #4
0
            internal static bool MergeIsAdvantageous(SwitchBucket bucket1, SwitchBucket bucket2)
            {
                var startConstant = bucket1.StartConstant;
                var endConstant   = bucket2.EndConstant;

                if (BucketOverflow(startConstant, endConstant))
                {
                    // merged bucket would overflow
                    return(false);
                }

                uint  labelsCount = (uint)(bucket1.LabelsCount + bucket2.LabelsCount);
                ulong bucketSize  = GetBucketSize(startConstant, endConstant);

                return(!IsSparse(labelsCount, bucketSize));
            }
Example #5
0
        // Bucketing algorithm:

        //  Start with empty stack of buckets.

        //  While there are still labels

        //      If bucket from remaining labels is dense
        //             Create a newBucket from remaining labels
        //      Else
        //             Create a singleton newBucket from the next label

        //      While the top bucket on stack can be merged with newBucket into a dense bucket
        //          merge top bucket on stack into newBucket, and pop bucket from stack
        //      End While

        //      Push newBucket on stack

        //  End While

        private ImmutableArray <SwitchBucket> GenerateSwitchBuckets(int startLabelIndex, int endLabelIndex)
        {
            Debug.Assert(startLabelIndex >= 0 && startLabelIndex <= endLabelIndex);
            Debug.Assert(_sortedCaseLabels.Length > endLabelIndex);

            //  Start with empty stack of buckets.
            var switchBucketsStack = ArrayBuilder <SwitchBucket> .GetInstance();

            int curStartLabelIndex = startLabelIndex;

            //  While there are still labels
            while (curStartLabelIndex <= endLabelIndex)
            {
                SwitchBucket newBucket = CreateNextBucket(curStartLabelIndex, endLabelIndex);

                //      While the top bucket on stack can be merged with newBucket into a dense bucket
                //          merge top bucket on stack into newBucket, and pop bucket from stack
                //      End While

                while (!switchBucketsStack.IsEmpty())
                {
                    // get the bucket at top of the stack
                    SwitchBucket prevBucket = switchBucketsStack.Peek();
                    if (newBucket.TryMergeWith(prevBucket))
                    {
                        // pop the previous bucket from the stack
                        switchBucketsStack.Pop();
                    }
                    else
                    {
                        // merge failed
                        break;
                    }
                }

                //      Push newBucket on stack
                switchBucketsStack.Push(newBucket);

                // update curStartLabelIndex
                curStartLabelIndex++;
            }

            Debug.Assert(!switchBucketsStack.IsEmpty());

            // crumble leaf buckets that are too small
            var crumbled = ArrayBuilder <SwitchBucket> .GetInstance();

            foreach (var uncrumbled in switchBucketsStack)
            {
                if (uncrumbled.BucketCost > uncrumbled.LabelsCount)
                {
                    // this bucket is no better than testing each label individually.
                    // we do not want to keep it.
                    for (int i = uncrumbled.StartLabelIndex, l = uncrumbled.EndLabelIndex; i <= l; i++)
                    {
                        crumbled.Add(new SwitchBucket(_sortedCaseLabels, i));
                    }
                }
                else
                {
                    crumbled.Add(uncrumbled);
                }
            }

            switchBucketsStack.Free();
            return(crumbled.ToImmutableAndFree());
        }
        private object[] CreateBucketLabels(SwitchBucket switchBucket)
        {
            //  switch (N, t1, t2... tN)
            //      IL ==> ILOpCode.Switch < unsigned int32 > < int32 >... < int32 >

            //  For example: given a switch bucket [1, 3, 5] and FallThrough Label,
            //  we create the following labels array:
            //  { CaseLabel1, FallThrough, CaseLabel3, FallThrough, CaseLabel5 }

            ConstantValue startConstant = switchBucket.StartConstant;
            bool hasNegativeCaseLabels = startConstant.IsNegativeNumeric;

            int nextCaseIndex = 0;
            ulong nextCaseLabelNormalizedValue = 0;

            ulong bucketSize = switchBucket.BucketSize;
            object[] labels = new object[bucketSize];

            for (ulong i = 0; i < bucketSize; ++i)
            {
                if (i == nextCaseLabelNormalizedValue)
                {
                    labels[i] = switchBucket[nextCaseIndex].Value;
                    nextCaseIndex++;

                    if (nextCaseIndex >= switchBucket.LabelsCount)
                    {
                        Debug.Assert(i == bucketSize - 1);
                        break;
                    }

                    ConstantValue caseLabelConstant = switchBucket[nextCaseIndex].Key;
                    if (hasNegativeCaseLabels)
                    {
                        var nextCaseLabelValue = caseLabelConstant.Int64Value;
                        Debug.Assert(nextCaseLabelValue > startConstant.Int64Value);
                        nextCaseLabelNormalizedValue = (ulong)(nextCaseLabelValue - startConstant.Int64Value);
                    }
                    else
                    {
                        var nextCaseLabelValue = caseLabelConstant.UInt64Value;
                        Debug.Assert(nextCaseLabelValue > startConstant.UInt64Value);
                        nextCaseLabelNormalizedValue = nextCaseLabelValue - startConstant.UInt64Value;
                    }

                    continue;
                }

                labels[i] = fallThroughLabel;
            }

            Debug.Assert(nextCaseIndex >= switchBucket.LabelsCount);
            return labels;
        }
        private void EmitSwitchBucket(SwitchBucket switchBucket, object bucketFallThroughLabel)
        {
            if (switchBucket.LabelsCount == 1)
            {
                var c = switchBucket[0];
                //  if(key == constant)
                //      goto caseLabel;
                ConstantValue constant = c.Key;
                object caseLabel = c.Value;
                this.EmitEqBranchForSwitch(constant, caseLabel);
            }
            else
            {
                //  Emit key normalized to startConstant (i.e. key - startConstant)
                //  switch (N, label1, label2... labelN)
                //  goto fallThroughLabel;

                this.EmitNormalizedSwitchKey(switchBucket.StartConstant, switchBucket.EndConstant, bucketFallThroughLabel);

                // Create the labels array for emitting a switch instruction for the bucket
                object[] labels = this.CreateBucketLabels(switchBucket);

                // Emit the switch instruction
                builder.EmitSwitch(labels);
            }

            builder.EmitBranch(ILOpCode.Br, bucketFallThroughLabel);
        }
Example #8
0
        // Bucketing algorithm:

        //  Start with empty stack of buckets.

        //  While there are still labels

        //      If bucket from remaining labels is dense
        //             Create a newBucket from remaining labels
        //      Else
        //             Create a singleton newBucket from the next label

        //      While the top bucket on stack can be merged with newBucket into a dense bucket
        //          merge top bucket on stack into newBucket, and pop bucket from stack
        //      End While

        //      Push newBucket on stack

        //  End While

        private ImmutableArray <SwitchBucket> GenerateSwitchBuckets(int startLabelIndex, int endLabelIndex)
        {
            Debug.Assert(startLabelIndex >= 0 && startLabelIndex <= endLabelIndex);
            Debug.Assert(_sortedCaseLabels.Length > endLabelIndex);

            //  Start with empty stack of buckets.
            var switchBucketsStack = ArrayBuilder <SwitchBucket> .GetInstance();

            int curStartLabelIndex = startLabelIndex;

            //  While there are still labels
            while (curStartLabelIndex <= endLabelIndex)
            {
                SwitchBucket newBucket = CreateNextBucket(curStartLabelIndex, endLabelIndex);

                //      While the top bucket on stack can be merged with newBucket into a dense bucket
                //          merge top bucket on stack into newBucket, and pop bucket from stack
                //      End While

                while (!switchBucketsStack.IsEmpty())
                {
                    // get the bucket at top of the stack
                    SwitchBucket prevBucket = switchBucketsStack.Peek();
                    if (newBucket.TryMergeWith(prevBucket))
                    {
                        // pop the previous bucket from the stack
                        switchBucketsStack.Pop();
                    }
                    else
                    {
                        // merge failed
                        break;
                    }
                }

                //      Push newBucket on stack
                switchBucketsStack.Push(newBucket);

                // update curStartLabelIndex
                curStartLabelIndex++;
            }

            Debug.Assert(!switchBucketsStack.IsEmpty());

            // crumble leaf buckets into degenerate buckets where possible
            var crumbled = ArrayBuilder <SwitchBucket> .GetInstance();

            foreach (var uncrumbled in switchBucketsStack)
            {
                var degenerateSplit = uncrumbled.DegenerateBucketSplit;
                switch (degenerateSplit)
                {
                case -1:
                    // cannot be split
                    crumbled.Add(uncrumbled);
                    break;

                case 0:
                    // already degenerate
                    crumbled.Add(new SwitchBucket(_sortedCaseLabels, uncrumbled.StartLabelIndex, uncrumbled.EndLabelIndex, isDegenerate: true));
                    break;

                default:
                    // can split
                    crumbled.Add(new SwitchBucket(_sortedCaseLabels, uncrumbled.StartLabelIndex, degenerateSplit - 1, isDegenerate: true));
                    crumbled.Add(new SwitchBucket(_sortedCaseLabels, degenerateSplit, uncrumbled.EndLabelIndex, isDegenerate: true));
                    break;
                }
            }

            switchBucketsStack.Free();
            return(crumbled.ToImmutableAndFree());
        }
            /// <summary>
            /// Try to merge with the nextBucket.
            /// If merge results in a better bucket than two original ones, merge and return true.
            /// Else don't merge and return false.
            /// </summary>
            internal bool TryMergeWith(SwitchBucket prevBucket)
            {
                Debug.Assert(prevBucket._endLabelIndex + 1 == _startLabelIndex);
                if (MergeIsAdvantageous(prevBucket, this))
                {
                    this = new SwitchBucket(_allLabels, prevBucket._startLabelIndex, _endLabelIndex);
                    return true;
                }

                return false;
            }
            internal static bool MergeIsAdvantageous(SwitchBucket bucket1, SwitchBucket bucket2)
            {
                var startConstant = bucket1.StartConstant;
                var endConstant = bucket2.EndConstant;

                if (BucketOverflow(startConstant, endConstant))
                {
                    // merged bucket would overflow
                    return false;
                }

                uint labelsCount = (uint)(bucket1.LabelsCount + bucket2.LabelsCount);
                ulong bucketSize = GetBucketSize(startConstant, endConstant);

                return !IsSparse(labelsCount, bucketSize);
            }