private static MemorySSA.Value UpdateState(MemorySSA.Value state, NamedInstruction instruction) { var graph = instruction.Block.Graph; var proto = instruction.Prototype; if (proto is StorePrototype) { // Stores are special. They map directly to a memory SSA store. var storeProto = (StorePrototype)proto; var pointer = storeProto.GetPointer(instruction.Instruction); var value = storeProto.GetValue(instruction.Instruction); return(MemorySSA.Store.WithStore(state, pointer, value, graph)); } else if (proto is LoadPrototype) { return(state); } else if (graph.GetAnalysisResult <EffectfulInstructions>().Instructions.Contains(instruction)) { // Instructions that affect memory in unpredictable ways produce // an unknown state. // // TODO: reduce the number of instructions that produce an unknown state. // At the moment, all effectful instructions are considered to produce // an unknown memory state. However, many instructions that may throw // don't affect the (accessible) memory state at all. We need some way // to query whether instructions may write to memory or not. return(MemorySSA.Unknown.Instance); } else { return(state); } }
/// <summary> /// Tells if a particular instruction is dominated by another instruction, /// that is, if control cannot flow to the instruction unless it first flowed /// through the dominator instruction. /// </summary> /// <param name="instruction"> /// An instruction that might be dominated by <paramref name="dominator"/>. /// </param> /// <param name="dominator"> /// An instruction that might dominate <paramref name="instruction"/>. /// </param> /// <returns> /// <c>true</c> if <paramref name="instruction"/> is strictly dominated by /// <paramref name="dominator"/> or <paramref name="instruction"/> equals /// <paramref name="dominator"/>; otherwise, <c>false</c>. /// </returns> public bool IsDominatedBy(NamedInstruction instruction, NamedInstruction dominator) { var graph = instruction.Block.Graph; ContractHelpers.Assert(graph == dominator.Block.Graph); return(IsDominatedBy(instruction, dominator, graph)); }
/// <summary> /// Emits an instruction and its implementation. /// </summary> /// <param name="instruction">The instruction to emit.</param> /// <param name="selection">The instruction's implementation.</param> public void Emit(NamedInstruction instruction, SelectedInstructions <TInstruction> selection) { if (parent.ShouldMaterializeOnUse(instruction)) { return; } // Line up arguments. LoadDependencies(selection.Dependencies); // Emit the instruction's implementation. instructionBlobs.AddLast(selection.Instructions); if (parent.StackSelector.Pushes(instruction.Prototype)) { // Push the result on the stack, if there is a result. Store(instruction); } else { // Otherwise, we still want to indicate that `instruction` is defined // here, to keep us from reordering loads and stores in a bad way. resurrectionPoints[instruction] = instructionBlobs.Last; invResurrectionPoints[instructionBlobs.Last] = instruction; } }
private static bool DefaultIsEffectfulImpl(NamedInstruction selection) { var instruction = selection.Instruction; var proto = instruction.Prototype; var memorySpec = selection.Block.Graph .GetAnalysisResult <PrototypeMemorySpecs>() .GetMemorySpecification(proto); if (memorySpec.MayWrite) { // Instructions that may write to memory are effectful. return(true); } else if (selection.Block.Graph.GetAnalysisResult <InstructionExceptionSpecs>() .GetExceptionSpecification(selection.Instruction) .CanThrowSomething) { // TODO: consider method attributes. Some calls may // not have side-effects and may be marked as such. // Instructions whose exceptions can be delayed are not // necessarily effectful. return(!selection.Block.Graph.CanDelayExceptions(selection)); } else { // TODO: support effectful intrinsics that do not throw. // At the moment, all non-throwing intrinsics are assumed // to not be effectful, but that's not exactly ideal. return(false); } }
private void Materialize(NamedInstruction instruction) { var isel = blockBuilder.parent.InstructionSelector.SelectInstructions(instruction); foreach (var item in isel.Dependencies) { Materialize(Block.Graph.GetInstruction(item)); } insertionPoint = insertionPoint.List.AddAfter(insertionPoint, isel.Instructions); MakeStackNonEmptyAt(insertionPoint, null); }
/// <inheritdoc/> protected override bool ShouldMaterializeOnUse(NamedInstruction instruction) { if (CilSelector.AllocaToVariableMapping.ContainsKey(instruction)) { // Materialize ldloca instructions on use. return(true); } // Materialize trivial constants on use. var proto = instruction.Prototype as ConstantPrototype; return(proto != null && proto.Value != DefaultConstant.Instance); }
/// <summary> /// Gets the memory state before a particular instruction has executed. /// </summary> /// <param name="instruction">An instruction.</param> /// <returns>A memory state.</returns> public Value GetMemoryBefore(NamedInstruction instruction) { var prev = instruction.PreviousInstructionOrNull; if (prev == null) { return(GetMemoryAtEntry(instruction.Block)); } else { return(GetMemoryAfter(prev)); } }
private static bool DefaultIsEffectfulImpl(NamedInstruction selection) { var instruction = selection.Instruction; var proto = instruction.Prototype; if (proto is LoadPrototype) { // Load instructions are effectful if they may not be dereferenceable. var nullAnalysis = selection.Block.Graph.GetAnalysisResult <ValueNullability>(); return(!nullAnalysis.IsDereferenceable(((LoadPrototype)proto).GetPointer(instruction))); } else if (proto is GetFieldPointerPrototype) { // Get-field-pointer instructions are effectful if the base pointer // may not be dereferenceable, _unless_ the exception may be delayed. var nullAnalysis = selection.Block.Graph.GetAnalysisResult <ValueNullability>(); if (nullAnalysis.IsDereferenceable(((GetFieldPointerPrototype)proto).GetBasePointer(instruction))) { return(false); } } if (proto is StorePrototype || proto is CallPrototype || proto is NewObjectPrototype || selection.Block.Graph.GetAnalysisResult <InstructionExceptionSpecs>() .GetExceptionSpecification(selection.Instruction) .CanThrowSomething) { // TODO: consider method attributes. Some calls may // not have side-effects and may be marked as such. // Instructions whose exceptions can be delayed are not // necessarily effectful. return(!selection.Block.Graph.CanDelayExceptions(selection)); } else { // TODO: support effectful intrinsics that do not throw. // At the moment, all non-throwing intrinsics are assumed // to not be effectful, but that's not exactly ideal. return(false); } }
/// <summary> /// Adds an instruction to this value numbering. /// </summary> /// <param name="instruction"> /// The instruction to add. /// </param> public void AddInstruction(NamedInstruction instruction) { if (valueNumbers.ContainsKey(instruction)) { return; } // TODO: at the moment, we consider all basic block // parameters to have their own number. We can do better // than this. Concretely, we can use a lattice-based // update propagation approach as is used by the constant // propagation algorithm. Once lattice-based analyses are // abstracted over, we should use that for value numbering // instead of this simplistic algorithm. // // TL;DR: abstract over lattice-based update propagation, // then implement better value numbering based on that. var graph = instruction.Block.Graph; foreach (var arg in instruction.Instruction.Arguments) { if (graph.ContainsInstruction(arg)) { AddInstruction(graph.GetInstruction(arg)); } else { AddBlockParameter(arg); } } ValueTag number; if (!TryGetNumber(instruction.Instruction, out number)) { number = instruction; } valueNumbers[instruction] = number; instructionNumbers[instruction.Instruction] = number; }
private static bool IsDefaultInitialization(NamedInstruction instruction) { var proto = instruction.Prototype; if (proto is StorePrototype) { var storeProto = (StorePrototype)proto; var value = storeProto.GetValue(instruction.Instruction); var graph = instruction.Block.Graph; if (graph.ContainsInstruction(value)) { var valueProto = graph.GetInstruction(value).Prototype as ConstantPrototype; if (valueProto != null && valueProto.Value == DefaultConstant.Instance) { return(true); } } } return(false); }
private static void ToReductionList( NamedInstruction instruction, InstructionPrototype prototype, List <ValueTag> reductionArgs, HashSet <ValueTag> reductionOps, ValueUses uses) { if (uses.GetUseCount(instruction) == 1 && instruction.Prototype == prototype) { ToReductionList( instruction.Instruction.Arguments, prototype, reductionArgs, reductionOps, uses, instruction.Block.Graph); reductionOps.Add(instruction); } else { reductionArgs.Add(instruction); } }
public override LatticeCell Evaluate( NamedInstruction instruction, IReadOnlyDictionary <ValueTag, LatticeCell> cells) { return(Evaluate(instruction.Instruction, cells, instruction.Block.Graph)); }
/// <summary> /// Evaluates an instruction to a lattice cell, given the values /// other cells evaluate to. /// </summary> /// <param name="instruction"> /// The instruction to evaluate. /// </param> /// <param name="cells"> /// The lattice cells currently assigned to values in the graph. /// </param> /// <returns> /// A lattice cell. /// </returns> public abstract TCell Evaluate( NamedInstruction instruction, IReadOnlyDictionary <ValueTag, TCell> cells);
/// <summary> /// Gets the memory state after a particular instruction has executed. /// </summary> /// <param name="instruction">An instruction.</param> /// <returns>A memory state.</returns> public Value GetMemoryAfter(NamedInstruction instruction) { return(InstructionValues[instruction]); }
/// <summary> /// Tells if an instruction should always be materialized when it is /// used rather than when it is defined. /// </summary> /// <param name="instruction">An instruction to inspect.</param> /// <returns> /// <c>true</c> if <paramref name="instruction"/> should be materialized /// when it is used instead of when it is defined; otherwise, <c>false</c>. /// </returns> /// <remark> /// An instruction that is materialized on use may only depend on other /// instructions that are materialized on use. /// </remark> protected virtual bool ShouldMaterializeOnUse(NamedInstruction instruction) { return(false); }