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