예제 #1
0
 /// <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));
 }
예제 #2
0
        private static bool IsLeftToRightReassociable(
            IntrinsicPrototype prototype,
            out IntrinsicPrototype rightPrototype)
        {
            string op;
            string rightOp;
            bool   isChecked;

            if (ArithmeticIntrinsics.TryParseArithmeticIntrinsicName(prototype.Name, out op, out isChecked) &&
                !isChecked &&
                intArithLeftToRight.TryGetValue(op, out rightOp) &&
                prototype.ParameterTypes.All(x => x.IsIntegerType()))
            {
                rightPrototype = ArithmeticIntrinsics.CreatePrototype(
                    rightOp,
                    isChecked,
                    prototype.ResultType,
                    prototype.ParameterTypes);
                return(true);
            }
            else
            {
                rightPrototype = null;
                return(false);
            }
        }
예제 #3
0
 /// <summary>
 /// Creates an arithmetic intrinsic.
 /// </summary>
 /// <param name="operatorName">
 /// The name of the arithmetic operator to apply.
 /// </param>
 /// <param name="resultType">
 /// The type of value produced by the intrinsic.
 /// </param>
 /// <param name="parameterTypes">
 /// The parameter types taken by the intrinsic.
 /// </param>
 /// <param name="arguments">
 /// The argument list to pass to the instruction.
 /// </param>
 /// <returns>
 /// An arithmetic intrinsic.
 /// </returns>
 public static Instruction CreateArithmeticIntrinsic(
     string operatorName,
     IType resultType,
     IReadOnlyList <IType> parameterTypes,
     IReadOnlyList <ValueTag> arguments)
 {
     return(ArithmeticIntrinsics.CreatePrototype(
                operatorName,
                resultType,
                parameterTypes).Instantiate(arguments));
 }
예제 #4
0
        private static bool IsAssociative(IntrinsicPrototype prototype)
        {
            string op;
            bool   isChecked;

            if (ArithmeticIntrinsics.TryParseArithmeticIntrinsicName(prototype.Name, out op, out isChecked) &&
                !isChecked &&
                assocIntArith.Contains(op))
            {
                return(prototype.ParameterTypes.All(x => x.IsIntegerType()));
            }
            else
            {
                return(false);
            }
        }
예제 #5
0
        /// <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)
            {
                string   arithOp;
                Constant result;

                if (ArithmeticIntrinsics.TryParseArithmeticIntrinsicName(
                        ((IntrinsicPrototype)prototype).Name,
                        out arithOp) &&
                    ArithmeticIntrinsics.TryEvaluate(
                        arithOp,
                        prototype.ResultType,
                        arguments,
                        out result))
                {
                    return(result);
                }
            }
            return(null);
        }
예제 #6
0
 /// <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);
 }
예제 #7
0
        static RuleBasedPrototypeExceptionSpecs()
        {
            Default = new RuleBasedPrototypeExceptionSpecs();

            // Instruction prototypes that never throw.
            Default.Register <AllocaArrayPrototype>(ExceptionSpecification.NoThrow);
            Default.Register <AllocaPrototype>(ExceptionSpecification.NoThrow);
            Default.Register <BoxPrototype>(ExceptionSpecification.NoThrow);
            Default.Register <ConstantPrototype>(ExceptionSpecification.NoThrow);
            Default.Register <CopyPrototype>(ExceptionSpecification.NoThrow);
            Default.Register <DynamicCastPrototype>(ExceptionSpecification.NoThrow);
            Default.Register <GetStaticFieldPointerPrototype>(ExceptionSpecification.NoThrow);
            Default.Register <LoadPrototype>(ExceptionSpecification.NoThrow);
            Default.Register <ReinterpretCastPrototype>(ExceptionSpecification.NoThrow);
            Default.Register <StorePrototype>(ExceptionSpecification.NoThrow);

            // Instruction prototypes that may throw because of implicit null checks.
            Default.Register <GetFieldPointerPrototype>(
                new NullCheckExceptionSpecification(0, ExceptionSpecification.ThrowAny));
            Default.Register <NewDelegatePrototype>(
                proto => proto.Lookup == MethodLookup.Virtual
                    ? new NullCheckExceptionSpecification(0, ExceptionSpecification.ThrowAny)
                    : ExceptionSpecification.NoThrow);
            Default.Register <UnboxPrototype>(
                new NullCheckExceptionSpecification(0, ExceptionSpecification.ThrowAny));

            // Call-like instruction prototypes.
            Default.Register <CallPrototype>(
                proto => proto.Lookup == MethodLookup.Static
                    ? proto.Callee.GetExceptionSpecification()
                    : ExceptionSpecification.ThrowAny);
            Default.Register <NewObjectPrototype>(
                proto => proto.Constructor.GetExceptionSpecification());
            Default.Register <IndirectCallPrototype>(ExceptionSpecification.ThrowAny);

            // Unchecked arithmetic intrinsics never throw.
            foreach (var name in ArithmeticIntrinsics.Operators.All)
            {
                Default.Register(
                    ArithmeticIntrinsics.GetArithmeticIntrinsicName(name, false),
                    ExceptionSpecification.NoThrow);
            }

            // Array intrinsics are a little more complicated.
            // TODO: model bounds checks somehow.
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.GetElementPointer),
                ExceptionSpecification.ThrowAny);
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.LoadElement),
                ExceptionSpecification.ThrowAny);
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.StoreElement),
                ExceptionSpecification.ThrowAny);
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.GetLength),
                new NullCheckExceptionSpecification(0, ExceptionSpecification.ThrowAny));
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.NewArray),
                ExceptionSpecification.NoThrow);

            // Exception intrinsics.
            Default.Register(
                ExceptionIntrinsics.Namespace.GetIntrinsicName(ExceptionIntrinsics.Operators.Capture),
                ExceptionSpecification.NoThrow);
            Default.Register(
                ExceptionIntrinsics.Namespace.GetIntrinsicName(ExceptionIntrinsics.Operators.GetCapturedException),
                ExceptionSpecification.NoThrow);
            Default.Register(
                ExceptionIntrinsics.Namespace.GetIntrinsicName(ExceptionIntrinsics.Operators.Rethrow),
                ExceptionSpecification.ThrowAny);
            Default.Register(
                ExceptionIntrinsics.Namespace.GetIntrinsicName(ExceptionIntrinsics.Operators.Throw),
                proto => ExceptionSpecification.Exact.Create(proto.ParameterTypes[0]));

            // Object intrinsics.
            // TODO: model exception thrown by type check.
            Default.Register(
                ObjectIntrinsics.Namespace.GetIntrinsicName(ObjectIntrinsics.Operators.UnboxAny),
                ExceptionSpecification.ThrowAny);

            // Memory intrinsics.
            Default.Register(
                MemoryIntrinsics.Namespace.GetIntrinsicName(MemoryIntrinsics.Operators.AllocaPinned),
                ExceptionSpecification.NoThrow);
        }
예제 #8
0
        static RuleBasedPrototypeMemorySpecs()
        {
            Default = new RuleBasedPrototypeMemorySpecs();

            Default.Register <AllocaArrayPrototype>(MemorySpecification.Nothing);
            Default.Register <AllocaPrototype>(MemorySpecification.Nothing);
            Default.Register <BoxPrototype>(MemorySpecification.Nothing);
            Default.Register <ConstantPrototype>(MemorySpecification.Nothing);
            Default.Register <CopyPrototype>(MemorySpecification.Nothing);
            Default.Register <DynamicCastPrototype>(MemorySpecification.Nothing);
            Default.Register <GetStaticFieldPointerPrototype>(MemorySpecification.Nothing);
            Default.Register <ReinterpretCastPrototype>(MemorySpecification.Nothing);
            Default.Register <GetFieldPointerPrototype>(MemorySpecification.Nothing);
            Default.Register <NewDelegatePrototype>(MemorySpecification.Nothing);
            Default.Register <UnboxPrototype>(MemorySpecification.Nothing);

            // Mark volatile loads and stores as unknown to ensure that they are never reordered
            // with regard to other memory operations.
            // TODO: is this really how we should represent volatility?
            Default.Register <LoadPrototype>(proto =>
                                             proto.IsVolatile ? MemorySpecification.Unknown : MemorySpecification.ArgumentRead.Create(0));
            Default.Register <StorePrototype>(proto =>
                                              proto.IsVolatile ? MemorySpecification.Unknown : MemorySpecification.ArgumentWrite.Create(0));

            // Call-like instruction prototypes.
            Default.Register <CallPrototype>(
                proto => proto.Lookup == MethodLookup.Static
                    ? proto.Callee.GetMemorySpecification()
                    : MemorySpecification.Unknown);
            Default.Register <NewObjectPrototype>(
                proto => proto.Constructor.GetMemorySpecification());
            Default.Register <IndirectCallPrototype>(MemorySpecification.Unknown);

            // Arithmetic intrinsics never read or write.
            foreach (var name in ArithmeticIntrinsics.Operators.All)
            {
                Default.Register(
                    ArithmeticIntrinsics.GetArithmeticIntrinsicName(name, false),
                    MemorySpecification.Nothing);
                Default.Register(
                    ArithmeticIntrinsics.GetArithmeticIntrinsicName(name, true),
                    MemorySpecification.Nothing);
            }

            // Array intrinsics.
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.GetElementPointer),
                MemorySpecification.Nothing);
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.LoadElement),
                MemorySpecification.UnknownRead);
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.StoreElement),
                MemorySpecification.UnknownWrite);
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.GetLength),
                MemorySpecification.Nothing);
            Default.Register(
                ArrayIntrinsics.Namespace.GetIntrinsicName(ArrayIntrinsics.Operators.NewArray),
                MemorySpecification.Nothing);

            // Exception intrinsics.
            Default.Register(
                ExceptionIntrinsics.Namespace.GetIntrinsicName(ExceptionIntrinsics.Operators.GetCapturedException),
                MemorySpecification.UnknownRead);

            // Object intrinsics.
            Default.Register(
                ObjectIntrinsics.Namespace.GetIntrinsicName(ObjectIntrinsics.Operators.UnboxAny),
                MemorySpecification.UnknownRead);

            // Memory intrinsics.
            Default.Register(
                MemoryIntrinsics.Namespace.GetIntrinsicName(MemoryIntrinsics.Operators.AllocaPinned),
                MemorySpecification.Nothing);
        }
예제 #9
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);
        }