コード例 #1
0
 public BitTestLowering(
     SwitchFlow flow,
     IntegerConstant minValue,
     IntegerConstant valueRange,
     TypeEnvironment typeEnvironment)
 {
     this.Flow            = flow;
     this.MinValue        = minValue;
     this.ValueRange      = valueRange;
     this.TypeEnvironment = typeEnvironment;
 }
コード例 #2
0
        private SwitchFlowLowering GetSwitchFlowLowering(SwitchFlow flow)
        {
            // This algorithm is based on the switch-lowering algorithm described in
            // "Improving Switch Lowering for The LLVM Compiler System" by Anton Korobeynikov
            // (http://llvm.org/pubs/2007-05-31-Switch-Lowering.pdf)

            if (flow.Cases.Count == 0)
            {
                return(new JumpLowering(flow.DefaultBranch));
            }
            else if (flow.IsIntegerSwitch)
            {
                var values = flow.Cases
                             .SelectMany(item => item.Values)
                             .Cast <IntegerConstant>()
                             .OrderBy(val => val)
                             .ToArray();

                var minValue   = values[0];
                var maxValue   = values[values.Length - 1];
                var valueRange = maxValue.Subtract(minValue);

                if (ShouldUseBitTestSwitch(valueRange, flow.Cases.Count, values.Length))
                {
                    return(new BitTestLowering(flow, minValue, valueRange, TypeEnvironment));
                }
                else if (values.Length <= 3)
                {
                    return(new TestCascadeLowering(flow));
                }
                else if (ShouldUseJumpTable(valueRange, values.Length))
                {
                    return(new JumpTableLowering(flow));
                }
                else
                {
                    var splitFlows = SplitIntegerSwitch(values, flow);
                    return(new SearchTreeLowering(
                               splitFlows.Item1,
                               GetSwitchFlowLowering(splitFlows.Item2),
                               GetSwitchFlowLowering(splitFlows.Item3),
                               TypeEnvironment));
                }
            }
            else
            {
                // TODO: use the search tree lowering for large switches
                // with non-integer, comparable constants (e.g., float32
                // and float64).
                return(new TestCascadeLowering(flow));
            }
        }
コード例 #3
0
            /// <inheritdoc/>
            public override BlockFlow Emit(FlowGraphBuilder graph, ValueTag value)
            {
                int caseCounter = 0;
                BasicBlockBuilder currentBlock = null;
                var       defaultJump          = new JumpFlow(flow.DefaultBranch);
                BlockFlow result = defaultJump;

                foreach (var switchCase in flow.Cases)
                {
                    foreach (var pattern in switchCase.Values)
                    {
                        caseCounter++;
                        var nextBlock = graph.AddBasicBlock("case" + caseCounter);
                        var blockFlow = new SwitchFlow(
                            Instruction.CreateCopy(graph.GetValueType(value), value),
                            new[]
                        {
                            new SwitchCase(ImmutableHashSet.Create(pattern), switchCase.Branch)
                        },
                            new Branch(nextBlock));

                        if (currentBlock == null)
                        {
                            result = blockFlow;
                        }
                        else
                        {
                            currentBlock.Flow = blockFlow;
                        }
                        currentBlock = nextBlock;
                    }
                }

                if (currentBlock != null)
                {
                    currentBlock.Flow = defaultJump;
                }

                return(result);
            }
コード例 #4
0
        /// <summary>
        /// Splits an integer switch into two switches.
        /// </summary>
        /// <param name="values">
        /// A sorted list of all values the switch flow lists in its cases.
        /// </param>
        /// <param name="flow">The switch flow to split.</param>
        /// <returns>A (pivot value, left switch, right switch) triple.</returns>
        private Tuple <IntegerConstant, SwitchFlow, SwitchFlow> SplitIntegerSwitch(
            IntegerConstant[] values,
            SwitchFlow flow)
        {
            int pivotIndex = ChooseIntegerPivotIndex(values);
            var leftValues = ImmutableHashSet.CreateRange(
                new ArraySegment <IntegerConstant>(
                    values,
                    0,
                    pivotIndex + 1));

            var rightValues = ImmutableHashSet.CreateRange(
                new ArraySegment <IntegerConstant>(
                    values,
                    pivotIndex + 1,
                    values.Length - pivotIndex - 1));

            var leftCases  = new List <SwitchCase>();
            var rightCases = new List <SwitchCase>();

            foreach (var switchCase in flow.Cases)
            {
                var leftPattern = switchCase.Values.Intersect(leftValues);
                if (!leftPattern.IsEmpty)
                {
                    leftCases.Add(new SwitchCase(leftPattern, switchCase.Branch));
                }

                var rightPattern = switchCase.Values.Intersect(rightValues);
                if (!rightPattern.IsEmpty)
                {
                    rightCases.Add(new SwitchCase(rightPattern, switchCase.Branch));
                }
            }

            return(new Tuple <IntegerConstant, SwitchFlow, SwitchFlow>(
                       values[pivotIndex],
                       new SwitchFlow(flow.SwitchValue, leftCases, flow.DefaultBranch),
                       new SwitchFlow(flow.SwitchValue, rightCases, flow.DefaultBranch)));
        }
コード例 #5
0
            /// <inheritdoc/>
            public override BlockFlow Emit(FlowGraphBuilder graph, ValueTag value)
            {
                var headerBlock = graph.AddBasicBlock("searchtree.header");
                var leftBlock   = graph.AddBasicBlock("searchtree.left");
                var rightBlock  = graph.AddBasicBlock("searchtree.right");

                var valueType = graph.GetValueType(value);

                headerBlock.Flow = SwitchFlow.CreateIfElse(
                    Instruction.CreateRelationalIntrinsic(
                        ArithmeticIntrinsics.Operators.IsGreaterThan,
                        typeEnvironment.Boolean,
                        valueType,
                        value,
                        headerBlock.AppendInstruction(
                            Instruction.CreateConstant(pivot, valueType))),
                    new Branch(rightBlock),
                    new Branch(leftBlock));

                leftBlock.Flow  = leftTree.Emit(graph, value);
                rightBlock.Flow = rightTree.Emit(graph, value);

                return(new JumpFlow(headerBlock));
            }
コード例 #6
0
        /// <summary>
        /// Compiles Brainfuck source code down to a method body.
        /// </summary>
        /// <param name="reader">
        /// A source reader for Brainfuck source code.
        /// </param>
        /// <returns>
        /// A method body.
        /// </returns>
        private MethodBody CompileBody(SourceReader reader)
        {
            // Create a control-flow graph that consists of an entry point only.
            var graph = new FlowGraphBuilder();

            // Use a permissive exception delayability model to make the optimizer's
            // life easier.
            graph.AddAnalysis(
                new ConstantAnalysis <ExceptionDelayability>(
                    PermissiveExceptionDelayability.Instance));

            // Grab the entry point block.
            var block = graph.EntryPoint;

            // Allocate an array of Brainfuck cells, which we'll represent using
            // 8-bit unsigned integers (i.e., unsigned bytes).
            var cellCount = block.AppendInstruction(
                Instruction.CreateConstant(
                    new IntegerConstant(30000, IntegerSpec.Int32),
                    Environment.Int32));

            var cells = block.AppendInstruction(
                Instruction.CreateNewArrayIntrinsic(
                    Environment.MakeArrayType(Environment.UInt8, 1),
                    Environment.Int32,
                    cellCount));

            // Allocate a stack variable that stores an index into the array.
            var indexAlloca = block.AppendInstruction(
                Instruction.CreateAlloca(Environment.Int32));

            // Initially set that variable to one.
            block.AppendInstruction(
                Instruction.CreateStore(
                    Environment.Int32,
                    indexAlloca,
                    block.AppendInstruction(
                        Instruction.CreateConstant(
                            new IntegerConstant(1, IntegerSpec.Int32),
                            Environment.Int32))));

            // We now iterate through the Brainfuck source code and turn it into
            // instructions.
            var whileHeaders     = new Stack <BasicBlockBuilder>();
            var whileTerminators = new Stack <BasicBlockBuilder>();

            while (!reader.IsEmpty)
            {
                char item = reader.Code[reader.Position];
                if (item == '>')
                {
                    IncrementOrDecrement(indexAlloca, block, ArithmeticIntrinsics.Operators.Add);
                }
                else if (item == '<')
                {
                    IncrementOrDecrement(indexAlloca, block, ArithmeticIntrinsics.Operators.Subtract);
                }
                else if (item == '+')
                {
                    IncrementOrDecrement(
                        GetDataPointer(Environment.UInt8, cells, indexAlloca, block),
                        block,
                        ArithmeticIntrinsics.Operators.Add);
                }
                else if (item == '-')
                {
                    IncrementOrDecrement(
                        GetDataPointer(Environment.UInt8, cells, indexAlloca, block),
                        block,
                        ArithmeticIntrinsics.Operators.Subtract);
                }
                else if (item == '[')
                {
                    var loopHeader     = graph.AddBasicBlock();
                    var loopBody       = graph.AddBasicBlock();
                    var loopTerminator = graph.AddBasicBlock();

                    whileHeaders.Push(loopHeader);
                    whileTerminators.Push(loopTerminator);

                    var dataPtr = GetDataPointer(Environment.UInt8, cells, indexAlloca, block);
                    loopHeader.Flow = SwitchFlow.CreateConstantCheck(
                        Instruction.CreateLoad(Environment.UInt8, dataPtr),
                        new IntegerConstant(0, IntegerSpec.UInt8),
                        new Branch(loopTerminator),
                        new Branch(loopBody));

                    block.Flow = new JumpFlow(loopHeader);

                    block = loopBody;

                    if (reader.IsEmpty)
                    {
                        Log.Log(
                            new LogEntry(
                                Severity.Error,
                                "loop not closed",
                                "a loop was opened with '[', but not closed with ']'.",
                                reader.Highlight(reader.Position, 1)));
                    }
                }
                else if (item == ']')
                {
                    if (whileHeaders.Count == 0)
                    {
                        Log.Log(
                            new LogEntry(
                                Severity.Warning,
                                "program closed",
                                "the program was closed by ']'. This is an fbfc compiler extension.",
                                reader.Highlight(reader.Position, 1)));
                    }
                    var header = whileHeaders.Pop();
                    var term   = whileTerminators.Pop();
                    block.Flow = new JumpFlow(header);
                    block      = term;
                }
                else if (item == '.')
                {
                    var ptr = GetDataPointer(Environment.UInt8, cells, indexAlloca, block);
                    Dependencies.EmitWrite(
                        ref block,
                        block.AppendInstruction(Instruction.CreateLoad(Environment.UInt8, ptr)));
                }
                else if (item == ',')
                {
                    var ptr = GetDataPointer(Environment.UInt8, cells, indexAlloca, block);
                    block.AppendInstruction(
                        Instruction.CreateStore(
                            Environment.UInt8,
                            ptr,
                            Dependencies.EmitRead(ref block, Environment.UInt8)));
                }
                reader.Position++;
            }

            // Terminate the block with a 'return void' flow.
            block.Flow = new ReturnFlow(
                Instruction.CreateConstant(DefaultConstant.Instance, Environment.Void));

            // Finish up the method body.
            return(new MethodBody(
                       new Parameter(Environment.Void),
                       default(Parameter),
                       EmptyArray <Parameter> .Value,
                       graph.ToImmutable()));
        }
コード例 #7
0
        /// <summary>
        /// Emits instructions that read a character from the input stream.
        /// </summary>
        /// <param name="block">A basic block builder.</param>
        /// <param name="resultType">A control-flow graph builder.</param>
        /// <returns>The character that is read from the input stream.</returns>
        public ValueTag EmitRead(ref BasicBlockBuilder block, IType resultType)
        {
            if (ReadMethod == null)
            {
                // If we didn't find a 'read' method, then we'll just return zero.
                return(block.AppendInstruction(
                           Instruction.CreateConstant(
                               new IntegerConstant(0, resultType.GetIntegerSpecOrNull()),
                               resultType)));
            }

            var returnValue = block.AppendInstruction(
                Instruction.CreateCall(
                    ReadMethod,
                    MethodLookup.Static,
                    EmptyArray <ValueTag> .Value));

            var returnType = returnValue.Instruction.ResultType;

            if (returnType == resultType)
            {
                return(returnValue);
            }
            else if (returnType.IsSignedIntegerType() && !resultType.IsSignedIntegerType())
            {
                // When converting a signed return type to an unsigned result type,
                // we want to map negative values to zero because both represent an
                // end-of-stream marker but a direct conversion won't preserve those
                // semantics.

                // Create a zero constant with the same type as the return type.
                var returnZero = block.AppendInstruction(
                    Instruction.CreateConstant(
                        new IntegerConstant(0, returnType.GetIntegerSpecOrNull()),
                        returnType));

                // Compare the return value to zero.
                var lt = Instruction.CreateRelationalIntrinsic(
                    ArithmeticIntrinsics.Operators.IsLessThan,
                    returnType,
                    returnType,
                    returnValue,
                    returnZero);

                // Create a new basic block so we can set `block`'s flow to a switch.
                var successorBlock = block.Graph.AddBasicBlock();
                var resultParam    = new BlockParameter(resultType);
                successorBlock.AppendParameter(resultParam);

                // Create an additional basic block that converts the return value to
                // the result type if the return value is positive.
                var convBlock = block.Graph.AddBasicBlock();

                // Convert the return value to the result type.
                var convReturnValue = block.AppendInstruction(
                    Instruction.CreateConvertIntrinsic(resultType, returnType, returnValue));

                // Set the conversion block's outgoing flow to jump to the successor block.
                convBlock.Flow = new JumpFlow(successorBlock, new ValueTag[] { convReturnValue });

                // Create a zero constant with the same type as the result type.
                var resultZero = block.AppendInstruction(
                    Instruction.CreateConstant(
                        new IntegerConstant(0, resultType.GetIntegerSpecOrNull()),
                        resultType));

                // Set the outgoing flow.
                block.Flow = SwitchFlow.CreateIfElse(
                    lt,
                    new Branch(successorBlock, new ValueTag[] { resultZero }),
                    new Branch(convBlock));

                // Update the block.
                block = successorBlock;

                // Return the value tag of the result parameter.
                return(resultParam.Tag);
            }
            else
            {
                // Otherwise, just convert the result using a straightforward intrinsic.
                return(block.AppendInstruction(
                           Instruction.CreateConvertIntrinsic(resultType, returnType, returnValue)));
            }
        }
コード例 #8
0
        private static BlockFlow SimplifySwitchFlow(SwitchFlow flow, FlowGraph graph)
        {
            var value = SimplifyInstruction(flow.SwitchValue, graph);

            if (value.Prototype is ConstantPrototype)
            {
                // Turn the switch into a jump.
                var constant         = ((ConstantPrototype)value.Prototype).Value;
                var valuesToBranches = flow.ValueToBranchMap;
                return(new JumpFlow(
                           valuesToBranches.ContainsKey(constant)
                    ? valuesToBranches[constant]
                    : flow.DefaultBranch));
            }
            else if (ArithmeticIntrinsics.IsArithmeticIntrinsicPrototype(value.Prototype))
            {
                var proto         = (IntrinsicPrototype)value.Prototype;
                var intrinsicName = ArithmeticIntrinsics.ParseArithmeticIntrinsicName(proto.Name);
                if (intrinsicName == ArithmeticIntrinsics.Operators.Convert &&
                    proto.ParameterCount == 1 &&
                    flow.IsIntegerSwitch)
                {
                    // We can eliminate instructions that extend integers
                    // by changing the values in the list of cases.
                    var operand     = proto.GetArgumentList(value).Single();
                    var operandType = graph.GetValueType(operand);
                    var convType    = proto.ResultType;
                    var operandSpec = operandType.GetIntegerSpecOrNull();

                    if (operandSpec == null)
                    {
                        // The operand of the conversion intrinsic is not an
                        // integer.
                        return(flow);
                    }

                    var convSpec = convType.GetIntegerSpecOrNull();

                    if (operandSpec.Size > convSpec.Size)
                    {
                        // We can't handle this case. To handle it anyway
                        // would require us to introduce additional cases
                        // and that's costly.
                        return(flow);
                    }

                    var caseList = new List <SwitchCase>();
                    foreach (var switchCase in flow.Cases)
                    {
                        // Retain only those switch cases that have values
                        // that are in the range of the conversion function.
                        var values = ImmutableHashSet.CreateBuilder <Constant>();
                        foreach (var val in switchCase.Values.Cast <IntegerConstant>())
                        {
                            var opVal = val.Cast(operandSpec);
                            if (opVal.Cast(convSpec).Equals(val))
                            {
                                values.Add(opVal);
                            }
                        }
                        if (values.Count > 0)
                        {
                            caseList.Add(new SwitchCase(values.ToImmutableHashSet(), switchCase.Branch));
                        }
                    }
                    return(SimplifySwitchFlow(
                               new SwitchFlow(
                                   Instruction.CreateCopy(
                                       operandType,
                                       operand),
                                   caseList,
                                   flow.DefaultBranch),
                               graph));
                }
                else if (intrinsicName == ArithmeticIntrinsics.Operators.IsEqualTo &&
                         proto.ParameterCount == 2 &&
                         proto.ResultType.IsIntegerType())
                {
                    var      args = proto.GetArgumentList(value);
                    var      lhs  = args[0];
                    var      rhs  = args[1];
                    Constant constant;
                    ValueTag operand;
                    if (TryExtractConstantAndValue(lhs, rhs, graph, out constant, out operand))
                    {
                        // The 'arith.eq' intrinsic always either produces '0' or '1'.
                        // Because of that property, we can safely rewrite switches
                        // like so:
                        //
                        // switch arith.eq(value, constant)
                        //   0 -> zeroBranch
                        //   1 -> oneBranch
                        //   default -> defaultBranch
                        //
                        // -->
                        //
                        // switch value
                        //   constant -> oneBranch ?? defaultBranch
                        //   default -> zeroBranch ?? defaultBranch
                        //
                        var resultSpec = proto.ResultType.GetIntegerSpecOrNull();
                        var zeroVal    = new IntegerConstant(0, resultSpec);
                        var oneVal     = new IntegerConstant(1, resultSpec);

                        var valuesToBranches = flow.ValueToBranchMap;
                        var zeroBranch       = valuesToBranches.ContainsKey(zeroVal)
                            ? valuesToBranches[zeroVal]
                            : flow.DefaultBranch;
                        var oneBranch = valuesToBranches.ContainsKey(oneVal)
                            ? valuesToBranches[oneVal]
                            : flow.DefaultBranch;

                        return(SimplifySwitchFlow(
                                   new SwitchFlow(
                                       Instruction.CreateCopy(
                                           graph.GetValueType(operand),
                                           operand),
                                       new[] { new SwitchCase(ImmutableHashSet.Create(constant), oneBranch) },
                                       zeroBranch),
                                   graph));
                    }
                }
            }
            return(flow);
        }
コード例 #9
0
 public JumpTableLowering(SwitchFlow flow)
 {
     this.flow = flow;
 }
コード例 #10
0
 public TestCascadeLowering(SwitchFlow flow)
 {
     this.flow = flow;
 }
コード例 #11
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));
            }