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); }
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); }
/// <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)); }
// 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); }
// 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); }