public BitTestLowering( SwitchFlow flow, IntegerConstant minValue, IntegerConstant valueRange, TypeEnvironment typeEnvironment) { this.Flow = flow; this.MinValue = minValue; this.ValueRange = valueRange; this.TypeEnvironment = typeEnvironment; }
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)); } }
/// <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); }
/// <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))); }
/// <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)); }
/// <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())); }
/// <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))); } }
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); }
public JumpTableLowering(SwitchFlow flow) { this.flow = flow; }
public TestCascadeLowering(SwitchFlow flow) { this.flow = flow; }
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)); }