/// <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());
        }
Exemple #2
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var           builder    = graph.ToBuilder();
            BasicBlockTag entryThunk = null;

            foreach (var block in builder.BasicBlocks)
            {
                var flow = block.Flow;
                foreach (var branch in block.Flow.Branches)
                {
                    if (branch.Target == graph.EntryPointTag)
                    {
                        if (entryThunk == null)
                        {
                            entryThunk = CreateEntryPointThunk(builder);
                            break;
                        }
                    }
                }
                if (entryThunk != null)
                {
                    break;
                }
            }
            if (entryThunk != null)
            {
                builder.EntryPointTag = entryThunk;
            }
            return(builder.ToImmutable());
        }
Exemple #3
0
        /// <summary>
        /// Propagates copies in a flow graph.
        /// </summary>
        /// <param name="graph">
        /// The graph to transform.
        /// </param>
        /// <returns>
        /// A transformed graph.
        /// </returns>
        public override FlowGraph Apply(FlowGraph graph)
        {
            // We'll first create a mapping of values to the
            // values they copy. Then we'll use that to
            // rewrite uses.
            var copyMap = FindCopies(graph);

            var graphBuilder = graph.ToBuilder();

            // Undefined values are encoded as `null` values.
            // We will first materialize them as `default`
            // constants and then replace uses.
            var entryPoint = graphBuilder.GetBasicBlock(graphBuilder.EntryPointTag);
            var materializedReplacements = new Dictionary <ValueTag, ValueTag>();

            foreach (var pair in copyMap)
            {
                if (pair.Value == null)
                {
                    materializedReplacements[pair.Key] = entryPoint.InsertInstruction(
                        0,
                        Instruction.CreateDefaultConstant(graphBuilder.GetValueType(pair.Key)),
                        pair.Key.Name);
                }
                else
                {
                    materializedReplacements[pair.Key] = pair.Value;
                }
            }

            graphBuilder.ReplaceUses(materializedReplacements);
            return(graphBuilder.ToImmutable());
        }
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var builder = graph.ToBuilder();

            foreach (var block in builder.BasicBlocks)
            {
                var instruction = block.NamedInstructions.FirstOrDefault();
                while (instruction != null)
                {
                    ReassociateNonAssociative(instruction);
                    instruction = instruction.NextInstructionOrNull;
                }
            }

            foreach (var block in builder.BasicBlocks)
            {
                var instruction = block.NamedInstructions.LastOrDefault();
                while (instruction != null)
                {
                    ReassociateReduction(instruction);
                    instruction = instruction.PreviousInstructionOrNull;
                }
            }

            return(builder.ToImmutable());
        }
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var ordering = graph.GetAnalysisResult <InstructionOrdering>();

            var builder = graph.ToBuilder();

            foreach (var block in builder.BasicBlocks)
            {
                // Create a linked list that contains all instructions.
                var schedule = new LinkedList <ValueTag>(block.InstructionTags);

                // Plus a dummy node for the block's flow.
                schedule.AddLast((ValueTag)null);

                // Reorder branch arguments.
                foreach (var branch in block.Flow.Branches.Reverse())
                {
                    ReorderArguments(
                        branch.Arguments
                        .Select(arg => arg.ValueOrNull)
                        .Where(arg => arg != null),
                        block,
                        schedule.Last,
                        ordering,
                        builder.ImmutableGraph);
                }

                // Reorder anonymous instruction arguments.
                foreach (var instruction in block.Flow.Instructions.Reverse())
                {
                    ReorderArguments(instruction, block, schedule.Last, ordering, builder.ImmutableGraph);
                }

                // Reorder named instruction arguments in reverse.
                var finger = schedule.Last.Previous;
                while (finger != null)
                {
                    var instruction = graph.GetInstruction(finger.Value);
                    ReorderArguments(instruction.Instruction, block, finger, ordering, builder.ImmutableGraph);
                    finger = finger.Previous;
                }

                // Materialize the new schedule.
                int index = 0;
                foreach (var value in schedule)
                {
                    if (value == null)
                    {
                        continue;
                    }

                    var instruction = builder.GetInstruction(value);
                    instruction.MoveTo(index, block);
                    index++;
                }
            }
            return(builder.ToImmutable());
        }
        /// <summary>
        /// Simplifies switches in a particular flow graph.
        /// </summary>
        /// <param name="graph">The graph to transform.</param>
        /// <returns>A transformed graph.</returns>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var graphBuilder = graph.ToBuilder();

            foreach (var block in graphBuilder.BasicBlocks)
            {
                TrySimplifySwitchFlow(block);
            }
            return(graphBuilder.ToImmutable());
        }
Exemple #7
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var builder = graph.ToBuilder();

            foreach (var instruction in builder.Instructions)
            {
                TrySimplify(instruction);
            }
            return(builder.ToImmutable());
        }
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            // Figure out which blocks contain nothing but a return.
            var candidatesToValues = new Dictionary <BasicBlockTag, ValueTag>();

            foreach (var block in graph.BasicBlocks)
            {
                if (block.InstructionTags.Count == 0)
                {
                    var flow = block.Flow as ReturnFlow;
                    if (flow != null)
                    {
                        var retInsn  = flow.ReturnValue;
                        var retProto = retInsn.Prototype as CopyPrototype;
                        if (retProto != null)
                        {
                            candidatesToValues[block] = retProto.GetCopiedValue(retInsn);
                        }
                    }
                }
            }

            if (candidatesToValues.Count == 0)
            {
                return(graph);
            }
            else
            {
                // Rewrite blocks if we found any candidates.
                var builder = graph.ToBuilder();
                foreach (var block in builder.BasicBlocks)
                {
                    var      flow = block.Flow as JumpFlow;
                    ValueTag retValue;
                    if (flow != null &&
                        candidatesToValues.TryGetValue(flow.Branch.Target, out retValue))
                    {
                        foreach (var pair in flow.Branch.ZipArgumentsWithParameters(builder.ToImmutable()))
                        {
                            if (pair.Key == retValue)
                            {
                                retValue = pair.Value.ValueOrNull;
                            }
                        }
                        block.Flow = new ReturnFlow(
                            Instruction.CreateCopy(
                                builder.GetValueType(retValue),
                                retValue));
                    }
                }
                return(builder.ToImmutable());
            }
        }
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            // Compute the value numbering for the graph.
            var numbering = graph.GetAnalysisResult <ValueNumbering>();

            // Partition the set of all instructions into equivalence classes.
            var equivValues = new Dictionary <Instruction, HashSet <ValueTag> >(
                new ValueNumberingInstructionComparer(numbering));

            foreach (var insn in graph.NamedInstructions)
            {
                HashSet <ValueTag> valueSet;
                if (!equivValues.TryGetValue(insn.Instruction, out valueSet))
                {
                    equivValues[insn.Instruction] = valueSet = new HashSet <ValueTag>();
                }
                valueSet.Add(insn);
            }

            // Compute the dominator tree for the graph.
            var domTree = graph.GetAnalysisResult <DominatorTree>();

            // Replace instructions with copies to your heart's content.
            var builder = graph.ToBuilder();

            foreach (var insn in builder.Instructions)
            {
                // An instruction can be replaced with another instruction
                // if it is equivalent to that instruction and it is strictly
                // dominated by the other instruction.
                HashSet <ValueTag> valueSet;
                if (!equivValues.TryGetValue(insn.Instruction, out valueSet))
                {
                    continue;
                }

                foreach (var equivValue in valueSet)
                {
                    if (domTree.IsStrictlyDominatedBy(insn, equivValue))
                    {
                        insn.Instruction = Instruction.CreateCopy(insn.Instruction.ResultType, equivValue);
                        break;
                    }
                }
            }
            return(builder.ToImmutable());
        }
        /// <summary>
        /// Removes dead blocks from a particular graph.
        /// </summary>
        /// <param name="graph">The graph to rewrite.</param>
        /// <returns>A rewritten flow graph.</returns>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var reachability = graph.GetAnalysisResult<BlockReachability>();

            var deadBlocks = new HashSet<BasicBlockTag>(graph.BasicBlockTags);
            deadBlocks.Remove(graph.EntryPointTag);
            deadBlocks.ExceptWith(
                reachability.GetStrictlyReachableBlocks(
                    graph.EntryPointTag));

            var graphBuilder = graph.ToBuilder();
            foreach (var tag in deadBlocks)
            {
                graphBuilder.RemoveBasicBlock(tag);
            }
            return graphBuilder.ToImmutable();
        }
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            // This transform does the following things:
            //   1. It figures out which values need materializing.
            //   2. It delays the materialization of aggregates that cannot
            //      be fully replaced by scalars.
            //   3. It runs the regular scalar replacement pass.

            var builder = graph.ToBuilder();

            var analysisResults = new MaterializationAnalysis().Analyze(graph);

            DelayMaterialization(builder, analysisResults.BlockResults);
            builder.Transform(new ScalarReplacement(CanReplaceByScalars));

            return(builder.ToImmutable());
        }
Exemple #12
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 BoxPrototype)
                {
                    var boxProto = (BoxPrototype)proto;
                    if (boxProto.ElementType.IsReferenceType())
                    {
                        LowerReferenceTypeBox(instruction, boxProto.ElementType);
                    }
                }
            }
            return(builder.ToImmutable());
        }
Exemple #13
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var builder  = graph.ToBuilder();
            var ordering = graph.GetAnalysisResult <InstructionOrdering>();

            foreach (var block in builder.BasicBlocks)
            {
                if (block.Flow is ReturnFlow)
                {
                    var retFlow = (ReturnFlow)block.Flow;
                    TryReplaceWithBranchToEntry(
                        block,
                        retFlow.ReturnValue,
                        builder,
                        ordering);
                }
            }
            return(builder.ToImmutable());
        }
        // This transform is based on the algorithm described by M. Braun et al
        // in Simple and Efficient Construction of Static Single Assignment Form
        // (https://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf).

        /// <summary>
        /// Applies the alloca to register transformation to a flow graph.
        /// </summary>
        /// <param name="graph">
        /// A flow graph to transform.
        /// </param>
        /// <returns>
        /// A transformed flow graph.
        /// </returns>
        public override FlowGraph Apply(FlowGraph graph)
        {
            // Our first order of business is to identify
            // all `alloca` instructions that we *cannot*
            // replace with SSA magic.
            //
            // To keep things simple, we'll just blacklist
            // any `alloca` that is used by instructions other
            // than `load` and `store`.
            var eligibleAllocas = GetAllocasWithoutIdentity(graph);

            // We now have a set of all blacklisted `alloca` instructions.
            // All we need to do now is actually apply the SSA construction
            // algorithm.
            var graphBuilder = graph.ToBuilder();
            var algo         = new SSAConstructionAlgorithm(graphBuilder, eligibleAllocas);

            algo.Run();
            return(graphBuilder.ToImmutable());
        }
Exemple #15
0
        /// <summary>
        /// Lowers general switch flow in a particular flow graph.
        /// </summary>
        /// <param name="graph">The flow graph to rewrite.</param>
        /// <returns>A rewritten flow graph.</returns>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var graphBuilder = graph.ToBuilder();

            foreach (var block in graphBuilder.BasicBlocks)
            {
                var flow = block.Flow;
                if (flow is SwitchFlow)
                {
                    var switchFlow = (SwitchFlow)flow;
                    if (switchFlow.IsIfElseFlow)
                    {
                        // If-else flow is its own optimal lowering.
                        continue;
                    }
                    var value = block.AppendInstruction(switchFlow.SwitchValue, "switchval");
                    block.Flow = GetSwitchFlowLowering(switchFlow)
                                 .Emit(graphBuilder, value);
                }
            }
            return(graphBuilder.ToImmutable());
        }
        /// <summary>
        /// Applies the jump threading optimization to a flow graph.
        /// </summary>
        /// <param name="graph">The flow graph to rewrite.</param>
        /// <returns>An optimized flow graph.</returns>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var graphBuilder = graph.ToBuilder();

            // Ensure that the graph is in register forwarding form.
            // Jump threading will work even if it isn't, but register
            // forwarding form will allow jump threading to make more
            // aggressive simplifications.
            graphBuilder.Transform(ForwardRegisters.Instance);

            // Suggest exception spec analyses if the graph doesn't have any yet.
            if (!graphBuilder.HasAnalysisFor <PrototypeExceptionSpecs>())
            {
                graphBuilder.AddAnalysis(
                    new ConstantAnalysis <PrototypeExceptionSpecs>(
                        RuleBasedPrototypeExceptionSpecs.Default));
            }
            if (!graphBuilder.HasAnalysisFor <InstructionExceptionSpecs>())
            {
                graphBuilder.AddAnalysis(
                    new ConstantAnalysis <InstructionExceptionSpecs>(
                        new TrivialInstructionExceptionSpecs(
                            graphBuilder.GetAnalysisResult <PrototypeExceptionSpecs>())));
            }

            var finished = new HashSet <BasicBlockTag>();

            foreach (var block in graphBuilder.BasicBlocks)
            {
                ThreadJumps(block, finished);
            }

            // Move out of register forwarding form by applying copy propagation
            // and dead code elimination.
            graphBuilder.Transform(CopyPropagation.Instance, DeadValueElimination.Instance, DeadBlockElimination.Instance);

            return(graphBuilder.ToImmutable());
        }
Exemple #17
0
        /// <summary>
        /// Applies the jump threading optimization to a flow graph.
        /// </summary>
        /// <param name="graph">The flow graph to rewrite.</param>
        /// <returns>An optimized flow graph.</returns>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var graphBuilder = graph.ToBuilder();

            // Ensure that the graph is in register forwarding form.
            // Jump threading will work even if it isn't, but register
            // forwarding form will allow jump threading to make more
            // aggressive simplifications.
            graphBuilder.Transform(ForwardRegisters.Instance);

            var finished = new HashSet <BasicBlockTag>();

            foreach (var block in graphBuilder.BasicBlocks)
            {
                ThreadJumps(block, finished);
            }

            // Move out of register forwarding form by applying copy propagation
            // and dead code elimination.
            graphBuilder.Transform(CopyPropagation.Instance, DeadValueElimination.Instance, DeadBlockElimination.Instance);

            return(graphBuilder.ToImmutable());
        }
Exemple #18
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var uses = graph.GetAnalysisResult <ValueUses>();

            // Figure out which unbox instructions are eligible.
            // TODO: implement a proper escape analysis.
            var nonescapingUnboxInstructions = new HashSet <ValueTag>();

            foreach (var instruction in graph.NamedInstructions)
            {
                if (instruction.Prototype is UnboxPrototype &&
                    uses.GetFlowUses(instruction).Count == 0 &&
                    !uses.GetInstructionUses(instruction).Any(
                        tag => AllowsEscape(tag, instruction, graph)))
                {
                    nonescapingUnboxInstructions.Add(instruction);
                }
            }

            var builder = graph.ToBuilder();

            // Rewrite box and unbox instructions.
            var unboxReplacements = new Dictionary <ValueTag, ValueTag>();

            foreach (var instruction in builder.NamedInstructions)
            {
                var boxProto = instruction.Prototype as BoxPrototype;
                if (boxProto != null &&
                    uses.GetFlowUses(instruction).Count == 0 &&
                    uses.GetInstructionUses(instruction).IsSubsetOf(
                        nonescapingUnboxInstructions))
                {
                    var boxedValue = boxProto.GetBoxedValue(instruction.Instruction);

                    // Replace the box with an alloca.
                    instruction.Instruction = Instruction.CreateAlloca(boxProto.ElementType);

                    // Initialize the alloca with the boxed value.
                    instruction.InsertAfter(
                        Instruction.CreateStore(
                            boxProto.ElementType,
                            instruction,
                            boxedValue));

                    // Record which unbox instructions wrap to which
                    // alloca instructions.
                    foreach (var use in uses.GetInstructionUses(instruction))
                    {
                        unboxReplacements[use] = instruction;
                    }
                }
            }

            // Replace all unbox instructions that use the box with
            // references to alloca instructions. Delete the unbox
            // instructions themselves.
            builder.ReplaceUses(unboxReplacements);
            builder.RemoveInstructionDefinitions(unboxReplacements.Keys);

            return(builder.ToImmutable());
        }
Exemple #19
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            // Figure out which aggregates can be replaced by scalars.
            var eligible = FindEligibleAllocas(graph);

            var builder = graph.ToBuilder();

            // Create allocas for fields.
            var replacements = new Dictionary <ValueTag, Dictionary <IField, ValueTag> >();

            foreach (var allocaTag in eligible)
            {
                var allocaInstruction = builder.GetInstruction(allocaTag);
                var elementType       = ((PointerType)allocaInstruction.ResultType).ElementType;
                var fieldSlots        = new Dictionary <IField, ValueTag>();
                foreach (var field in elementType.GetAllInstanceFields())
                {
                    fieldSlots[field] = allocaInstruction.InsertAfter(
                        Instruction.CreateAlloca(field.FieldType),
                        allocaTag.Name + ".scalarrepl.field." + field.Name);
                }
                replacements[allocaTag] = fieldSlots;
            }

            // Rewrite instructions.
            foreach (var instruction in builder.NamedInstructions)
            {
                var proto = instruction.Prototype;
                if (proto is GetFieldPointerPrototype)
                {
                    var gfpProto    = (GetFieldPointerPrototype)proto;
                    var basePointer = gfpProto.GetBasePointer(instruction.Instruction);
                    if (eligible.Contains(basePointer))
                    {
                        instruction.Instruction = Instruction.CreateCopy(
                            instruction.Instruction.ResultType,
                            replacements[basePointer][gfpProto.Field]);
                    }
                }
                else if (IsDefaultInitialization(instruction.Instruction, graph))
                {
                    var storeProto = (StorePrototype)proto;
                    var pointer    = storeProto.GetPointer(instruction.Instruction);
                    if (eligible.Contains(pointer))
                    {
                        foreach (var pair in replacements[pointer])
                        {
                            // Initialize each field with
                            //
                            //     c = const(#default, field_type)();
                            //     _ = store(field_pointer, c);
                            //
                            var constant = instruction.InsertAfter(
                                Instruction.CreateDefaultConstant(pair.Key.FieldType));

                            constant.InsertAfter(
                                Instruction.CreateStore(
                                    pair.Key.FieldType,
                                    pair.Value,
                                    constant));
                        }
                        // Replace the store with a copy, in case someone is
                        // using the value it returns.
                        instruction.Instruction = Instruction.CreateCopy(
                            instruction.Instruction.ResultType,
                            storeProto.GetValue(instruction.Instruction));
                    }
                }
                else if (proto is StorePrototype)
                {
                    TryRewriteStore(instruction, replacements);
                }
            }

            var uses     = builder.GetAnalysisResult <ValueUses>();
            var killList = new HashSet <ValueTag>(eligible);

            // In a second pass, also rewrite load instructions. It is crucial
            // that they are handled *after* stores are handled, because the
            // lowering for stores is a whole lot better if we don't lower the
            // load to something convoluted first.
            foreach (var instruction in builder.NamedInstructions)
            {
                var proto = instruction.Prototype;
                if (proto is LoadPrototype)
                {
                    if (uses.GetUseCount(instruction) == 0)
                    {
                        // Delete dead load. Loads may die during the loop above; we
                        // don't want to create lots of garbage code.
                        killList.Add(instruction);
                        continue;
                    }

                    var loadProto = (LoadPrototype)proto;
                    var pointer   = loadProto.GetPointer(instruction.Instruction);
                    if (eligible.Contains(pointer))
                    {
                        // Looks like we're going to have to materialize a scalarrepl'ed
                        // value. Create a temporary, fill it, and load it.
                        var temporary = instruction.Graph.EntryPoint.InsertInstruction(
                            0,
                            Instruction.CreateAlloca(loadProto.ResultType));

                        var insertionPoint = instruction;
                        foreach (var pair in replacements[pointer].Reverse())
                        {
                            // Copy each field as follows:
                            //
                            //     val = load(field_type)(field_replacement);
                            //     field_ptr = get_field_pointer(field)(temp);
                            //     _ = store(field_ptr, val);
                            //

                            var value = insertionPoint.InsertBefore(
                                Instruction.CreateLoad(pair.Key.FieldType, pair.Value));

                            var fieldPointer = value.InsertAfter(
                                Instruction.CreateGetFieldPointer(pair.Key, temporary));

                            insertionPoint = fieldPointer.InsertAfter(
                                Instruction.CreateStore(pair.Key.FieldType, fieldPointer, value));
                        }

                        instruction.Instruction = loadProto.Instantiate(temporary);
                    }
                }
            }

            // Delete the replaced allocas.
            builder.RemoveInstructionDefinitions(killList);

            return(builder.ToImmutable());
        }
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var builder = graph.ToBuilder();

            // Analyze all local value definitions and find imported values.
            var definitions = new Dictionary <BasicBlockTag, Dictionary <ValueTag, ValueTag> >();
            var extraArgs   = new Dictionary <BasicBlockTag, List <ValueTag> >();
            var imports     = new Dictionary <BasicBlockTag, HashSet <ValueTag> >();

            foreach (var block in builder.BasicBlocks)
            {
                var localDefs    = new Dictionary <ValueTag, ValueTag>();
                var localImports = new HashSet <ValueTag>();
                foreach (var parameter in block.ParameterTags)
                {
                    localDefs[parameter] = parameter;
                }
                foreach (var instruction in block.NamedInstructions)
                {
                    localDefs[instruction] = instruction;
                    localImports.UnionWith(instruction.Arguments);
                }
                foreach (var instruction in block.Flow.Instructions)
                {
                    localImports.UnionWith(instruction.Arguments);
                }
                foreach (var branch in block.Flow.Branches)
                {
                    foreach (var arg in branch.Arguments)
                    {
                        if (arg.IsValue)
                        {
                            localImports.Add(arg.ValueOrNull);
                        }
                    }
                }
                definitions[block] = localDefs;
                imports[block]     = localImports;
                extraArgs[block]   = new List <ValueTag>();
            }

            var predecessors = graph.GetAnalysisResult <BasicBlockPredecessors>();

            // Now import definitions until we reach a fixpoint.
            var worklist = new HashSet <BasicBlockTag>(builder.BasicBlockTags);

            while (worklist.Count > 0)
            {
                var block = builder.GetBasicBlock(worklist.First());
                worklist.Remove(block);

                var blockDefs = definitions[block];
                var blockArgs = extraArgs[block];

                var blockImports = imports[block];
                blockImports.ExceptWith(blockDefs.Keys);
                imports[block] = new HashSet <ValueTag>();

                foreach (var value in blockImports)
                {
                    var rffName = value.Name + ".rff." + block.Tag.Name;

                    NamedInstruction valueInstruction;
                    if (graph.TryGetInstruction(value, out valueInstruction) &&
                        valueInstruction.Prototype is ConstantPrototype)
                    {
                        // Create duplicate definitions of constants instead of
                        // importing them using phis.
                        blockDefs[value] = block.InsertInstruction(0, valueInstruction.Instruction, rffName);
                        continue;
                    }

                    // Import a definition by introducing a new parameter and recursively
                    // importing it the value in predecessor blocks.
                    blockDefs[value] = block.AppendParameter(
                        builder.GetValueType(value),
                        rffName);
                    blockArgs.Add(value);

                    foreach (var pred in predecessors.GetPredecessorsOf(block))
                    {
                        if (!definitions[pred].ContainsKey(value))
                        {
                            imports[pred].Add(value);
                            worklist.Add(pred);
                        }
                    }
                }
            }

            // We have introduced basic block parameters and know which values are
            // used by blocks. We finish by replacing value uses and appending
            // branch arguments.
            foreach (var block in builder.BasicBlocks)
            {
                var blockDefs = definitions[block];
                foreach (var insn in block.NamedInstructions)
                {
                    insn.Instruction = insn.Instruction.MapArguments(blockDefs);
                }
                block.Flow = block.Flow
                             .MapValues(blockDefs)
                             .MapBranches(branch =>
                {
                    var args = new List <BranchArgument>(branch.Arguments);
                    foreach (var arg in extraArgs[branch.Target])
                    {
                        args.Add(BranchArgument.FromValue(blockDefs[arg]));
                    }
                    return(branch.WithArguments(args));
                });
            }

            return(builder.ToImmutable());
        }
Exemple #21
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            IEnumerable <BasicBlockTag> liveBlocks;

            // Do the fancy analysis.
            var cells = FillCells(graph, out liveBlocks);

            // Get ready to rewrite the flow graph.
            var graphBuilder = graph.ToBuilder();

            // Eliminate switch cases whenever possible.
            SimplifySwitches(graphBuilder, cells);

            // Replace instructions with constants.
            foreach (var selection in graphBuilder.NamedInstructions)
            {
                LatticeCell cell;
                if (cells.TryGetValue(selection, out cell) &&
                    cell.IsConstant)
                {
                    selection.Instruction = Instruction.CreateConstant(
                        cell.Value,
                        selection.Instruction.ResultType);
                }
            }

            // Replace block parameters with constants if possible.
            var phiReplacements = new Dictionary <ValueTag, ValueTag>();
            var entryPoint      = graphBuilder.GetBasicBlock(graphBuilder.EntryPointTag);

            foreach (var block in graphBuilder.BasicBlocks)
            {
                foreach (var param in block.Parameters)
                {
                    LatticeCell cell;
                    if (cells.TryGetValue(param.Tag, out cell) &&
                        cell.IsConstant)
                    {
                        phiReplacements[param.Tag] = entryPoint.InsertInstruction(
                            0,
                            Instruction.CreateConstant(cell.Value, param.Type));
                    }
                }

                var  flowInstructions    = block.Flow.Instructions;
                var  newFlowInstructions = new Instruction[flowInstructions.Count];
                bool anyChanged          = false;
                for (int i = 0; i < newFlowInstructions.Length; i++)
                {
                    var cell = EvaluateInstruction(flowInstructions[i], cells, graph);
                    if (cell.IsConstant)
                    {
                        anyChanged             = true;
                        newFlowInstructions[i] = Instruction.CreateConstant(
                            cell.Value,
                            flowInstructions[i].ResultType);
                    }
                    else
                    {
                        newFlowInstructions[i] = flowInstructions[i];
                    }
                }

                if (anyChanged)
                {
                    block.Flow = block.Flow.WithInstructions(newFlowInstructions);
                }
            }

            graphBuilder.ReplaceUses(phiReplacements);
            graphBuilder.RemoveDefinitions(phiReplacements.Keys);

            // Remove all instructions from dead blocks and mark the blocks
            // themselves as unreachable.
            foreach (var tag in graphBuilder.BasicBlockTags.Except(liveBlocks).ToArray())
            {
                var block = graphBuilder.GetBasicBlock(tag);

                // Turn the block's flow into unreachable flow.
                block.Flow = UnreachableFlow.Instance;

                // Delete the block's instructions.
                graphBuilder.RemoveInstructionDefinitions(block.InstructionTags);
            }

            return(graphBuilder.ToImmutable());
        }
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var builder = graph.ToBuilder();

            foreach (var instruction in builder.NamedInstructions)
            {
                var proto = instruction.Prototype;
                if (proto is CallPrototype)
                {
                    // CIL delegates are called using a (virtual) call to the
                    // delegate's 'Invoke' method. However, in Flame IR we want
                    // to make indirect function calls explicit using a specialized
                    // opcode.
                    var     callProto    = (CallPrototype)proto;
                    var     delegateType = callProto.Callee.ParentType;
                    IMethod invokeMethod;
                    if (TypeHelpers.TryGetDelegateInvokeMethod(delegateType, out invokeMethod) &&
                        invokeMethod == callProto.Callee)
                    {
                        instruction.Instruction = Instruction.CreateIndirectCall(
                            callProto.ResultType,
                            callProto.Callee.Parameters.EagerSelect(p => p.Type),
                            callProto.GetThisArgument(instruction.Instruction),
                            callProto.GetArgumentList(instruction.Instruction).ToArray());
                    }
                }
                else if (proto is NewObjectPrototype && proto.ParameterCount == 2)
                {
                    // Delegates are created by the 'newobj' opcode, but we want
                    // to use Flame IR's delegate-creation instructions, which better
                    // communicate what is going on to the optimizer.

                    var newobjProto     = (NewObjectPrototype)proto;
                    var argList         = newobjProto.GetArgumentList(instruction.Instruction);
                    var boundObject     = argList[0];
                    var functionPointer = argList[1];
                    if (!builder.ContainsInstruction(functionPointer))
                    {
                        continue;
                    }

                    var functionPointerInstruction = builder.GetInstruction(functionPointer);
                    var functionPointerProto       = functionPointerInstruction.Prototype as NewDelegatePrototype;
                    if (functionPointerProto == null)
                    {
                        continue;
                    }

                    var     callee       = functionPointerProto.Callee;
                    var     delegateType = TypeHelpers.UnboxIfPossible(newobjProto.ResultType);
                    IMethod invokeMethod;
                    if (TypeHelpers.TryGetDelegateInvokeMethod(delegateType, out invokeMethod))
                    {
                        if (functionPointerProto.Lookup == MethodLookup.Static)
                        {
                            // We're dealing with a static method lookup. These are actually
                            // slightly tricky because CIL has zany rules that allow for either
                            // a 'this' pointer or a first parameter to be captured by the
                            // delegate itself.
                            bool hasThisParameter = !callee.IsStatic ||
                                                    invokeMethod.Parameters.Count == callee.Parameters.Count - 1;

                            instruction.Instruction = Instruction.CreateNewDelegate(
                                delegateType,
                                callee,
                                hasThisParameter
                                    ? ConvertThisArgument(
                                    boundObject,
                                    callee.IsStatic
                                            ? callee.Parameters[0].Type
                                            : TypeHelpers.BoxIfReferenceType(callee.ParentType),
                                    instruction)
                                    : null,
                                MethodLookup.Static);
                        }
                        else
                        {
                            // We're dealing with a virtual method lookup. These are actually fairly
                            // easy to handle.
                            var thisArg = functionPointerProto.GetThisArgument(functionPointerInstruction.Instruction);
                            if (boundObject == thisArg)
                            {
                                instruction.Instruction = Instruction.CreateNewDelegate(
                                    delegateType,
                                    callee,
                                    ConvertThisArgument(
                                        thisArg,
                                        TypeHelpers.BoxIfReferenceType(callee.ParentType),
                                        instruction),
                                    MethodLookup.Virtual);
                            }
                        }
                    }
                }
            }
            return(builder.ToImmutable());
        }
Exemple #23
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            // Build a mapping of blocks to their predecessors.
            var predecessors = graph.GetAnalysisResult <BasicBlockPredecessors>();

            // Figure out which blocks jump unconditionally to a block
            // with only one predecessor.
            var fusible = new HashSet <BasicBlockTag>();

            foreach (var block in graph.BasicBlockTags)
            {
                BasicBlockTag tail;
                if (TryGetFusibleTail(block, graph, predecessors, out tail))
                {
                    fusible.Add(block);
                }
            }

            // Start editing the graph.
            var builder = graph.ToBuilder();

            // Maintain a dictionary of values that need to be
            // replaced with other values.
            var replacements = new Dictionary <ValueTag, ValueTag>();

            // Maintain a set of blocks to delete.
            var deadBlocks = new HashSet <BasicBlockTag>();

            // Fuse fusible blocks.
            while (fusible.Count > 0)
            {
                // Pop an item from the worklist.
                var tag = fusible.First();

                // Grab the block to edit.
                var block = builder.GetBasicBlock(tag);

                // Grab the successor block to which the block jumps.
                var branch    = ((JumpFlow)block.Flow).Branch;
                var successor = builder.GetBasicBlock(branch.Target);

                // Update the worklist.
                if (!fusible.Remove(successor))
                {
                    fusible.Remove(tag);
                }

                // Replace branch parameters.
                foreach (var pair in branch.ZipArgumentsWithParameters(builder))
                {
                    replacements.Add(pair.Key, pair.Value.ValueOrNull);
                }

                // Move instructions around.
                foreach (var instruction in successor.NamedInstructions)
                {
                    instruction.MoveTo(block);
                }

                // Update the block's flow.
                block.Flow = successor.Flow;
            }

            // Replace instruction uses.
            builder.ReplaceUses(replacements);

            // Delete dead blocks.
            foreach (var tag in deadBlocks)
            {
                builder.RemoveBasicBlock(tag);
            }

            return(builder.ToImmutable());
        }
Exemple #24
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 override FlowGraph Apply(FlowGraph graph)
        {
            // Figure out which aggregates can be replaced by scalars.
            var eligible = FindEligibleAllocas(graph);

            var builder = graph.ToBuilder();

            // Create allocas for fields.
            var replacements = new Dictionary <ValueTag, Dictionary <IField, ValueTag> >();

            foreach (var allocaTag in eligible)
            {
                var allocaInstruction = builder.GetInstruction(allocaTag);
                var allocaProto       = (AllocaPrototype)allocaInstruction.Prototype;
                var fieldSlots        = new Dictionary <IField, ValueTag>();
                foreach (var field in allocaProto.ElementType.Fields)
                {
                    fieldSlots[field] = allocaInstruction.InsertAfter(
                        Instruction.CreateAlloca(field.FieldType),
                        allocaTag.Name + "_field_" + field.Name);
                }
                replacements[allocaTag] = fieldSlots;
            }

            // Rewrite instructions.
            foreach (var instruction in builder.NamedInstructions)
            {
                var proto = instruction.Prototype;
                if (proto is GetFieldPointerPrototype)
                {
                    var gfpProto    = (GetFieldPointerPrototype)proto;
                    var basePointer = gfpProto.GetBasePointer(instruction.Instruction);
                    if (eligible.Contains(basePointer))
                    {
                        instruction.Instruction = Instruction.CreateCopy(
                            instruction.Instruction.ResultType,
                            replacements[basePointer][gfpProto.Field]);
                    }
                }
                else if (IsDefaultInitialization(instruction.ToImmutable()))
                {
                    var storeProto = (StorePrototype)proto;
                    var pointer    = storeProto.GetPointer(instruction.Instruction);
                    if (eligible.Contains(pointer))
                    {
                        foreach (var pair in replacements[pointer])
                        {
                            // Initialize each field with
                            //
                            //     c = const(#default, field_type)();
                            //     _ = store(field_pointer, c);
                            //
                            var constant = instruction.InsertAfter(
                                Instruction.CreateDefaultConstant(pair.Key.FieldType));

                            constant.InsertAfter(
                                Instruction.CreateStore(
                                    pair.Key.FieldType,
                                    pair.Value,
                                    constant));
                        }
                        // Replace the store with a copy, in case someone is
                        // using the value it returns.
                        instruction.Instruction = Instruction.CreateCopy(
                            instruction.Instruction.ResultType,
                            storeProto.GetValue(instruction.Instruction));
                    }
                }
            }

            // Delete the replaced allocas.
            builder.RemoveInstructionDefinitions(eligible);

            return(builder.ToImmutable());
        }
Exemple #26
0
        /// <inheritdoc/>
        public override FlowGraph Apply(FlowGraph graph)
        {
            var memSSA        = graph.GetAnalysisResult <MemorySSA>();
            var aliasing      = graph.GetAnalysisResult <AliasAnalysisResult>();
            var effectfulness = graph.GetAnalysisResult <EffectfulInstructions>();

            var builder = graph.ToBuilder();

            // First try and eliminate loads and stores based on their memory SSA
            // states.
            foreach (var instruction in builder.NamedInstructions)
            {
                var proto = instruction.Prototype;
                if (proto is LoadPrototype)
                {
                    // Loads can be eliminated if we know the memory contents.
                    var      loadProto = (LoadPrototype)proto;
                    var      state     = memSSA.GetMemoryAfter(instruction);
                    ValueTag value;
                    if (state.TryGetValueAt(
                            loadProto.GetPointer(instruction.Instruction),
                            graph,
                            out value))
                    {
                        instruction.Instruction = Instruction.CreateCopy(
                            instruction.ResultType,
                            value);
                    }
                }
                else if (proto is StorePrototype)
                {
                    // Stores can be eliminated if they don't affect the memory state.
                    var storeProto  = (StorePrototype)proto;
                    var stateBefore = memSSA.GetMemoryBefore(instruction);
                    var stateAfter  = memSSA.GetMemoryAfter(instruction);
                    if (stateBefore == stateAfter)
                    {
                        EliminateStore(instruction);
                    }
                }
            }

            // Then try to coalesce stores by iterating through basic blocks.
            foreach (var block in builder.BasicBlocks)
            {
                var pendingStores = new List <NamedInstructionBuilder>();
                foreach (var instruction in block.NamedInstructions)
                {
                    var proto = instruction.Prototype;
                    if (proto is StorePrototype)
                    {
                        var storeProto = (StorePrototype)proto;
                        var pointer    = storeProto.GetPointer(instruction.Instruction);

                        var newPending = new List <NamedInstructionBuilder>();
                        foreach (var pending in pendingStores)
                        {
                            var pendingProto   = (StorePrototype)pending.Prototype;
                            var pendingPointer = pendingProto.GetPointer(pending.Instruction);
                            var aliasState     = aliasing.GetAliasing(pointer, pendingPointer);
                            if (aliasState == Aliasing.MustAlias)
                            {
                                // Yes, do it. Delete the pending store.
                                EliminateStore(pending);
                            }
                            else if (aliasState == Aliasing.NoAlias)
                            {
                                // We can't eliminate the pending store, but we can keep it
                                // in the pending list.
                                newPending.Add(pending);
                            }
                        }
                        pendingStores = newPending;

                        // Add this store to the list of pending stores as well.
                        pendingStores.Add(instruction);
                    }
                    else if (proto is LoadPrototype || proto is CopyPrototype)
                    {
                        // Loads are perfectly benign. They *may* be effectful in the sense
                        // that they can trigger a segfault and make the program blow up, but
                        // there's no way we can catch that anyway. We'll just allow store
                        // coalescing across load boundaries.
                        //
                        // We also want to test for copy instructions here because our effectfulness
                        // analysis is slightly outdated and we may have turned loads/stores into copies.
                    }
                    else if (effectfulness.Instructions.Contains(instruction))
                    {
                        // Effectful instructions are a barrier for store coalescing.
                        pendingStores.Clear();
                    }
                }
            }

            return(builder.ToImmutable());
        }