示例#1
0
        /// <summary>
        /// Gauges a particular method body's inline gain.
        /// </summary>
        /// <param name="body">A method body that might be inlined.</param>
        /// <param name="arguments">
        /// The list of arguments to feed to <paramref name="body"/>.
        /// </param>
        /// <param name="caller">
        /// The control-flow graph of the caller, which defines the arguments.
        /// </param>
        /// <returns>
        /// A number that represents how much new information we expect to gain from inlining.
        /// </returns>
        protected virtual int GetInlineGain(
            MethodBody body,
            IReadOnlyList <ValueTag> arguments,
            FlowGraph caller)
        {
            // Inline method bodies containing up to ten instructions/blocks.
            // TODO: be smarter about arguments:
            //   * Inlining a method that takes a more derived type as argument
            //     may result in a direct call getting turned into an indirect
            //     call.
            int gain = 10;

            foreach (var arg in arguments)
            {
                NamedInstruction argInstruction;
                if (caller.TryGetInstruction(arg, out argInstruction))
                {
                    if (argInstruction.Prototype is AllocaPrototype)
                    {
                        // Inlining a method that takes an `alloca` argument may result
                        // in a level of memory indirection getting stripped away.
                        gain += 4;
                        continue;
                    }
                }

                var type = caller.GetValueType(arg);

                // Inlining means that we don't have to pass around big arguments.
                // Encourage inlining methods that take hefty arguments.
                gain += EstimateTypeSize(type) / 4;
            }
            gain += EstimateTypeSize(body.ReturnParameter.Type) / 4;
            return(gain);
        }
示例#2
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var builder = graph.ToBuilder();

            foreach (var insn in builder.NamedInstructions)
            {
                ValueTag           array;
                ClrFieldDefinition pseudoField;
                if (IsArrayInit(insn, out array, out pseudoField))
                {
                    array = ElideReinterpretCasts(array, builder.ToImmutable());
                    var   arrayType = TypeHelpers.UnboxIfPossible(graph.GetValueType(array));
                    IType elementType;
                    IReadOnlyList <Constant> data;
                    int rank;
                    if (ClrArrayType.TryGetArrayElementType(arrayType, out elementType) &&
                        ClrArrayType.TryGetArrayRank(arrayType, out rank) &&
                        rank == 1 &&
                        TryDecodePseudoFieldData(pseudoField, elementType, out data))
                    {
                        // Generate instructions to fill the array.
                        FillWith(array, data, elementType, insn);
                        // Neuter the old array init function.
                        insn.Instruction = Instruction.CreateConstant(DefaultConstant.Instance, insn.ResultType);
                    }
                }
            }
            return(builder.ToImmutable());
        }
示例#3
0
        private static bool TryExtractConstantAndValue(
            ValueTag leftHandSide,
            ValueTag rightHandSide,
            FlowGraph graph,
            out Constant constant,
            out ValueTag value)
        {
            var lhsInstruction = SimplifyInstruction(
                Instruction.CreateCopy(
                    graph.GetValueType(leftHandSide),
                    leftHandSide),
                graph);

            if (lhsInstruction.Prototype is ConstantPrototype)
            {
                constant = ((ConstantPrototype)lhsInstruction.Prototype).Value;
                value    = rightHandSide;
                return(true);
            }

            var rhsInstruction = SimplifyInstruction(
                Instruction.CreateCopy(
                    graph.GetValueType(rightHandSide),
                    rightHandSide),
                graph);

            if (rhsInstruction.Prototype is ConstantPrototype)
            {
                constant = ((ConstantPrototype)rhsInstruction.Prototype).Value;
                value    = leftHandSide;
                return(true);
            }
            else
            {
                constant = null;
                value    = null;
                return(false);
            }
        }
示例#4
0
        private static IType GetActualType(ValueTag value, FlowGraph graph)
        {
            NamedInstruction insn;

            if (graph.TryGetInstruction(value, out insn))
            {
                var proto = insn.Prototype;
                if (proto is ReinterpretCastPrototype || proto is CopyPrototype)
                {
                    return(GetActualType(insn.Arguments[0], graph));
                }
            }
            return(graph.GetValueType(value));
        }
示例#5
0
        /// <inheritdoc/>
        protected override bool TryGetPreallocatedRegister(
            ValueTag value,
            FlowGraph graph,
            out CilCodegenRegister register)
        {
            ParameterDefinition parameter;

            if (paramRegisters.TryGetValue(value, out parameter))
            {
                register = new CilCodegenRegister(parameter, graph.GetValueType(value));
                return(true);
            }
            else
            {
                register = default(CilCodegenRegister);
                return(false);
            }
        }
示例#6
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var builder = graph.ToBuilder();

            foreach (var instruction in builder.Instructions)
            {
                var proto = instruction.Prototype;
                if (proto is IndirectCallPrototype)
                {
                    // Flame IR has dedicated instructions for delegate calls,
                    // but in CIL they are implemented by a virtual call to
                    // a magic 'Invoke' method.
                    var     callProto    = (IndirectCallPrototype)proto;
                    var     calleeValue  = callProto.GetCallee(instruction.Instruction);
                    var     delegateType = TypeHelpers.UnboxIfPossible(graph.GetValueType(calleeValue));
                    IMethod invokeMethod;
                    if (TypeHelpers.TryGetDelegateInvokeMethod(delegateType, out invokeMethod))
                    {
                        instruction.Instruction = Instruction.CreateCall(
                            invokeMethod,
                            MethodLookup.Virtual,
                            calleeValue,
                            callProto.GetArgumentList(instruction.Instruction).ToArray());
                    }
                }
                else if (proto is NewDelegatePrototype && instruction is NamedInstructionBuilder)
                {
                    // TODO: also lower NewDelegatePrototype for anonymous instructions!

                    // CIL delegates are created by first loading a function pointer
                    // onto the stack (using either `ldftn` or `ldvirtftn`) and then
                    // constructing the actual delegate using a `newobj` opcode.

                    var newDelegateProto = (NewDelegatePrototype)proto;
                    var delegateType     = TypeHelpers.UnboxIfPossible(newDelegateProto.ResultType);

                    IMethod invokeMethod;
                    if (!TypeHelpers.TryGetDelegateInvokeMethod(delegateType, out invokeMethod))
                    {
                        continue;
                    }

                    var  constructor = delegateType.Methods.Single(method => method.IsConstructor);
                    bool isVirtual   = newDelegateProto.Lookup == MethodLookup.Virtual;

                    // First create an instruction that loads the function pointer.
                    var namedInstruction = (NamedInstructionBuilder)instruction;
                    var functionPointer  = namedInstruction.InsertBefore(
                        Instruction.CreateNewDelegate(
                            constructor.Parameters[1].Type,
                            newDelegateProto.Callee,
                            isVirtual
                                ? newDelegateProto.GetThisArgument(instruction.Instruction)
                                : null,
                            newDelegateProto.Lookup),
                        namedInstruction.Tag.Name + "_fptr");

                    // CLR delegate constructors always take two parameters: a function
                    // pointer and a 'this' argument (of type 'Object *box').
                    // The latter can be somewhat tricky to get right:
                    //
                    //   * if the delegate has a 'this' argument then that 'this' argument
                    //     should be reinterpreted as an instance of 'Object *box'.
                    //
                    //   * if the delegate does not have a 'this' argument, then we pass
                    //     a `null` constant as 'this' argument.

                    ValueTag thisArgument;
                    if (newDelegateProto.HasThisArgument)
                    {
                        thisArgument = newDelegateProto.GetThisArgument(instruction.Instruction);

                        var thisType         = builder.GetValueType(thisArgument);
                        var expectedThisType = constructor.Parameters[0].Type;

                        if (thisType != expectedThisType)
                        {
                            thisArgument = functionPointer.InsertBefore(
                                Instruction.CreateReinterpretCast(
                                    (PointerType)expectedThisType,
                                    thisArgument));
                        }
                    }
                    else
                    {
                        thisArgument = functionPointer.InsertBefore(
                            Instruction.CreateConstant(
                                NullConstant.Instance,
                                constructor.Parameters[0].Type));
                    }

                    // And then create the actual delegate. To do so we must use the
                    // delegate type's constructor. This constructor will always take
                    // two parameters: a bound object (of type System.Object) and a
                    // function pointer (of type natural int).
                    instruction.Instruction = Instruction.CreateNewObject(
                        constructor,
                        new[]
                    {
                        thisArgument,
                        functionPointer
                    });
                }
            }
            return(builder.ToImmutable());
        }
        /// <inheritdoc/>
        public RegisterAllocation <TRegister> Analyze(
            FlowGraph graph)
        {
            // Run the related values and interference graph analyses.
            var related      = graph.GetAnalysisResult <RelatedValues>();
            var interference = graph.GetAnalysisResult <InterferenceGraph>();

            // Create a mapping of values to registers. This will become our
            // return value.
            var allocation = new Dictionary <ValueTag, TRegister>();

            // Create a mapping of registers to the set of all values they
            // interfere with due to the registers getting allocated to values.
            var registerInterference = new Dictionary <TRegister, HashSet <ValueTag> >();

            // Before we do any real register allocation, we should set up
            // preallocated registers. The reason for doing this now instead
            // of later on in the allocation loop is that preallocated registers
            // are "free:" they don't incur register allocations. At the same time,
            // preallocated registers can be recycled, so we'll have more
            // registers to recycle (and hopefully create fewer new ones) if we
            // handle preallocated registers first.
            foreach (var value in graph.ValueTags)
            {
                TRegister assignedRegister;
                if (TryGetPreallocatedRegister(value, graph, out assignedRegister))
                {
                    // If we have a preallocated register, then we should just accept it.
                    allocation[value] = assignedRegister;
                    registerInterference[assignedRegister] = new HashSet <ValueTag>(
                        interference.GetInterferingValues(value));
                }
            }

            // Iterate over all values in the graph.
            foreach (var value in graph.ValueTags)
            {
                if (!RequiresRegister(value, graph) || allocation.ContainsKey(value))
                {
                    // The value may not need a register or may already have one.
                    // If so, then we shouldn't allocate one.
                    continue;
                }

                // Compose a set of registers we might be able to recycle.
                // Specifically, we'll look for all registers that are not
                // allocated to values that interfere with the current value.
                var recyclable = new HashSet <TRegister>();
                foreach (var pair in registerInterference)
                {
                    if (!pair.Value.Contains(value))
                    {
                        // If the value is not in the interference set of
                        // the register, then we're good to go.
                        recyclable.Add(pair.Key);
                    }
                }

                // We would like to recycle a register that has been
                // allocated to a related but non-interfering value.
                // To do so, we'll build a set of candidate registers.
                var relatedRegisters = new HashSet <TRegister>();
                foreach (var relatedValue in related.GetRelatedValues(value))
                {
                    TRegister reg;
                    if (allocation.TryGetValue(relatedValue, out reg) &&
                        recyclable.Contains(reg))
                    {
                        relatedRegisters.Add(reg);
                    }
                }

                // If at all possible, try to recycle a related register. If that
                // doesn't work out, try to recycle a non-related register. If
                // that fails as well, then we'll create a new register.
                var       valueType = graph.GetValueType(value);
                TRegister assignedRegister;
                if (!TryRecycleRegister(valueType, relatedRegisters, out assignedRegister) &&
                    !TryRecycleRegister(valueType, recyclable, out assignedRegister))
                {
                    assignedRegister = CreateRegister(valueType);
                    registerInterference[assignedRegister] = new HashSet <ValueTag>();
                }

                // Allocate the register we recycled or created to the value.
                allocation[value] = assignedRegister;
                registerInterference[assignedRegister].UnionWith(
                    interference.GetInterferingValues(value));
            }

            return(new RegisterAllocation <TRegister>(allocation));
        }
示例#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);
        }