package org.jruby.ir;
import org.jruby.ParseResult;
import org.jruby.RubyInstanceConfig;
import org.jruby.RubyModule;
import org.jruby.RubySymbol;
import org.jruby.compiler.Compilable;
import org.jruby.ir.dataflow.analyses.LiveVariablesProblem;
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem;
import org.jruby.ir.dataflow.analyses.UnboxableOpsAnalysisProblem;
import org.jruby.ir.instructions.*;
import org.jruby.ir.interpreter.FullInterpreterContext;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.operands.*;
import org.jruby.ir.operands.Float;
import org.jruby.ir.passes.*;
import org.jruby.ir.persistence.IRWriterEncoder;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.representations.CFG;
import org.jruby.ir.transformations.inlining.CFGInliner;
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
import org.jruby.ir.util.IGVDumper;
import org.jruby.parser.StaticScope;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicInteger;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.util.ByteList;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
import static org.jruby.ir.IRFlags.*;
public abstract class IRScope implements ParseResult {
public static final Logger LOG = LoggerFactory.getLogger(IRScope.class);
private static final Collection<IRClosure> NO_CLOSURES = Collections.EMPTY_LIST;
private static final AtomicInteger globalScopeCount = new AtomicInteger();
private final int scopeId;
private ByteList name;
private final int lineNumber;
private IRScope lexicalParent;
private List<IRClosure> nestedClosures;
private int nextClosureIndex;
private List<IRScope> lexicalChildren;
private final StaticScope staticScope;
protected InterpreterContext interpreterContext;
protected FullInterpreterContext fullInterpreterContext;
protected FullInterpreterContext optimizedInterpreterContext;
protected int temporaryVariableIndex;
private int nextLabelIndex = 0;
Map<RubySymbol, LocalVariable> localVars;
final EnumSet<IRFlags> flags;
private IRManager manager;
private boolean alreadyHasInline;
private String inlineFailed;
public Compilable compilable;
protected IRScope(IRScope s, IRScope lexicalParent) {
this.lexicalParent = lexicalParent;
this.manager = s.manager;
this.lineNumber = s.lineNumber;
this.staticScope = s.staticScope;
this.nextClosureIndex = s.nextClosureIndex;
this.temporaryVariableIndex = s.temporaryVariableIndex;
this.interpreterContext = null;
this.flags = s.flags.clone();
this.localVars = new HashMap<>(s.localVars);
this.scopeId = globalScopeCount.getAndIncrement();
setupLexicalContainment();
}
public IRScope(IRManager manager, IRScope lexicalParent, ByteList name, int lineNumber, StaticScope staticScope) {
this.manager = manager;
this.lexicalParent = lexicalParent;
this.name = name;
this.lineNumber = lineNumber;
this.staticScope = staticScope;
this.nextClosureIndex = 0;
this.temporaryVariableIndex = -1;
this.interpreterContext = null;
this.flags = DEFAULT_SCOPE_FLAGS.clone();
if (parentMaybeUsingRefinements()) flags.add(MAYBE_USING_REFINEMENTS);
this.localVars = new HashMap<>(1);
this.scopeId = globalScopeCount.getAndIncrement();
setupLexicalContainment();
}
private void setupLexicalContainment() {
if (manager.isDryRun() || RubyInstanceConfig.IR_WRITING || RubyInstanceConfig.RECORD_LEXICAL_HIERARCHY) {
lexicalChildren = new ArrayList<>(1);
if (lexicalParent != null) lexicalParent.addChildScope(this);
}
}
public int getScopeId() {
return scopeId;
}
@Override
public int hashCode() {
return scopeId;
}
public void setInterpreterContext(InterpreterContext interpreterContext) {
this.interpreterContext = interpreterContext;
}
@Override
public boolean equals(Object other) {
return (other != null) && (getClass() == other.getClass()) && (scopeId == ((IRScope) other).scopeId);
}
protected void addChildScope(IRScope scope) {
if (lexicalChildren == null) lexicalChildren = new ArrayList<>(1);
lexicalChildren.add(scope);
}
public List<IRScope> getLexicalScopes() {
if (lexicalChildren == null) lexicalChildren = new ArrayList<>(1);
return lexicalChildren;
}
public void addClosure(IRClosure closure) {
if (nestedClosures == null) nestedClosures = new ArrayList<>(1);
nestedClosures.add(closure);
}
public void removeClosure(IRClosure closure) {
if (nestedClosures != null) nestedClosures.remove(closure);
}
public Label getNewLabel(String prefix) {
return new Label(prefix, nextLabelIndex++);
}
public Label getNewLabel() {
return getNewLabel("LBL");
}
public Collection<IRClosure> getClosures() {
return nestedClosures == null ? NO_CLOSURES : nestedClosures;
}
public IRManager getManager() {
return manager;
}
public void setIsMaybeUsingRefinements() {
flags.add(MAYBE_USING_REFINEMENTS);
}
public boolean parentMaybeUsingRefinements() {
for (IRScope s = this; s != null; s = s.getLexicalParent()) {
if (s.getFlags().contains(MAYBE_USING_REFINEMENTS)) return true;
if (s instanceof IREvalScript) return false;
}
return false;
}
public boolean maybeUsingRefinements() {
return getFlags().contains(MAYBE_USING_REFINEMENTS);
}
public IRScope getLexicalParent() {
return lexicalParent;
}
public StaticScope getStaticScope() {
return staticScope;
}
public boolean isWithinEND() {
for (IRScope current = this; current != null && current instanceof IRClosure; current = current.getLexicalParent()) {
if (((IRClosure) current).isEND()) return true;
}
return false;
}
public IRMethod getNearestMethod() {
IRScope current = this;
while (current != null && !(current instanceof IRMethod)) {
current = current.getLexicalParent();
}
return (IRMethod) current;
}
public IRScope getNearestTopLocalVariableScope() {
IRScope current = this;
while (current != null && !current.isTopLocalVariableScope()) {
current = current.getLexicalParent();
}
return current;
}
public boolean isScopeContainedBy(IRScope parentScope) {
IRScope current = this;
while (current != null) {
if (parentScope == current) return true;
current = current.getLexicalParent();
}
return false;
}
public int getNearestModuleReferencingScopeDepth() {
int n = 0;
IRScope current = this;
while (!(current instanceof IRModuleBody)) {
if (current == null || current instanceof IREvalScript) return -1;
current = current.getLexicalParent();
if (!(current instanceof IRFor)) n++;
}
return n;
}
public String getId() {
return getName().idString();
}
public RubySymbol getName() {
return getManager().getRuntime().newSymbol(name);
}
public ByteList getByteName() {
return name;
}
public void setByteName(ByteList name) {
this.name = name;
}
public void setFileName(String filename) {
getRootLexicalScope().setFileName(filename);
}
@Deprecated
public String getFileName() {
return getFile();
}
public String getFile() {
return getRootLexicalScope().getFile();
}
@Deprecated
public int getLineNumber() {
return lineNumber;
}
public int getLine() {
return lineNumber;
}
public IRScope getRootLexicalScope() {
IRScope current = this;
for (; current != null && !current.isScriptScope(); current = current.getLexicalParent()) {}
return current;
}
public boolean isNestedInClosure(IRClosure closure) {
for (IRScope s = this; s != null && !s.isTopLocalVariableScope(); s = s.getLexicalParent()) {
if (s == closure) return true;
}
return false;
}
public void setHasLoopsFlag() {
flags.add(HAS_LOOPS);
}
public boolean hasLoops() {
return flags.contains(HAS_LOOPS);
}
public boolean hasExplicitCallProtocol() {
return flags.contains(HAS_EXPLICIT_CALL_PROTOCOL);
}
public void setExplicitCallProtocolFlag() {
flags.add(HAS_EXPLICIT_CALL_PROTOCOL);
}
public boolean receivesKeywordArgs() {
return flags.contains(RECEIVES_KEYWORD_ARGS);
}
public boolean bindingHasEscaped() {
return flags.contains(BINDING_HAS_ESCAPED);
}
public boolean usesEval() {
return flags.contains(USES_EVAL);
}
public boolean usesZSuper() {
return flags.contains(USES_ZSUPER);
}
public boolean canReceiveNonlocalReturns() {
computeScopeFlags();
return flags.contains(CAN_RECEIVE_NONLOCAL_RETURNS);
}
public void putLiveVariablesProblem(LiveVariablesProblem problem) {
if (fullInterpreterContext == null) {
if (problem != null) throw new IllegalStateException("LVP being stored when no FIC");
return;
}
fullInterpreterContext.getDataFlowProblems().put(LiveVariablesProblem.NAME, problem);
}
public LiveVariablesProblem getLiveVariablesProblem() {
if (fullInterpreterContext == null) return null;
return (LiveVariablesProblem) fullInterpreterContext.getDataFlowProblems().get(LiveVariablesProblem.NAME);
}
public void putStoreLocalVarPlacementProblem(StoreLocalVarPlacementProblem problem) {
if (fullInterpreterContext == null) {
if (problem != null) throw new IllegalStateException("StoreLocalVarPlacementProblem being stored when no FIC");
return;
}
fullInterpreterContext.getDataFlowProblems().put(StoreLocalVarPlacementProblem.NAME, problem);
}
public StoreLocalVarPlacementProblem getStoreLocalVarPlacementProblem() {
if (fullInterpreterContext == null) return null;
return (StoreLocalVarPlacementProblem) fullInterpreterContext.getDataFlowProblems().get(StoreLocalVarPlacementProblem.NAME);
}
public void putUnboxableOpsAnalysisProblem(UnboxableOpsAnalysisProblem problem) {
if (fullInterpreterContext == null) {
if (problem != null) throw new IllegalStateException("UboxableOpsAnalysisProblem being stored when no FIC");
return;
}
fullInterpreterContext.getDataFlowProblems().put(UnboxableOpsAnalysisProblem.NAME, problem);
}
public UnboxableOpsAnalysisProblem getUnboxableOpsAnalysisProblem() {
if (fullInterpreterContext == null) return null;
return (UnboxableOpsAnalysisProblem) fullInterpreterContext.getDataFlowProblems().get(UnboxableOpsAnalysisProblem.NAME);
}
public CFG getCFG() {
if (getOptimizedInterpreterContext() != null) {
return getOptimizedInterpreterContext().getCFG();
}
if (getFullInterpreterContext() == null) prepareFullBuildCommon();
return fullInterpreterContext.getCFG();
}
public List<CompilerPass> getExecutedPasses() {
return fullInterpreterContext == null ? new ArrayList<CompilerPass>(1) : fullInterpreterContext.getExecutedPasses();
}
private void runCompilerPasses(List<CompilerPass> passes, IGVDumper dumper) {
if (dumper != null) dumper.dump(getCFG(), "Start");
CompilerPassScheduler scheduler = IRManager.schedulePasses(passes);
for (CompilerPass pass : scheduler) {
pass.run(this);
if (dumper != null) dumper.dump(getCFG(), pass.getShortLabel());
}
if (RubyInstanceConfig.IR_UNBOXING) {
CompilerPass pass = new UnboxingPass();
pass.run(this);
if (dumper != null) dumper.dump(getCFG(), pass.getShortLabel());
}
if (dumper != null) dumper.close();
}
public InterpreterContext allocateInterpreterContext(List<Instr> instructions) {
interpreterContext = new InterpreterContext(this, instructions);
if (RubyInstanceConfig.IR_COMPILER_DEBUG) LOG.info(interpreterContext.toString());
return interpreterContext;
}
public InterpreterContext allocateInterpreterContext(Callable<List<Instr>> instructions) {
interpreterContext = new InterpreterContext(this, instructions);
if (RubyInstanceConfig.IR_COMPILER_DEBUG) LOG.info(interpreterContext.toString());
return interpreterContext;
}
private Instr[] cloneInstrs() {
SimpleCloneInfo cloneInfo = new SimpleCloneInfo(this, false);
Instr[] instructions = interpreterContext.getInstructions();
int length = instructions.length;
Instr[] newInstructions = new Instr[length];
for (int i = 0; i < length; i++) {
newInstructions[i] = instructions[i].clone(cloneInfo);
}
return newInstructions;
}
private void prepareFullBuildCommon() {
if (fullInterpreterContext != null) return;
fullInterpreterContext = new FullInterpreterContext(this, cloneInstrs());
}
public synchronized FullInterpreterContext prepareFullBuild() {
if (optimizedInterpreterContext != null) return optimizedInterpreterContext;
if (fullInterpreterContext != null && fullInterpreterContext.buildComplete()) return fullInterpreterContext;
for (IRScope scope: getClosures()) {
scope.prepareFullBuild();
}
prepareFullBuildCommon();
runCompilerPasses(getManager().getCompilerPasses(this), dumpToIGV());
getManager().optimizeIfSimpleScope(this);
new AddCallProtocolInstructions().run(this);
fullInterpreterContext.generateInstructionsForInterpretation();
return fullInterpreterContext;
}
public String getFullyQualifiedName() {
if (getLexicalParent() == null) return getId();
return getLexicalParent().getFullyQualifiedName() + "::" + getId();
}
public IGVDumper dumpToIGV() {
if (RubyInstanceConfig.IR_DEBUG_IGV != null) {
String spec = RubyInstanceConfig.IR_DEBUG_IGV;
if (spec.contains(":") && spec.equals(getFileName() + ":" + getLineNumber()) ||
spec.equals(getFileName())) {
return new IGVDumper(getFullyQualifiedName() + "; line " + getLineNumber());
}
}
return null;
}
public synchronized BasicBlock[] prepareForCompilation() {
if (optimizedInterpreterContext != null && optimizedInterpreterContext.buildComplete()) {
return optimizedInterpreterContext.getLinearizedBBList();
}
if (fullInterpreterContext != null && fullInterpreterContext.buildComplete()) return fullInterpreterContext.getLinearizedBBList();
for (IRScope scope: getClosures()) {
scope.prepareForCompilation();
}
prepareFullBuildCommon();
runCompilerPasses(getManager().getJITPasses(this), dumpToIGV());
BasicBlock[] bbs = fullInterpreterContext.linearizeBasicBlocks();
return bbs;
}
public Map<BasicBlock, Label> buildJVMExceptionTable() {
Map<BasicBlock, Label> map = new HashMap<>(1);
for (BasicBlock bb: fullInterpreterContext.getLinearizedBBList()) {
BasicBlock rescueBB = getCFG().getRescuerBBFor(bb);
if (rescueBB != null) {
map.put(bb, rescueBB.getLabel());
}
}
return map;
}
public EnumSet<IRFlags> getFlags() {
return flags;
}
private void initScopeFlags() {
flags.remove(CAN_CAPTURE_CALLERS_BINDING);
flags.remove(CAN_RECEIVE_BREAKS);
flags.remove(CAN_RECEIVE_NONLOCAL_RETURNS);
flags.remove(HAS_BREAK_INSTRS);
flags.remove(HAS_NONLOCAL_RETURNS);
flags.remove(USES_ZSUPER);
flags.remove(USES_EVAL);
flags.remove(REQUIRES_DYNSCOPE);
flags.remove(REQUIRES_LASTLINE);
flags.remove(REQUIRES_BACKREF);
flags.remove(REQUIRES_VISIBILITY);
flags.remove(REQUIRES_BLOCK);
flags.remove(REQUIRES_SELF);
flags.remove(REQUIRES_METHODNAME);
flags.remove(REQUIRES_LINE);
flags.remove(REQUIRES_CLASS);
flags.remove(REQUIRES_FILENAME);
flags.remove(REQUIRES_SCOPE);
}
private void bindingEscapedScopeFlagsCheck() {
if (this instanceof IREvalScript || this instanceof IRScriptBody) {
flags.add(BINDING_HAS_ESCAPED);
} else {
flags.remove(BINDING_HAS_ESCAPED);
}
}
private void calculateClosureScopeFlags() {
for (IRClosure cl: getClosures()) {
cl.computeScopeFlags();
if (cl.usesEval()) {
flags.add(CAN_RECEIVE_BREAKS);
flags.add(CAN_RECEIVE_NONLOCAL_RETURNS);
flags.add(USES_ZSUPER);
} else {
if (cl.flags.contains(HAS_BREAK_INSTRS) || cl.flags.contains(CAN_RECEIVE_BREAKS)) {
flags.add(CAN_RECEIVE_BREAKS);
}
if (cl.flags.contains(HAS_NONLOCAL_RETURNS) || cl.flags.contains(CAN_RECEIVE_NONLOCAL_RETURNS)) {
flags.add(CAN_RECEIVE_NONLOCAL_RETURNS);
}
if (cl.usesZSuper()) {
flags.add(USES_ZSUPER);
}
}
}
}
private static final EnumSet<IRFlags> DEFAULT_SCOPE_FLAGS =
EnumSet.of(CAN_CAPTURE_CALLERS_BINDING, BINDING_HAS_ESCAPED, USES_EVAL, REQUIRES_BACKREF,
REQUIRES_LASTLINE, REQUIRES_DYNSCOPE, USES_ZSUPER);
private static final EnumSet<IRFlags> NEEDS_DYNAMIC_SCOPE_FLAGS =
EnumSet.of(CAN_RECEIVE_BREAKS, HAS_NONLOCAL_RETURNS, CAN_RECEIVE_NONLOCAL_RETURNS, BINDING_HAS_ESCAPED);
private void computeNeedsDynamicScopeFlag() {
for (IRFlags f : NEEDS_DYNAMIC_SCOPE_FLAGS) {
if (flags.contains(f)) {
flags.add(REQUIRES_DYNSCOPE);
return;
}
}
}
public void computeScopeFlagsEarly(List<Instr> instructions) {
initScopeFlags();
bindingEscapedScopeFlagsCheck();
for (Instr i : instructions) {
i.computeScopeFlags(this);
}
calculateClosureScopeFlags();
computeNeedsDynamicScopeFlag();
flags.add(FLAGS_COMPUTED);
}
public void computeScopeFlags() {
if (flags.contains(FLAGS_COMPUTED)) return;
initScopeFlags();
bindingEscapedScopeFlagsCheck();
if (fullInterpreterContext != null) {
fullInterpreterContext.computeScopeFlagsFromInstructions();
} else {
interpreterContext.computeScopeFlagsFromInstructions();
}
calculateClosureScopeFlags();
computeNeedsDynamicScopeFlag();
flags.add(FLAGS_COMPUTED);
}
public abstract IRScopeType getScopeType();
@Override
public String toString() {
return String.valueOf(getScopeType()) + ' ' + getId() + '[' + getFile() + ':' + getLine() + "]<" + toStringCompileForm() + ">";
}
public String toStringCompileForm() {
return optimizedInterpreterContext != null ? "optimized" : fullInterpreterContext != null ? "full" : "startup";
}
public String debugOutput() {
return toStringInstrs();
}
public String toStringInstrs() {
if (fullInterpreterContext != null) {
return "Instructions:\n" + fullInterpreterContext.toStringInstrs();
} else {
return interpreterContext.toStringInstrs();
}
}
public Variable getSelf() {
return Self.SELF;
}
public Variable createCurrentModuleVariable() {
temporaryVariableIndex++;
return TemporaryCurrentModuleVariable.ModuleVariableFor(temporaryVariableIndex);
}
public Variable createCurrentScopeVariable() {
temporaryVariableIndex++;
return TemporaryCurrentScopeVariable.ScopeVariableFor(temporaryVariableIndex);
}
public Map<RubySymbol, LocalVariable> getLocalVariables() {
return localVars;
}
public Set<LocalVariable> getUsedLocalVariables() {
return getFullInterpreterContext().getUsedLocalVariables();
}
public void setLocalVariables(Map<RubySymbol, LocalVariable> variables) {
this.localVars = variables;
}
public void setNextLabelIndex(int index) {
nextLabelIndex = index;
}
public int getNextLabelIndex() {
return nextLabelIndex;
}
public LocalVariable lookupExistingLVar(RubySymbol name) {
return localVars.get(name);
}
protected LocalVariable findExistingLocalVariable(RubySymbol name, int depth) {
return localVars.get(name);
}
public LocalVariable getLocalVariable(RubySymbol name, int scopeDepth) {
LocalVariable lvar = findExistingLocalVariable(name, scopeDepth);
if (lvar == null) {
lvar = getNewLocalVariable(name, scopeDepth);
} else if (lvar.getScopeDepth() != scopeDepth) {
lvar = lvar.cloneForDepth(scopeDepth);
}
return lvar;
}
public LocalVariable getNewLocalVariable(RubySymbol name, int scopeDepth) {
assert scopeDepth == 0: "Scope depth is non-zero for new-var request " + name + " in " + this;
LocalVariable lvar = new LocalVariable(name, scopeDepth, getStaticScope().addVariable(name.idString()));
localVars.put(name, lvar);
return lvar;
}
public TemporaryLocalVariable createTemporaryVariable() {
return getNewTemporaryVariable(TemporaryVariableType.LOCAL);
}
public TemporaryLocalVariable getNewTemporaryVariableFor(LocalVariable var) {
temporaryVariableIndex++;
return new TemporaryLocalReplacementVariable(var.getId(), temporaryVariableIndex);
}
public TemporaryLocalVariable getNewTemporaryVariable(TemporaryVariableType type) {
switch (type) {
case FLOAT: {
getFullInterpreterContext().floatVariableIndex++;
return new TemporaryFloatVariable(getFullInterpreterContext().floatVariableIndex);
}
case FIXNUM: {
getFullInterpreterContext().fixnumVariableIndex++;
return new TemporaryFixnumVariable(getFullInterpreterContext().fixnumVariableIndex);
}
case BOOLEAN: {
getFullInterpreterContext().booleanVariableIndex++;
return new TemporaryBooleanVariable(getFullInterpreterContext().booleanVariableIndex);
}
case LOCAL: {
temporaryVariableIndex++;
return manager.newTemporaryLocalVariable(temporaryVariableIndex);
}
}
throw new RuntimeException("Invalid temporary variable being alloced in this scope: " + type);
}
public void setTemporaryVariableCount(int count) {
temporaryVariableIndex = count + 1;
}
public TemporaryLocalVariable getNewUnboxedVariable(Class type) {
TemporaryVariableType varType;
if (type == Float.class) {
varType = TemporaryVariableType.FLOAT;
} else if (type == Fixnum.class) {
varType = TemporaryVariableType.FIXNUM;
} else if (type == java.lang.Boolean.class) {
varType = TemporaryVariableType.BOOLEAN;
} else {
varType = TemporaryVariableType.LOCAL;
}
return getNewTemporaryVariable(varType);
}
public int getTemporaryVariablesCount() {
return temporaryVariableIndex + 1;
}
public Variable getNewInlineVariable(ByteList inlinePrefix, Variable v) {
return createTemporaryVariable();
}
public int getLocalVariablesCount() {
return localVars.size();
}
public boolean usesLocalVariable(Variable v) {
return getFullInterpreterContext().usesLocalVariable(v);
}
public boolean definesLocalVariable(Variable v) {
return getFullInterpreterContext().definesLocalVariable(v);
}
public boolean hasBeenBuilt() {
return true;
}
public FullInterpreterContext getExecutionContext() {
return fullInterpreterContext;
}
public InterpreterContext getInterpreterContext() {
return interpreterContext;
}
public FullInterpreterContext getFullInterpreterContext() {
return fullInterpreterContext;
}
public FullInterpreterContext getOptimizedInterpreterContext() {
return optimizedInterpreterContext;
}
protected void depends(Object obj) {
assert obj != null: "Unsatisfied dependency and this depends() was set " +
"up wrong. Use depends(build()) not depends(build).";
}
public void resetState() {
interpreterContext = null;
fullInterpreterContext = null;
flags.remove(FLAGS_COMPUTED);
flags.add(CAN_CAPTURE_CALLERS_BINDING);
flags.add(BINDING_HAS_ESCAPED);
flags.add(USES_EVAL);
flags.add(USES_ZSUPER);
flags.remove(HAS_BREAK_INSTRS);
flags.remove(HAS_NONLOCAL_RETURNS);
flags.remove(CAN_RECEIVE_BREAKS);
flags.remove(CAN_RECEIVE_NONLOCAL_RETURNS);
FullInterpreterContext fic = getFullInterpreterContext();
if (fic != null) {
int i = 0;
while (i < fic.getExecutedPasses().size()) {
if (!fic.getExecutedPasses().get(i).invalidate(this)) {
i++;
}
}
}
}
private FullInterpreterContext inlineFailed(String reason) {
inlineFailed = reason;
return null;
}
private FullInterpreterContext inlineMethodCommon(IRMethod methodToInline, long callsiteId, int classToken, boolean cloneHost) {
alreadyHasInline = true;
if (getFullInterpreterContext() == null) return inlineFailed("inline into startup interpreter scope");
if (!methodToInline.getClosures().isEmpty()) return inlineFailed("inline a method which contains nested closures");
FullInterpreterContext newContext = getFullInterpreterContext().duplicate();
BasicBlock basicBlock = newContext.findBasicBlockOf(callsiteId);
CallBase call = (CallBase) basicBlock.siteOf(callsiteId);
RubyModule implClass = compilable.getImplementationClass();
String error = new CFGInliner(newContext).inlineMethod(methodToInline, implClass, classToken, basicBlock, call, cloneHost);
return error == null ? newContext : inlineFailed(error);
}
public void inlineMethod(IRMethod methodToInline, long callsiteId, int classToken, boolean cloneHost) {
if (alreadyHasInline) return;
FullInterpreterContext newContext = inlineMethodCommon(methodToInline, callsiteId, classToken, cloneHost);
if (newContext == null) {
if (IRManager.IR_INLINER_VERBOSE) LOG.info("Inline of " + methodToInline + " into " + this + " failed: " + inlineFailed + ".");
return;
} else {
if (IRManager.IR_INLINER_VERBOSE) LOG.info("Inline of " + methodToInline + " into " + this + " succeeded.");
}
newContext.generateInstructionsForInterpretation();
this.optimizedInterpreterContext = newContext;
manager.getRuntime().getJITCompiler().getTaskFor(manager.getRuntime().getCurrentContext(), compilable).run();
}
public void inlineMethodJIT(IRMethod methodToInline, long callsiteId, int classToken, boolean cloneHost) {
if (alreadyHasInline) return;
FullInterpreterContext newContext = inlineMethodCommon(methodToInline, callsiteId, classToken, cloneHost);
if (newContext == null) {
if (IRManager.IR_INLINER_VERBOSE) LOG.info("Inline of " + methodToInline + " into " + this + " failed: " + inlineFailed + ".");
return;
} else {
if (IRManager.IR_INLINER_VERBOSE) LOG.info("Inline of " + methodToInline + " into " + this + " succeeded.");
}
newContext.linearizeBasicBlocks();
this.optimizedInterpreterContext = newContext;
manager.getRuntime().getJITCompiler().getTaskFor(manager.getRuntime().getCurrentContext(), compilable).run();
}
public void inlineMethodCompiled(IRMethod methodToInline, long callsiteId, int classToken, boolean cloneHost) {
if (alreadyHasInline) return;
FullInterpreterContext newContext = inlineMethodCommon(methodToInline, callsiteId, classToken, cloneHost);
if (newContext == null) {
if (IRManager.IR_INLINER_VERBOSE) LOG.info("Inline of " + methodToInline + " into " + this + " failed: " + inlineFailed + ".");
return;
} else {
if (IRManager.IR_INLINER_VERBOSE) LOG.info("Inline of " + methodToInline + " into " + this + " succeeded.");
}
newContext.linearizeBasicBlocks();
this.optimizedInterpreterContext = newContext;
manager.getRuntime().getJITCompiler().getTaskFor(manager.getRuntime().getCurrentContext(), compilable).run();
}
public int getNextClosureId() {
nextClosureIndex++;
return nextClosureIndex;
}
public boolean isModuleBody() {
return false;
}
public boolean isNonSingletonClassBody() {
return false;
}
public boolean isTopLocalVariableScope() {
return true;
}
public boolean isScriptScope() {
return false;
}
public boolean needsFrame() {
boolean bindingHasEscaped = bindingHasEscaped();
boolean requireFrame = bindingHasEscaped || usesEval();
for (IRFlags flag : getFlags()) {
switch (flag) {
case BINDING_HAS_ESCAPED:
case CAN_CAPTURE_CALLERS_BINDING:
case REQUIRES_LASTLINE:
case REQUIRES_BACKREF:
case REQUIRES_VISIBILITY:
case REQUIRES_BLOCK:
case REQUIRES_SELF:
case REQUIRES_METHODNAME:
case REQUIRES_CLASS:
case USES_EVAL:
case USES_ZSUPER:
requireFrame = true;
}
}
return requireFrame;
}
public boolean needsOnlyBackref() {
boolean backrefSeen = false;
for (IRFlags flag : getFlags()) {
switch (flag) {
case BINDING_HAS_ESCAPED:
case CAN_CAPTURE_CALLERS_BINDING:
case REQUIRES_LASTLINE:
case REQUIRES_VISIBILITY:
case REQUIRES_BLOCK:
case REQUIRES_SELF:
case REQUIRES_METHODNAME:
case REQUIRES_CLASS:
case USES_EVAL:
case USES_ZSUPER:
return false;
case REQUIRES_BACKREF:
backrefSeen = true;
break;
}
}
return backrefSeen;
}
public boolean reuseParentScope() {
return getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE);
}
public boolean needsBinding() {
return reuseParentScope() || !getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED);
}
public boolean inliningAllowed() {
return !alreadyHasInline;
}
public void captureParentRefinements(ThreadContext context) {
if (maybeUsingRefinements()) {
for (IRScope cur = this.getLexicalParent(); cur != null; cur = cur.getLexicalParent()) {
RubyModule overlay = cur.staticScope.getOverlayModuleForRead();
if (overlay != null && !overlay.getRefinements().isEmpty()) {
RubyModule myOverlay = staticScope.getOverlayModuleForWrite(context);
myOverlay.getRefinementsForWrite().putAll(overlay.getRefinements());
break;
}
}
}
}
public void cleanupAfterExecution() {
}
public boolean executesOnce() {
return false;
}
public void (IRWriterEncoder file) {
if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("IRScopeType = " + getScopeType());
file.encode(getScopeType());
if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("Line # = " + getLine());
file.encode(getLine());
if (RubyInstanceConfig.IR_WRITING_DEBUG) System.out.println("# of temp vars = " + getTemporaryVariablesCount());
file.encode(getTemporaryVariablesCount());
file.encode(getNextLabelIndex());
}
}