/// <summary> /// Tells if syntactically equivalent instances of a particular intrinsic /// are semantically equivalent. /// </summary> /// <param name="intrinsic">An intrinsic to consider.</param> /// <returns> /// <c>true</c> if syntactically equivalent instances of /// <paramref name="intrinsic"/> are semantically equivalent; /// otherwise, <c>false</c>. /// </returns> private static bool IsCopyableIntrinsic(IntrinsicPrototype intrinsic) { return(ArithmeticIntrinsics.IsArithmeticIntrinsicPrototype(intrinsic) || ArrayIntrinsics.Namespace.IsIntrinsicPrototype(intrinsic, ArrayIntrinsics.Operators.GetLength) || ArrayIntrinsics.Namespace.IsIntrinsicPrototype(intrinsic, ArrayIntrinsics.Operators.GetElementPointer) || ExceptionIntrinsics.Namespace.IsIntrinsicPrototype(intrinsic, ExceptionIntrinsics.Operators.GetCapturedException)); }
/// <summary> /// The default constant instruction evaluation function. /// </summary> /// <param name="prototype"> /// The prorotype of the instruction to evaluate. /// </param> /// <param name="arguments"> /// A list of arguments to the instruction, all of which /// must be constants. /// </param> /// <returns> /// <c>null</c> if the instruction cannot be evaluated; otherwise, the constant /// to which the instruction evaluates. /// </returns> public static Constant EvaluateDefault( InstructionPrototype prototype, IReadOnlyList <Constant> arguments) { if (prototype is CopyPrototype || prototype is ReinterpretCastPrototype) { return(arguments[0]); } else if (prototype is ConstantPrototype) { var constProto = (ConstantPrototype)prototype; if (constProto.Value is DefaultConstant) { // Try to specialize 'default' constants. var intSpec = constProto.ResultType.GetIntegerSpecOrNull(); if (intSpec != null) { return(new IntegerConstant(0, intSpec)); } } return(constProto.Value); } else if (prototype is IntrinsicPrototype) { var intrinsicProto = (IntrinsicPrototype)prototype; Constant result; if (ArithmeticIntrinsics.IsArithmeticIntrinsicPrototype(intrinsicProto) && ArithmeticIntrinsics.TryEvaluate(intrinsicProto, arguments, out result)) { return(result); } } return(null); }
/// <summary> /// Looks for an instruction that is semantically /// equivalent to a given instruction but minimizes /// the number of layers of indirection erected by /// copies. /// </summary> /// <param name="instruction"> /// The instruction to simplify. /// </param> /// <param name="graph"> /// The graph that defines the instruction. /// </param> /// <returns> /// A semantically equivalent instruction.</returns> private static Instruction SimplifyInstruction( Instruction instruction, FlowGraph graph) { if (instruction.Prototype is CopyPrototype) { var copyProto = (CopyPrototype)instruction.Prototype; var copiedVal = copyProto.GetCopiedValue(instruction); if (graph.ContainsInstruction(copiedVal)) { var copiedInsn = graph.GetInstruction(copiedVal).Instruction; // Only simplify copies of arithmetic intriniscs and constants. // Those are the only instructions we're actually // interested in and they don't have any "funny" behavior. if (copiedInsn.Prototype is ConstantPrototype || copiedInsn.Prototype is CopyPrototype || ArithmeticIntrinsics.IsArithmeticIntrinsicPrototype(copiedInsn.Prototype)) { return(SimplifyInstruction(copiedInsn, graph)); } } } return(instruction); }
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); }