/** * Clone the instruction for use in an inlining context (either when a scope is inlined into * another scope, or when a block has to be cloned because its associated call belongs to * an inlined scope). This requires renaming variables and labels to eliminate naming * conflicts. * * @param inlinerInfo This object manages renaming of variables and labels, handles * args and return values. * @return a new instruction that can be used in the target scope. */ public Instr cloneForInlining(InlinerInfo inlinerInfo) { throw new RuntimeException("cloneForInlining: Not implemented for: " + this.getOperation()); }
/** * Clone the instruction for use in an inlining context (either when a scope is inlined into * another scope, or when a block has to be cloned because its associated call belongs to * an inlined scope). This requires renaming variables and labels to eliminate naming * conflicts. * * @param inlinerInfo This object manages renaming of variables and labels, handles * args and return values. * @return a new instruction that can be used in the target scope. */ public Instr cloneForInlining(InlinerInfo inlinerInfo) { throw new RuntimeException("cloneForInlining: Not implemented for: " + this.getOperation()); }
public void encode(IRWriterEncoder e) { if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("Instr(" + getOperation() + "): " + this); e.encode(getOperation()); }
public void encode(IRWriterEncoder e) { if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("Instr(" + getOperation() + "): " + this); e.encode(getOperation()); }
/** * This method takes as input a map of operands to their values, and outputs * * If the value map provides a value for any of the instruction's operands * this method is expected to replace the original operands with the simplified values. * It is not required that it do so -- code correctness is not compromised by failure * to simplify */ public void simplifyOperands(Map<Operand, Operand> valueMap, boolean force) { if (getOperands() != EMPTY_OPERANDS) { System.out.println("simplifyOperands: Missing implementation for: " + this.getOperation()); throw new RuntimeException("simplifyOperands: Missing implementation for: " + this.getOperation()); } }
/** * This method takes as input a map of operands to their values, and outputs * * If the value map provides a value for any of the instruction's operands * this method is expected to replace the original operands with the simplified values. * It is not required that it do so -- code correctness is not compromised by failure * to simplify */ public void simplifyOperands(Map<Operand, Operand> valueMap, boolean force) { if (getOperands() != EMPTY_OPERANDS) { System.out.println("simplifyOperands: Missing implementation for: " + this.getOperation()); throw new RuntimeException("simplifyOperands: Missing implementation for: " + this.getOperation()); } }
private static void addJumpIfNextNotDestination(CFG cfg, BasicBlock next, Instr lastInstr, BasicBlock current) { Iterator<BasicBlock> outs = cfg.getOutgoingDestinations(current).iterator(); BasicBlock target = outs.hasNext() ? outs.next() : null; if (target != null && !outs.hasNext()) { if ((target != next) && ((lastInstr == null) || !lastInstr.getOperation().transfersControl())) { current.addInstr(new JumpInstr(target.getLabel())); } } }
private static void addJumpIfNextNotDestination(CFG cfg, BasicBlock next, Instr lastInstr, BasicBlock current) { Iterator<BasicBlock> outs = cfg.getOutgoingDestinations(current).iterator(); BasicBlock target = outs.hasNext() ? outs.next() : null; if (target != null && !outs.hasNext()) { if ((target != next) && ((lastInstr == null) || !lastInstr.getOperation().transfersControl())) { current.addInstr(new JumpInstr(target.getLabel())); } } }
private static void addJumpIfNextNotDestination(CFG cfg, BasicBlock next, Instr lastInstr, BasicBlock current) { Iterator<BasicBlock> outs = cfg.getOutgoingDestinations(current).iterator(); BasicBlock target = outs.hasNext() ? outs.next() : null; if (target != null && !outs.hasNext()) { if ((target != next) && ((lastInstr == null) || !lastInstr.getOperation().transfersControl())) { current.addInstr(new JumpInstr(target.getLabel())); } } }
private static void addJumpIfNextNotDestination(CFG cfg, BasicBlock next, Instr lastInstr, BasicBlock current) { Iterator<BasicBlock> outs = cfg.getOutgoingDestinations(current).iterator(); BasicBlock target = outs.hasNext() ? outs.next() : null; if (target != null && !outs.hasNext()) { if ((target != next) && ((lastInstr == null) || !lastInstr.getOperation().transfersControl())) { current.addInstr(new JumpInstr(target.getLabel())); } } }
Operation iop = instr.getOperation(); if (iop.isCall() && !instr.isDead()) { valueMap = new HashMap<>();
public static void runLocalOptsOnInstrArray(IRScope s, Instr[] instrs) { // Reset value map if this instruction is the start/end of a basic block Map<Operand,Operand> valueMap = new HashMap<>(); Map<Variable,List<Variable>> simplificationMap = new HashMap<>(); for (int i = 0; i < instrs.length; i++) { Instr instr = instrs[i]; Instr newInstr = optInstr(s, instr, valueMap, simplificationMap); if (newInstr != instr) { instrs[i] = newInstr; } // If the call has been optimized away in the previous step, it is no longer a hard boundary for opts! // // Right now, calls are considered hard boundaries for optimization and // information cannot be propagated across them! // // SSS FIXME: Rather than treat all calls with a broad brush, what we need // is to capture different attributes about a call : // - uses closures // - known call target // - can modify scope, // - etc. // // This information is present in instruction flags on CallBase. Use it! Operation iop = instr.getOperation(); if (iop.startsBasicBlock() || iop.endsBasicBlock() || (iop.isCall() && !instr.isDead())) { valueMap = new HashMap<>(); simplificationMap = new HashMap<>(); } } }
public static void runLocalOptsOnInstrArray(IRScope s, Instr[] instrs) { // Reset value map if this instruction is the start/end of a basic block Map<Operand,Operand> valueMap = new HashMap<>(); Map<Variable,List<Variable>> simplificationMap = new HashMap<>(); for (int i = 0; i < instrs.length; i++) { Instr instr = instrs[i]; Instr newInstr = optInstr(s, instr, valueMap, simplificationMap); if (newInstr != instr) { instrs[i] = newInstr; } // If the call has been optimized away in the previous step, it is no longer a hard boundary for opts! // // Right now, calls are considered hard boundaries for optimization and // information cannot be propagated across them! // // SSS FIXME: Rather than treat all calls with a broad brush, what we need // is to capture different attributes about a call : // - uses closures // - known call target // - can modify scope, // - etc. // // This information is present in instruction flags on CallBase. Use it! Operation iop = instr.getOperation(); if (iop.startsBasicBlock() || iop.endsBasicBlock() || (iop.isCall() && !instr.isDead())) { valueMap = new HashMap<>(); simplificationMap = new HashMap<>(); } } }
public boolean canBeDeleted(IRScope s) { if (hasSideEffects() || getOperation().isDebugOp() || getOperation().canRaiseException() || transfersControl()) { return false; } else if (this instanceof ResultInstr) { Variable r = ((ResultInstr)this).getResult(); if (s.bindingHasEscaped() && !r.getName().equals(Variable.BLOCK)) { // If the binding of this scope has escaped, then we have to preserve writes to // all local variables because anyone who uses the binding might query any of the // local variables from the binding. This is safe, but extremely conservative. return !(r instanceof LocalVariable); } else if (s.usesEval() && r.getName().equals(Variable.BLOCK)) { // If this scope (or any nested scope) has any evals, then the eval might have a yield which // would use %block. In that scenario, we cannot delete the '%block = recv_closure' instruction. // This is safe, but conservative. return false; } else if (s.usesZSuper() && getOperation().isArgReceive()) { // If this scope (or any nested scope) has a ZSuperInstr, then the arguments of this // scope could be used by any of those ZSuper instructions. If so, we cannot delete // the argument receive. return false; } else { return true; } } else { return true; } }
/** * Additional metadata about this method. */ public MethodData getMethodData() { if (methodData == null) { List<String> ivarNames = new ArrayList<>(); InterpreterContext context = ensureInstrsReady(); for (Instr i : context.getInstructions()) { switch (i.getOperation()) { case GET_FIELD: ivarNames.add(((GetFieldInstr) i).getId()); break; case PUT_FIELD: ivarNames.add(((PutFieldInstr) i).getId()); break; } } methodData = new MethodData(method.getId(), method.getFile(), ivarNames); } return methodData; } }
public boolean canBeDeleted(IRScope s) { if (hasSideEffects() || getOperation().isDebugOp() || getOperation().canRaiseException() || transfersControl()) { return false; } else if (this instanceof ResultInstr) { Variable r = ((ResultInstr)this).getResult(); if (s.bindingHasEscaped() && !r.getName().equals(Variable.BLOCK)) { // If the binding of this scope has escaped, then we have to preserve writes to // all local variables because anyone who uses the binding might query any of the // local variables from the binding. This is safe, but extremely conservative. return !(r instanceof LocalVariable); } else if (s.usesEval() && r.getName().equals(Variable.BLOCK)) { // If this scope (or any nested scope) has any evals, then the eval might have a yield which // would use %block. In that scenario, we cannot delete the '%block = recv_closure' instruction. // This is safe, but conservative. return false; } else if (s.usesZSuper() && getOperation().isArgReceive()) { // If this scope (or any nested scope) has a ZSuperInstr, then the arguments of this // scope could be used by any of those ZSuper instructions. If so, we cannot delete // the argument receive. return false; } else { return true; } } else { return true; } }
/** * Additional metadata about this method. */ public MethodData getMethodData() { if (methodData == null) { List<String> ivarNames = new ArrayList<>(); InterpreterContext context = ensureInstrsReady(); for (Instr i : context.getInstructions()) { switch (i.getOperation()) { case GET_FIELD: ivarNames.add(((GetFieldInstr) i).getId()); break; case PUT_FIELD: ivarNames.add(((PutFieldInstr) i).getId()); break; } } methodData = new MethodData(method.getId(), method.getFile(), ivarNames); } return methodData; } }
@Override public void visit(Instr instr) { printAnsi(INSTR_COLOR, instr.getOperation().toString().toLowerCase()); boolean comma = false; for (Operand o : instr.getOperands()) { if (!comma) printAnsi(INSTR_COLOR, "("); if (comma) print(", "); comma = true; visit(o); } for (Field f : instr.dumpableFields()) { if (!comma) printAnsi(INSTR_COLOR, "("); if (comma) print(", "); comma = true; f.setAccessible(true); printAnsi(FIELD_COLOR, f.getName() + ": "); print(get(f, instr)); } if (comma) printAnsi(INSTR_COLOR, ")"); }
@Override public void visit(Instr instr) { printAnsi(INSTR_COLOR, instr.getOperation().toString().toLowerCase()); boolean comma = false; for (Operand o : instr.getOperands()) { if (!comma) printAnsi(INSTR_COLOR, "("); if (comma) print(", "); comma = true; visit(o); } for (Field f : instr.dumpableFields()) { if (!comma) printAnsi(INSTR_COLOR, "("); if (comma) print(", "); comma = true; f.setAccessible(true); printAnsi(FIELD_COLOR, f.getName() + ": "); print(get(f, instr)); } if (comma) printAnsi(INSTR_COLOR, ")"); }
if ((lastInstr == null) || !lastInstr.getOperation().transfersControl()) {