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