private AssignmentStatement <TInstruction> CreateAssignment( Expression <TInstruction> expression, TInstruction instruction, IVariable[] writtenVariables) { // We will create stack slots for every push and "simulate" an assignment // to them. This way the resulting AST won't have the "concept" of a stack. int pushes = _context.Architecture.GetStackPushCount(instruction); var slots = GetStackSlots(pushes); var buffer = new AstVariable[slots.Length + writtenVariables.Length]; slots.CopyTo(buffer.AsSpan()); // Also assign to written variables. for (int i = 0; i < writtenVariables.Length; i++) { // Get the correct version of the variable to assign to. var variable = writtenVariables[i]; int version = _context.GetVariableVersion(variable); var snapshot = new VariableSnapshot(variable, version); var versionedVariable = _context.GetVersionedVariable(snapshot); buffer[slots.Length + i] = versionedVariable; } // Assign stack slots. _context.StackSlots[instruction] = slots; return(new AssignmentStatement <TInstruction>(buffer, expression)); }
internal AstVariable GetVersionedVariable(VariableSnapshot snapshot) { if (VersionedVariables.TryGetValue(snapshot, out var variable)) { return(variable); } variable = new AstVariable(snapshot.ToString()); VersionedVariables.Add(snapshot, variable); return(variable); }
private IVariable[] VersionWrittenVariables(TInstruction instruction) { var buffer = GetWrittenVariables(instruction); foreach (var variable in buffer) { if (_context.VariableStates.TryGetValue(instruction, out var states)) { // If we already have a dictionary that contains versions for this instruction, // we can reuse the same instance. // If the instruction hasn't "seen" the variable before, we add a new entry. if (!states.ContainsKey(variable)) { states.Add(variable, _context.VariableVersions[variable]++); } // Create a new snapshot of the variable and store the versioned variable // created from the snapshot. var snapshot = new VariableSnapshot(variable, _context.VariableVersions[variable]); var versionedVariable = _context.GetVersionedVariable(snapshot); _context.VersionedVariables[snapshot] = versionedVariable; } else { // Otherwise we will create a new dictionary and store it so it can be // reused later. // Increment the version of the variable, create a snapshot and // add it to the newly created dictionary. int version = _context.IncrementVariableVersion(variable); var snapshot = new VariableSnapshot(variable, version); states = new Dictionary <IVariable, int> { [variable] = version }; // Save the dictionary for later reuse and get a variable for the snapshot. _context.VariableStates[instruction] = states; _context.VersionedVariables[snapshot] = _context.GetVersionedVariable(snapshot); } } return(buffer); }
private void CollectVariableDependencies( BasicBlock <Statement <TInstruction> > block, DataFlowNode <TInstruction> dataFlowNode, Span <IVariable> buffer, ref int phiStatementCount) { int index = 0; var variableDependencies = dataFlowNode.VariableDependencies; foreach (var pair in variableDependencies) { var variable = pair.Key; var dependency = pair.Value; if (dependency.Count <= 1) { // If the dependency has only 1 possible source we just simply // get the variable. But since the AST utilizes SSA, all of the // variables are versioned. This is good because everything is // "immutable". One "real" variable will have a new versioned // variable created every time it is assigned to. int version = _context.GetVariableVersion(variable); var snapshot = new VariableSnapshot(variable, version); buffer[index++] = _context.GetVersionedVariable(snapshot); continue; } // Otherwise (>0), we will get the list of versioned(!) variables // that could reach the instruction and create a "phi" statement // like in the stack dependencies. var sources = CollectVariables(); if (_context.VariableSourcesToPhiVariable.TryGetValue(sources, out var phi)) { // If a phi slot already exists for the list of variables, // reuse the same phi slot. buffer[index++] = phi; } else { // Otherwise, create a new phi slot for the list of variables // and save it if we encounter the same variables again. phi = CreatePhiSlot(); var slots = sources .Select(source => new VariableExpression <TInstruction>(source)) .ToArray(); _context.VariableSourcesToPhiVariable.Add(sources, phi); block.Instructions.Insert(phiStatementCount++, new PhiStatement <TInstruction>(phi, slots)); buffer[index++] = phi; } List <AstVariable> CollectVariables() { var result = new List <AstVariable>(); foreach (var instruction in dependency.Select(dep => dep.Node.Contents)) { if (_context.VariableStates.TryGetValue(instruction, out var versions)) { // If we already have a dictionary for the instruction, we will // just get the versioned variable from the existing dictionary. var snapshot = new VariableSnapshot(variable, versions[variable]); result.Add(_context.VersionedVariables[snapshot]); } else { // Otherwise, we will create a new dictionary for the instruction. var snapshot = new VariableSnapshot(variable, 0); var slot = _context.GetVersionedVariable(snapshot); _context.VariableStates.Add(instruction, new Dictionary <IVariable, int> { [variable] = 0 }); result.Add(slot); } } return(result); } } }