package org.jruby.ir.interpreter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRFlags;
import org.jruby.ir.IRScope;
import org.jruby.ir.dataflow.DataFlowProblem;
import org.jruby.ir.instructions.Instr;
import org.jruby.ir.instructions.LabelInstr;
import org.jruby.ir.instructions.ReceiveSelfInstr;
import org.jruby.ir.instructions.ResultInstr;
import org.jruby.ir.instructions.Site;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.passes.CompilerPass;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.representations.CFG;
import org.jruby.ir.representations.CFGLinearizer;
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
public class FullInterpreterContext extends InterpreterContext {
private CFG cfg;
private BasicBlock[] linearizedBBList = null;
private Map<String, DataFlowProblem> dataFlowProblems;
private List<CompilerPass> executedPasses = new ArrayList<>();
private Set<LocalVariable> definedLocalVars;
private Set<LocalVariable> usedLocalVars;
public int floatVariableIndex = -1;
public int fixnumVariableIndex = -1;
public int booleanVariableIndex = -1;
public FullInterpreterContext(IRScope scope, CFG cfg, BasicBlock[] linearizedBBList) {
super(scope, (List<Instr>) null);
this.cfg = cfg;
this.linearizedBBList = linearizedBBList;
}
public FullInterpreterContext(IRScope scope, Instr[] instructions) {
super(scope, (List<Instr>)null);
cfg = buildCFG(instructions);
}
@Override
public boolean buildComplete() {
return linearizedBBList != null;
}
public BasicBlock[] linearizeBasicBlocks() {
linearizedBBList = CFGLinearizer.linearize(cfg);
return linearizedBBList;
}
private CFG buildCFG(Instr[] instructions) {
CFG newCFG = new CFG(getScope());
newCFG.build(instructions);
return newCFG;
}
@Override
public boolean hasExplicitCallProtocol() {
return getScope().getFlags().contains(IRFlags.HAS_EXPLICIT_CALL_PROTOCOL);
}
@Override
public boolean pushNewDynScope() {
return !getScope().getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) && !reuseParentDynScope();
}
@Override
public boolean popDynScope() {
return pushNewDynScope() || reuseParentDynScope();
}
@Override
public boolean reuseParentDynScope() {
return getScope().getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
}
public void generateInstructionsForInterpretation() {
linearizeBasicBlocks();
List<Instr> newInstrs = new ArrayList<>();
int ipc = 0;
for (BasicBlock b: getLinearizedBBList()) {
b.getLabel().setTargetPC(ipc);
List<Instr> bbInstrs = b.getInstrs();
int bbInstrsLength = bbInstrs.size();
for (int i = 0; i < bbInstrsLength; i++) {
Instr instr = bbInstrs.get(i);
if (!(instr instanceof ReceiveSelfInstr)) {
if (instr instanceof LabelInstr) ((LabelInstr) instr).getLabel().setTargetPC(ipc);
newInstrs.add(instr);
ipc++;
}
}
}
cfg.getExitBB().getLabel().setTargetPC(ipc + 1);
Instr[] linearizedInstrArray = newInstrs.toArray(new Instr[newInstrs.size()]);
BasicBlock[] basicBlocks = getLinearizedBBList();
rescueIPCs = new int[2 * basicBlocks.length];
ipc = 0;
for (int i = 0; i < basicBlocks.length; i++) {
BasicBlock bb = basicBlocks[i];
BasicBlock rescuerBB = cfg.getRescuerBBFor(bb);
int rescuerPC = rescuerBB == null ? -1 : rescuerBB.getLabel().getTargetPC();
rescueIPCs[i * 2] = ipc + bb.getInstrs().size();
rescueIPCs[i * 2 + 1] = rescuerPC;
for (Instr instr : bb.getInstrs()) {
if (!(instr instanceof ReceiveSelfInstr)) {
ipc++;
} else {
rescueIPCs[i * 2]--;
}
}
}
instructions = linearizedInstrArray;
temporaryVariableCount = getScope().getTemporaryVariablesCount();
}
@Override
public CFG getCFG() {
return cfg;
}
@Override
public void computeScopeFlagsFromInstructions() {
for (BasicBlock b: cfg.getBasicBlocks()) {
for (Instr i: b.getInstrs()) {
i.computeScopeFlags(getScope());
}
}
}
public Map<String, DataFlowProblem> getDataFlowProblems() {
if (dataFlowProblems == null) dataFlowProblems = new HashMap<>();
return dataFlowProblems;
}
public List<CompilerPass> getExecutedPasses() {
return executedPasses;
}
public BasicBlock[] getLinearizedBBList() {
return linearizedBBList;
}
@Override
public String toStringInstrs() {
return "\nCFG:\n" + cfg.toStringGraph() + "\nInstructions:\n" + cfg.toStringInstrs();
}
public String toStringLinearized() {
StringBuilder buf = new StringBuilder();
for (BasicBlock bb: getLinearizedBBList()) {
buf.append(bb + bb.toStringInstrs());
}
return buf.toString();
}
public FullInterpreterContext duplicate() {
try {
CFG newCFG = cfg.clone(new SimpleCloneInfo(getScope(), false, true), getScope());
BasicBlock[] newLinearizedBBList = new BasicBlock[linearizedBBList.length];
for (int i = 0; i < linearizedBBList.length; i++) {
newLinearizedBBList[i] = newCFG.getBBForLabel(linearizedBBList[i].getLabel());
}
return new FullInterpreterContext(getScope(), newCFG, newLinearizedBBList);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
public int determineRPC(int ipc) {
int length = rescueIPCs.length;
for (int i = 0; i + 1 < length; i += 2) {
if (ipc <= rescueIPCs[i]) return rescueIPCs[i + 1];
}
throw new RuntimeException("BUG: no RPC found for " + getFileName() + ":" + getName() + ":" + ipc);
}
public BasicBlock findBasicBlockOf(long callsiteId) {
for (BasicBlock basicBlock: linearizeBasicBlocks()) {
for (Instr instr: basicBlock.getInstrs()) {
if (instr instanceof Site) {
Site site = (Site) instr;
if (site.getCallSiteId() == callsiteId) return basicBlock;
}
}
}
throw new RuntimeException("Bug: Looking for callsiteId: " + callsiteId + " in " + this);
}
public Set<LocalVariable> getUsedLocalVariables() {
return usedLocalVars;
}
public void setUpUseDefLocalVarMaps() {
definedLocalVars = new HashSet<>(1);
usedLocalVars = new HashSet<>(1);
for (BasicBlock bb : getCFG().getBasicBlocks()) {
for (Instr i : bb.getInstrs()) {
for (Variable v : i.getUsedVariables()) {
if (v instanceof LocalVariable) usedLocalVars.add((LocalVariable) v);
}
if (i instanceof ResultInstr) {
Variable v = ((ResultInstr) i).getResult();
if (v instanceof LocalVariable && !((LocalVariable)v).isOuterScopeVar()) {
definedLocalVars.add((LocalVariable) v);
}
}
}
}
for (IRClosure cl : getScope().getClosures()) {
cl.getFullInterpreterContext().setUpUseDefLocalVarMaps();
}
}
public boolean usesLocalVariable(Variable v) {
if (usedLocalVars == null) setUpUseDefLocalVarMaps();
if (usedLocalVars.contains(v)) return true;
for (IRClosure cl : getScope().getClosures()) {
if (cl.getFullInterpreterContext().usesLocalVariable(v)) return true;
}
return false;
}
public boolean definesLocalVariable(Variable v) {
if (definedLocalVars == null) setUpUseDefLocalVarMaps();
if (definedLocalVars.contains(v)) return true;
for (IRClosure cl : getScope().getClosures()) {
if (cl.getFullInterpreterContext().definesLocalVariable(v)) return true;
}
return false;
}
}