Beispiel #1
0
            public override BlockFlow Emit(
                FlowGraphBuilder graph,
                ValueTag value)
            {
                // Create the following blocks:
                //
                // bitswitch.entry():
                //   minvalue = const <MinValue>
                //   switchval.adjusted = switchval - minvalue
                //   switchval.unsigned = (uintX)switchval.adjusted
                //   valrange = const <ValueRange>
                //   switch (switchval.unsigned > valrange)
                //     0 -> bitswitch.header()
                //     default -> <defaultBranch>
                //
                // bitswitch.header():
                //   one = const 1
                //   shifted = one << switchval.unsigned
                //   bitmask1 = const <bitmask1>
                //   switch (shifted & bitmask1)
                //     0 -> bitswitch.case2()
                //     default -> <case1Branch>
                //
                // bitswitch.case2():
                //   bitmask2 = const <bitmask2>
                //   switch (shifted & bitmask1)
                //     0 -> bitswitch.case3()
                //     default -> <case2Branch>
                //
                // ...

                var entryBlock  = graph.AddBasicBlock("bitswitch.entry");
                var headerBlock = graph.AddBasicBlock("bitswitch.header");

                var valueType = graph.GetValueType(value);
                var valueSpec = valueType.GetIntegerSpecOrNull();

                var defaultBranch = Flow.DefaultBranch;

                // Subtract the min value from the switch value if necessary.
                if (!MinValue.IsZero)
                {
                    value = entryBlock.AppendInstruction(
                        Instruction.CreateBinaryArithmeticIntrinsic(
                            ArithmeticIntrinsics.Operators.Subtract,
                            false,
                            valueType,
                            value,
                            entryBlock.AppendInstruction(
                                Instruction.CreateConstant(MinValue, valueType),
                                "minvalue")),
                        "switchval.adjusted");
                }

                // Make the switch value unsigned if it wasn't already.
                if (valueSpec.IsSigned)
                {
                    var uintType = TypeEnvironment.MakeUnsignedIntegerType(valueSpec.Size);
                    value = entryBlock.AppendInstruction(
                        Instruction.CreateConvertIntrinsic(
                            false,
                            uintType,
                            valueType,
                            value),
                        "switchval.unsigned");
                    valueType = uintType;
                    valueSpec = uintType.GetIntegerSpecOrNull();
                }

                // Check that the value is within range.
                entryBlock.Flow = SwitchFlow.CreateIfElse(
                    Instruction.CreateRelationalIntrinsic(
                        ArithmeticIntrinsics.Operators.IsGreaterThan,
                        TypeEnvironment.Boolean,
                        valueType,
                        value,
                        entryBlock.AppendInstruction(
                            Instruction.CreateConstant(
                                ValueRange.CastSignedness(false),
                                valueType),
                            "valrange")),
                    defaultBranch,
                    new Branch(headerBlock));

                // Pick an appropriate type for the bitmasks.
                var bitmaskType = valueType;

                if (valueSpec.Size < 32)
                {
                    bitmaskType = TypeEnvironment.UInt32;
                }
                if (ValueRange.IsGreaterThan(new IntegerConstant(valueSpec.Size, ValueRange.Spec)))
                {
                    bitmaskType = TypeEnvironment.UInt64;
                }

                // Set up first part of the header block.
                if (bitmaskType != valueType)
                {
                    valueSpec = bitmaskType.GetIntegerSpecOrNull();
                }

                var zero = headerBlock.AppendInstruction(
                    Instruction.CreateConstant(
                        new IntegerConstant(0, valueSpec),
                        valueType),
                    "zero");

                var one = headerBlock.AppendInstruction(
                    Instruction.CreateConstant(
                        new IntegerConstant(1, valueSpec),
                        valueType),
                    "one");

                value = headerBlock.AppendInstruction(
                    Instruction.CreateArithmeticIntrinsic(
                        ArithmeticIntrinsics.Operators.LeftShift,
                        false,
                        bitmaskType,
                        new[] { bitmaskType, valueType },
                        new[] { one, value }),
                    "shifted");

                valueType = bitmaskType;

                // Start emitting cases.
                var caseBlock = headerBlock;
                var nextCase  = graph.AddBasicBlock("bitswitch.case1");

                for (int i = 0; i < Flow.Cases.Count; i++)
                {
                    // Construct a mask for the case.
                    var switchCase  = Flow.Cases[i];
                    var oneConstant = new IntegerConstant(1, valueSpec);
                    var mask        = new IntegerConstant(0, valueSpec);
                    foreach (var pattern in switchCase.Values)
                    {
                        mask = mask.BitwiseOr(oneConstant.ShiftLeft(((IntegerConstant)pattern).Subtract(MinValue)));
                    }

                    // Switch on the bitwise 'and' of the mask and
                    // the shifted value.
                    caseBlock.Flow = SwitchFlow.CreateIfElse(
                        Instruction.CreateBinaryArithmeticIntrinsic(
                            ArithmeticIntrinsics.Operators.And,
                            false,
                            valueType,
                            value,
                            caseBlock.AppendInstruction(
                                Instruction.CreateConstant(mask, valueType),
                                "bitmask" + i)),
                        switchCase.Branch,
                        new Branch(nextCase));

                    caseBlock = nextCase;
                    nextCase  = graph.AddBasicBlock("bitswitch.case" + (i + 2));
                }

                // Jump to the default branch if nothing matches.
                caseBlock.Flow = new JumpFlow(defaultBranch);

                // Jump to the header block and let it do all of the heavy
                // lifting.
                return(new JumpFlow(entryBlock));
            }