/// <summary> /// Gets the type of a particular Flame IR value. /// </summary> /// <param name="value">The value to inspect.</param> /// <returns>A type.</returns> public IType GetValueType(ValueTag value) { return(Block.Graph.GetValueType(value)); }
/// <summary> /// Creates an instance of this store prototype. /// </summary> /// <param name="pointer"> /// A pointer to the value to replace. /// </param> /// <param name="value"> /// The value to store in the pointer's pointee. /// </param> /// <returns>A store instruction.</returns> public Instruction Instantiate(ValueTag pointer, ValueTag value) { return(Instantiate(new ValueTag[] { pointer, value })); }
/// <summary> /// Tells if a register should be allocated for a /// particular value. /// </summary> /// <param name="value"> /// The value for which register allocation may or may /// not be necessary. /// </param> /// <param name="graph"> /// The control flow graph that defines <paramref name="value"/>. /// </param> /// <returns> /// <c>true</c> if a register must be allocated to /// <paramref name="value"/>; otherwise, <c>false</c>. /// </returns> /// <remarks> /// Implementations may override this method to suppress /// register allocation for values that are, e.g., stored /// on an evaluation stack. /// </remarks> protected virtual bool RequiresRegister( ValueTag value, FlowGraph graph) { return(true); }
/// <summary> /// Gets the set of basic block tags for all basic blocks containing flows /// that use <paramref name="tag"/>. /// </summary> /// <param name="tag">The tag to examine.</param> /// <returns> /// A set of basic block tags for all basic blocks containing flows /// that use <paramref name="tag"/>. /// </returns> public ImmutableHashSet <BasicBlockTag> GetFlowUses(ValueTag tag) { return(flow[tag]); }
/// <summary> /// Instantiates this prototype. /// </summary> /// <param name="elementCount"> /// The number of elements to allocate storage for. /// </param> /// <returns> /// An alloca-array instruction. /// </returns> public Instruction Instantiate(ValueTag elementCount) { return(Instantiate(new ValueTag[] { elementCount })); }
/// <inheritdoc/> public InstructionOrdering Analyze(FlowGraph graph) { // This analysis imposes a partial ordering on instructions (the // dependency relation) based on the following rules: // // 1. Non-delayable exception-throwing instructions are // totally ordered. // // 2. a. Value-reading instructions depend on value-writing // instructions that refer to the same address. // b. Value-writing instructions depend on value-writing // instructions that refer to the same address. // c. Exception-throwing instructions depend on value-writing // instructions. // d. Value-writing instructions depend on exception-throwing // instructions. // e. Value-writing instructions depend on value-reading // instructions that refer to the same address. // // 3. All instructions depend on their arguments, provided // that these arguments refer to instructions inside the // same basic block. // // 4. Dependencies are transitive. var memorySpecs = graph.GetAnalysisResult <PrototypeMemorySpecs>(); var exceptionSpecs = graph.GetAnalysisResult <InstructionExceptionSpecs>(); var aliasAnalysis = graph.GetAnalysisResult <AliasAnalysisResult>(); var delayability = graph.GetAnalysisResult <ExceptionDelayability>(); var dependencies = new Dictionary <ValueTag, HashSet <ValueTag> >(); foreach (var block in graph.BasicBlocks) { // `knownWrites` is a mapping of value-writing instructions to the // addresses they update. var knownWrites = new Dictionary <ValueTag, ValueTag>(); // Ditto for `knownReads`, but it describes reads instead. var knownReads = new Dictionary <ValueTag, ValueTag>(); // `unknownWrites` is the set of all writes to unknown addresses. var unknownWrites = new HashSet <ValueTag>(); // `lastWrite` is the last write. ValueTag lastWrite = null; // `unknownReads` is the set of all reads from unknown addresses. var unknownReads = new HashSet <ValueTag>(); // `lastRead` is the last read. ValueTag lastRead = null; // `lastNonDelayableThrower` is the last non-delayable exception-throwing // instruction. ValueTag lastNonDelayableThrower = null; // `lastThrower` is the last exception-throwing instruction. ValueTag lastThrower = null; foreach (var selection in block.NamedInstructions) { var insnDependencies = new HashSet <ValueTag>(); var instruction = selection.Instruction; var exceptionSpec = exceptionSpecs.GetExceptionSpecification(instruction); var memSpec = memorySpecs.GetMemorySpecification(instruction.Prototype); var oldLastThrower = lastThrower; var oldLastRead = lastRead; if (exceptionSpec.CanThrowSomething) { // Rule #2.c: Exception-throwing instructions depend on value-writing // instructions. insnDependencies.Add(lastWrite); if (delayability.CanDelayExceptions(instruction.Prototype)) { // Rule #1: Non-delayable exception-throwing instructions are // totally ordered. insnDependencies.Add(lastNonDelayableThrower); lastNonDelayableThrower = selection; } lastThrower = selection; } if (memSpec.MayRead) { // Rule #2.a: Value-reading instructions depend on value-writing // instructions that refer to the same address. insnDependencies.UnionWith(unknownWrites); if (memSpec is MemorySpecification.ArgumentRead) { var argReadSpec = (MemorySpecification.ArgumentRead)memSpec; var readAddress = instruction.Arguments[argReadSpec.ParameterIndex]; foreach (var pair in knownWrites) { if (aliasAnalysis.GetAliasing(pair.Value, readAddress) != Aliasing.NoAlias) { insnDependencies.Add(pair.Key); } } // Update the set of known reads. knownReads[selection] = selection; } else { insnDependencies.Add(lastWrite); // Update the unknown read set. unknownReads.Add(selection); } // Update the last read. lastRead = selection; } if (memSpec.MayWrite) { // Rule #2.b: Value-writing instructions depend on value-writing // instructions that refer to the same address. // Rule #2.e: Value-writing instructions depend on value-reading // instructions that refer to the same address. insnDependencies.UnionWith(unknownWrites); insnDependencies.UnionWith(unknownReads); if (memSpec is MemorySpecification.ArgumentWrite) { var argWriteSpec = (MemorySpecification.ArgumentWrite)memSpec; var writeAddress = instruction.Arguments[argWriteSpec.ParameterIndex]; foreach (var pair in knownWrites.Concat(knownReads)) { if (pair.Key == selection.Tag) { continue; } if (aliasAnalysis.GetAliasing(pair.Value, writeAddress) != Aliasing.NoAlias) { insnDependencies.Add(pair.Key); } } // Update the set of known writes. knownWrites[selection] = writeAddress; } else { insnDependencies.Add(lastWrite); insnDependencies.Add(oldLastRead); // Update the unknown write set. unknownWrites.Add(selection); } // Rule #2.d: Value-writing instructions depend on exception-throwing // instructions. insnDependencies.Add(oldLastThrower); // Update the last write. lastWrite = selection; } // Rule #3: all instructions depend on their arguments, provided // that these arguments refer to instructions inside the // same basic block. foreach (var arg in instruction.Arguments) { NamedInstruction argInstruction; if (graph.TryGetInstruction(arg, out argInstruction) && argInstruction.Block.Tag == block.Tag) { insnDependencies.Add(arg); } } // We might have added a `null` value tag to the set of dependencies. // Adding it was harmless, but we need to rid ourselves of it before // the dependency set is used in a situation where `null` is undesirable, // like in the loop below. Ditto for self-dependencies. insnDependencies.Remove(null); insnDependencies.Remove(selection); // Rule #4: dependencies are transitive. foreach (var item in insnDependencies.ToArray()) { if (dependencies.ContainsKey(item)) { insnDependencies.UnionWith(dependencies[item]); } } dependencies[selection.Tag] = insnDependencies; } } return(new DependencyBasedInstructionOrdering(dependencies)); }
/// <inheritdoc/> protected override bool RequiresRegister( ValueTag value, FlowGraph graph) { return(usedValues.Contains(value)); }
/// <inheritdoc/> public override NamedInstructionBuilder InsertBefore(Instruction instruction, ValueTag tag) { if (!IsValid) { throw new InvalidOperationException( "Cannot prepend an instruction to an invalid instruction builder."); } return(block.AppendInstruction(instruction, tag)); }
/// <summary> /// Gets a value's "number," i.e., another value that /// is representative of the set of all values that are /// equivalent with the value. /// </summary> /// <param name="value">A value tag to examine.</param> /// <returns> /// The set representative for the set of all values equivalent /// with <paramref name="value"/>. Requesting the set /// representative of another value that is equivalent with /// <paramref name="value"/> will produce the same set /// representative. /// </returns> public abstract ValueTag GetNumber(ValueTag value);
/// <summary> /// Tells if a particular value is dominated by another value, /// that is, if control cannot flow to the value unless it first flowed /// through the dominator value. /// </summary> /// <param name="value"> /// A value that might be dominated by <paramref name="dominator"/>. /// </param> /// <param name="dominator"> /// A value that might dominate <paramref name="value"/>. /// </param> /// <param name="graph"> /// A control-flow graph that defines both <paramref name="value"/> and <paramref name="dominator"/>. /// </param> /// <returns> /// <c>true</c> if <paramref name="value"/> is strictly dominated by /// <paramref name="dominator"/> or <paramref name="value"/> equals /// <paramref name="dominator"/>; otherwise, <c>false</c>. /// </returns> public bool IsDominatedBy(ValueTag value, ValueTag dominator, FlowGraph graph) { return(value == dominator || IsStrictlyDominatedBy(value, dominator, graph)); }
/// <summary> /// Tells if a particular value is dominated by another value, /// that is, if control cannot flow to the value unless it first flowed /// through the dominator value. /// </summary> /// <param name="value"> /// A value that might be dominated by <paramref name="dominator"/>. /// </param> /// <param name="dominator"> /// A value that might dominate <paramref name="value"/>. /// </param> /// <param name="graph"> /// A control-flow graph that defines both <paramref name="value"/> and <paramref name="dominator"/>. /// </param> /// <returns> /// <c>true</c> if <paramref name="value"/> is strictly dominated by /// <paramref name="dominator"/> or <paramref name="value"/> equals /// <paramref name="dominator"/>; otherwise, <c>false</c>. /// </returns> public bool IsDominatedBy(ValueTag value, ValueTag dominator, FlowGraphBuilder graph) { return(IsDominatedBy(value, dominator, graph.ImmutableGraph)); }
public override BlockFlow Emit( FlowGraphBuilder graph, ValueTag value) { // Create the following blocks: // // bitswitch.entry(): // minvalue = const <MinValue> // switchval.adjusted = switchval - minvalue // switchval.unsigned = (uintX)switchval.adjusted // valrange = const <ValueRange> // switch (switchval.unsigned > valrange) // 0 -> bitswitch.header() // default -> <defaultBranch> // // bitswitch.header(): // one = const 1 // shifted = one << switchval.unsigned // bitmask1 = const <bitmask1> // switch (shifted & bitmask1) // 0 -> bitswitch.case2() // default -> <case1Branch> // // bitswitch.case2(): // bitmask2 = const <bitmask2> // switch (shifted & bitmask1) // 0 -> bitswitch.case3() // default -> <case2Branch> // // ... var entryBlock = graph.AddBasicBlock("bitswitch.entry"); var headerBlock = graph.AddBasicBlock("bitswitch.header"); var valueType = graph.GetValueType(value); var valueSpec = valueType.GetIntegerSpecOrNull(); var defaultBranch = Flow.DefaultBranch; // Subtract the min value from the switch value if necessary. if (!MinValue.IsZero) { value = entryBlock.AppendInstruction( Instruction.CreateBinaryArithmeticIntrinsic( ArithmeticIntrinsics.Operators.Subtract, false, valueType, value, entryBlock.AppendInstruction( Instruction.CreateConstant(MinValue, valueType), "minvalue")), "switchval.adjusted"); } // Make the switch value unsigned if it wasn't already. if (valueSpec.IsSigned) { var uintType = TypeEnvironment.MakeUnsignedIntegerType(valueSpec.Size); value = entryBlock.AppendInstruction( Instruction.CreateConvertIntrinsic( false, uintType, valueType, value), "switchval.unsigned"); valueType = uintType; valueSpec = uintType.GetIntegerSpecOrNull(); } // Check that the value is within range. entryBlock.Flow = SwitchFlow.CreateIfElse( Instruction.CreateRelationalIntrinsic( ArithmeticIntrinsics.Operators.IsGreaterThan, TypeEnvironment.Boolean, valueType, value, entryBlock.AppendInstruction( Instruction.CreateConstant( ValueRange.CastSignedness(false), valueType), "valrange")), defaultBranch, new Branch(headerBlock)); // Pick an appropriate type for the bitmasks. var bitmaskType = valueType; if (valueSpec.Size < 32) { bitmaskType = TypeEnvironment.UInt32; } if (ValueRange.IsGreaterThan(new IntegerConstant(valueSpec.Size, ValueRange.Spec))) { bitmaskType = TypeEnvironment.UInt64; } // Set up first part of the header block. if (bitmaskType != valueType) { valueSpec = bitmaskType.GetIntegerSpecOrNull(); } var zero = headerBlock.AppendInstruction( Instruction.CreateConstant( new IntegerConstant(0, valueSpec), valueType), "zero"); var one = headerBlock.AppendInstruction( Instruction.CreateConstant( new IntegerConstant(1, valueSpec), valueType), "one"); value = headerBlock.AppendInstruction( Instruction.CreateArithmeticIntrinsic( ArithmeticIntrinsics.Operators.LeftShift, false, bitmaskType, new[] { bitmaskType, valueType }, new[] { one, value }), "shifted"); valueType = bitmaskType; // Start emitting cases. var caseBlock = headerBlock; var nextCase = graph.AddBasicBlock("bitswitch.case1"); for (int i = 0; i < Flow.Cases.Count; i++) { // Construct a mask for the case. var switchCase = Flow.Cases[i]; var oneConstant = new IntegerConstant(1, valueSpec); var mask = new IntegerConstant(0, valueSpec); foreach (var pattern in switchCase.Values) { mask = mask.BitwiseOr(oneConstant.ShiftLeft(((IntegerConstant)pattern).Subtract(MinValue))); } // Switch on the bitwise 'and' of the mask and // the shifted value. caseBlock.Flow = SwitchFlow.CreateIfElse( Instruction.CreateBinaryArithmeticIntrinsic( ArithmeticIntrinsics.Operators.And, false, valueType, value, caseBlock.AppendInstruction( Instruction.CreateConstant(mask, valueType), "bitmask" + i)), switchCase.Branch, new Branch(nextCase)); caseBlock = nextCase; nextCase = graph.AddBasicBlock("bitswitch.case" + (i + 2)); } // Jump to the default branch if nothing matches. caseBlock.Flow = new JumpFlow(defaultBranch); // Jump to the header block and let it do all of the heavy // lifting. return(new JumpFlow(entryBlock)); }
/// <inheritdoc/> public override BlockFlow Emit( FlowGraphBuilder graph, ValueTag value) { return(new JumpFlow(Branch)); }
/// <summary> /// Turns this switch flow lowering into an /// actual block flow. /// </summary> /// <param name="graph">A flow graph builder.</param> /// <param name="value"> /// The value being switched on. /// </param> /// <returns>Block flow.</returns> public abstract BlockFlow Emit( FlowGraphBuilder graph, ValueTag value);
/// <summary> /// Tells if the first instruction must run before the second /// instruction, assuming that both instructions are defined /// by the same basic block. /// </summary> /// <param name="first"> /// The value tag of the first instruction to inspect. /// </param> /// <param name="second"> /// The value tag of the second instruction to inspect. /// </param> /// <returns> /// <c>true</c> if the first instruction must run before the second /// instruction runs; otherwise, <c>false</c>. /// </returns> public abstract bool MustRunBefore(ValueTag first, ValueTag second);
public void AddBlockParameter(ValueTag parameterTag) { valueNumbers[parameterTag] = parameterTag; }
/// <inheritdoc/> public override bool MustRunBefore(ValueTag first, ValueTag second) { return(dependencies[second].Contains(first)); }
public override ValueTag GetNumber(ValueTag value) { return(valueNumbers[value]); }
private static void TryRemoveTrivialPhi( ValueTag phi, Dictionary <ValueTag, HashSet <ValueTag> > phiArgs, Dictionary <ValueTag, HashSet <ValueTag> > phiUsers, HashSet <ValueTag> specialPhis, Dictionary <ValueTag, ValueTag> copyMap) { // This algorithm is based on the `tryRemoveTrivialPhi` algorithm as 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). if (phi == null || specialPhis.Contains(phi) || copyMap.ContainsKey(phi)) { // Never ever eliminate special phis. return; } ValueTag same = null; foreach (var arg in phiArgs[phi]) { var actualArg = GetActualCopy(arg, copyMap); if (actualArg == null || actualArg == same || actualArg == phi) { continue; } else if (same != null) { // The phi merges at least two values, which // makes it non-trivial. return; } same = actualArg; } // Reroute all uses of `phi` to `same`. If `same` is null, // then that means that the phi has zero arguments. It is // trivial, but not a "real" copy, so we'll just write `null` // to the copy map as well. copyMap[phi] = same; // Recurse on `phi` users, which may have become trivial. foreach (var use in phiUsers[phi]) { var copy = GetActualCopy(use, copyMap); if (copy != null && phiArgs.ContainsKey(copy)) { // Be sure to check that the phi we want to eliminate // is actually a phi. Things will go horribly wrong if // we try to remove a "phi" that is actually an instruction // instead of a block parameter. TryRemoveTrivialPhi( copy, phiArgs, phiUsers, specialPhis, copyMap); } } }
public override bool TryGetNumber(Instruction instruction, out ValueTag number) { return(instructionNumbers.TryGetValue(instruction, out number)); }
/// <summary> /// Gets the set of all values that are defined by instructions /// that take <paramref name="tag"/> as an argument. /// </summary> /// <param name="tag">The tag to examine.</param> /// <returns> /// A set of all value tags of instructions that use <paramref name="tag"/>. /// </returns> public ImmutableHashSet <ValueTag> GetInstructionUses(ValueTag tag) { return(instructions[tag]); }
/// <summary> /// Tests if two values are equivalent. Values 'a', 'b' are considered /// to be equivalent iff 'a' dominates 'b' implies that 'b' can be /// replaced with a copy of 'a'. /// </summary> /// <param name="first">The first value to consider.</param> /// <param name="second">The second value to consider.</param> /// <returns> /// <c>true</c> if the values are equivalent; otherwise, <c>false</c>. /// </returns> public bool AreEquivalent(ValueTag first, ValueTag second) { return(GetNumber(first) == GetNumber(second)); }
/// <summary> /// Gets the number of distinct instructions and block flows /// that use a particular tag. /// </summary> /// <param name="tag"> /// The tag to find a use count for. /// </param> /// <returns> /// The number of distinct instructions and block flows /// that use <paramref name="tag"/>. /// </returns> public int GetUseCount(ValueTag tag) { return(GetInstructionUses(tag).Count + GetFlowUses(tag).Count); }
/// <summary> /// Tries to compute the value number of an instruction. /// </summary> /// <param name="instruction"> /// The instruction to number. /// </param> /// <param name="number"> /// A value number if a value is found that is equivalent /// to <paramref name="instruction"/>; otherwise, <c>null</c>. /// </param> /// <returns> /// <c>true</c> if a value is found that is equivalent /// to <paramref name="instruction"/>; otherwise, <c>false</c>. /// </returns> public abstract bool TryGetNumber(Instruction instruction, out ValueTag number);
/// <summary> /// Creates a store. /// </summary> /// <param name="operand"> /// The memory state to update. /// </param> /// <param name="address"> /// The address that is written to. /// </param> /// <param name="value"> /// The value that is written to the address. /// </param> public Store(Value operand, ValueTag address, ValueTag value) { this.Operand = operand; this.Address = address; this.Value = value; }
/// <summary> /// Tests if an instruction is equivalent to a value. /// </summary> /// <param name="first">The instruction to consider.</param> /// <param name="second">The value to consider.</param> /// <returns> /// <c>true</c> if the instruction is equivalent to the value; otherwise, <c>false</c>. /// </returns> public bool AreEquivalent(Instruction first, ValueTag second) { ValueTag number; return(TryGetNumber(first, out number) && number == GetNumber(second)); }
/// <summary> /// Creates an instance of this load prototype. /// </summary> /// <param name="pointer"> /// A pointer to the value to load. /// </param> /// <returns>A load instruction.</returns> public Instruction Instantiate(ValueTag pointer) { return(Instantiate(new ValueTag[] { pointer })); }
/// <summary> /// Instantiates this box instruction prototype. /// </summary> /// <param name="value"> /// The value to box. /// </param> /// <returns> /// A box instruction. /// </returns> public Instruction Instantiate(ValueTag value) { return(Instantiate(new ValueTag[] { value })); }
/// <summary> /// Gets the register allocated to a particular value. /// </summary> /// <param name="value"> /// The value to find a register for. /// </param> /// <returns> /// A register. /// </returns> public TRegister GetRegister(ValueTag value) { return(Allocation[value]); }
/// <summary> /// Pushes a reference to a value onto the evaluation stack. /// </summary> /// <param name="value">The value to push.</param> /// <returns>The <paramref name="value"/> parameter.</returns> public ValueTag Push(ValueTag value) { stack.Push(value); return(value); }