Commit a3f136e1 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Bash the existing OSR code into submission for the new AST interpreter

Once we get the AST interpreter in, we can add resume-from-any-point
functionality, which means that we can get rid of the current OSR system.
Maybe we should have done that first, but I thought it would be easy
to get the OSR stuff working.
parent a8606801
......@@ -459,7 +459,13 @@ bool PhiAnalysis::isPotentiallyUndefinedAfter(const std::string& name, CFGBlock*
if (block->successors.size() != 1)
return false;
for (CFGBlock* pred : block->successors[0]->predecessors) {
return isPotentiallyUndefinedAt(name, block->successors[0]);
}
bool PhiAnalysis::isPotentiallyUndefinedAt(const std::string& name, CFGBlock* block) {
assert(!startswith(name, "!"));
for (CFGBlock* pred : block->predecessors) {
DefinednessAnalysis::DefinitionLevel dlevel = definedness.isDefinedAtEnd(name, pred);
if (dlevel != DefinednessAnalysis::Defined)
return true;
......
......@@ -84,8 +84,9 @@ class PhiAnalysis {
public:
typedef std::unordered_set<std::string> RequiredSet;
private:
DefinednessAnalysis definedness;
private:
LivenessAnalysis* liveness;
std::unordered_map<CFGBlock*, const RequiredSet> required_phis;
......@@ -97,6 +98,7 @@ public:
const RequiredSet& getAllRequiredAfter(CFGBlock* block);
const RequiredSet& getAllRequiredFor(CFGBlock* block);
bool isPotentiallyUndefinedAfter(const std::string& name, CFGBlock* block);
bool isPotentiallyUndefinedAt(const std::string& name, CFGBlock* block);
};
LivenessAnalysis* computeLivenessInfo(CFG*);
......
......@@ -110,8 +110,8 @@ void gatherInterpreterRoots(GCVisitor* visitor) {
ASTInterpreter::ASTInterpreter(CompiledFunction* compiled_function)
: source_info(compiled_function->clfunc->source), next_block(0), current_block(0), current_inst(0),
last_exception(0), passed_closure(0), created_closure(0), generator(0), scope_info(0), compiled_func(0),
edgecount(0) {
last_exception(0), passed_closure(0), created_closure(0), generator(0), scope_info(0),
compiled_func(compiled_function), edgecount(0) {
CLFunction* f = compiled_function->clfunc;
if (!source_info->cfg)
source_info->cfg = computeCFG(f->source, f->source->body);
......@@ -283,17 +283,47 @@ Value ASTInterpreter::visit_branch(AST_Branch* node) {
Value ASTInterpreter::visit_jump(AST_Jump* node) {
if (ENABLE_OSR && node->target->idx < current_block->idx && compiled_func) {
++edgecount;
if (0 && edgecount > 10) {
if (edgecount > 10) {
eraseDeadSymbols();
OSRExit exit(compiled_func, OSREntryDescriptor::create(compiled_func, node));
std::map<std::string, Box*> sorted_symbol_table;
for (auto& l : sym_table)
sorted_symbol_table[l.getKey()] = l.getValue();
auto phis = compiled_func->clfunc->source->phis;
for (auto& name : phis->definedness.getDefinedNamesAtEnd(current_block)) {
auto it = sym_table.find(name);
if (!compiled_func->clfunc->source->liveness->isLiveAtEnd(name, current_block))
continue;
if (phis->isPotentiallyUndefinedAfter(name, current_block)) {
bool is_defined = it != sym_table.end();
sorted_symbol_table["!is_defined_" + name] = (Box*)is_defined;
sorted_symbol_table[name] = is_defined ? it->getValue() : NULL;
} else {
ASSERT(it != sym_table.end(), "%s", name.c_str());
sorted_symbol_table[it->getKey()] = it->getValue();
}
}
if (generator)
sorted_symbol_table["!passed_generator"] = generator; // TODO use PASSED_GENERATOR_NAME instead
if (passed_closure)
sorted_symbol_table["!passed_closure"] = passed_closure; // TODO use PASSED_CLOSURE_NAME instead
if (created_closure)
sorted_symbol_table["!created_closure"] = created_closure; // TODO use CREATED_CLOSURE_NAME instead
std::vector<Box*> arg_array;
for (auto& it : sorted_symbol_table) {
if (startswith(it.first, "!is_defined"))
exit.entry->args[it.first] = BOOL;
else if (it.first == "!passed_generator") // TODO use PASSED_GENERATOR_NAME
exit.entry->args[it.first] = GENERATOR;
else if (it.first == "!passed_closure" || it.first == "!created_closure") // TODO don't hardcode
exit.entry->args[it.first] = CLOSURE;
else
exit.entry->args[it.first] = UNKNOWN;
arg_array.push_back(it.second);
}
......@@ -303,12 +333,6 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
Box* arg2 = arg_array.size() >= 2 ? arg_array[1] : 0;
Box* arg3 = arg_array.size() >= 3 ? arg_array[2] : 0;
Box** args = arg_array.size() >= 4 ? &arg_array[3] : 0;
if (passed_closure && generator)
return partial_func->closure_generator_call(passed_closure, generator, arg1, arg2, arg3, args);
else if (passed_closure)
return partial_func->closure_call(passed_closure, arg1, arg2, arg3, args);
else if (generator)
return partial_func->generator_call(generator, arg1, arg2, arg3, args);
return partial_func->call(arg1, arg2, arg3, args);
}
}
......@@ -460,13 +484,17 @@ Box* ASTInterpreter::createFunction(AST* node, AST_arguments* args, const std::v
defaults.push_back(visit_expr(d).o);
defaults.push_back(0);
// FIXME: Using initializer_list is pretty annoying since you're not supposed to create them:
union {
struct {
Box** ptr;
size_t s;
} d;
std::initializer_list<Box*> il = {};
} u;
d.ptr = &defaults[0];
d.s = defaults.size() - 1;
u.d.ptr = &defaults[0];
u.d.s = defaults.size() - 1;
ScopeInfo* scope_info_node = source_info->scoping->getScopeInfoForNode(node);
bool is_generator = scope_info_node->takesGenerator();
......@@ -482,7 +510,7 @@ Box* ASTInterpreter::createFunction(AST* node, AST_arguments* args, const std::v
assert(closure);
}
return boxCLFunction(cl, closure, is_generator, *(std::initializer_list<Box*>*)(void*)&d);
return boxCLFunction(cl, closure, is_generator, u.il);
}
Value ASTInterpreter::visit_functionDef(AST_FunctionDef* node) {
......
......@@ -329,9 +329,8 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
// llvm::BranchInst::Create(llvm_entry_blocks[entry_descriptor->backedge->target->idx], entry_block);
llvm::BasicBlock* osr_entry_block_end = osr_entry_block;
llvm::BasicBlock* osr_unbox_block_end = osr_unbox_block;
std::unique_ptr<IREmitter> entry_emitter(createIREmitter(irstate, osr_entry_block_end));
std::unique_ptr<IREmitter> unbox_emitter(createIREmitter(irstate, osr_unbox_block_end));
std::unique_ptr<IREmitter> unbox_emitter(createIREmitter(irstate, osr_unbox_block));
CFGBlock* target_block = entry_descriptor->backedge->target;
......@@ -409,8 +408,29 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
}
ASSERT(speculated_class, "%s", phi_type->debugName().c_str());
ASSERT(entry_descriptor->args.count("!is_defined_" + p.first) == 0,
"This class-check-creating behavior will segfault if the argument wasn't actually defined!");
assert(p.first[0] != '!');
std::string is_defined_name = "!is_defined_" + p.first;
llvm::BasicBlock* defined_join = nullptr, * defined_prev = nullptr, * defined_check = nullptr;
if (entry_descriptor->args.count(is_defined_name)) {
// relying on the fact that we are iterating over the names in order
// and the fake names precede the real names:
assert(osr_syms->count(is_defined_name));
ConcreteCompilerVariable* is_defined_var = (*osr_syms)[is_defined_name];
assert(is_defined_var->getType() == BOOL);
llvm::Value* is_defined_i1 = i1FromBool(*entry_emitter, is_defined_var);
defined_check = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
defined_join = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
llvm::BranchInst* br
= entry_emitter->getBuilder()->CreateCondBr(is_defined_i1, defined_check, defined_join);
defined_prev = osr_entry_block_end;
osr_entry_block_end = defined_check;
entry_emitter->getBuilder()->SetInsertPoint(defined_check);
}
llvm::Value* type_check = ConcreteCompilerVariable(p.second, from_arg, true)
.makeClassCheck(*entry_emitter, speculated_class);
......@@ -421,6 +441,18 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
}
// entry_emitter->getBuilder()->CreateCall(g.funcs.my_assert, type_check);
if (defined_join) {
entry_emitter->getBuilder()->CreateBr(defined_join);
osr_entry_block_end = defined_join;
entry_emitter->getBuilder()->SetInsertPoint(defined_join);
auto guard_phi = entry_emitter->getBuilder()->CreatePHI(g.i1, 2);
guard_phi->addIncoming(getConstantInt(0, g.i1), defined_prev);
guard_phi->addIncoming(guard_val, defined_check);
guard_val = guard_phi;
}
if (speculated_class == int_cls) {
v = unbox_emitter->getBuilder()->CreateCall(g.funcs.unboxInt, from_arg);
(new ConcreteCompilerVariable(BOXED_INT, from_arg, true))->decvref(*unbox_emitter);
......@@ -621,25 +653,32 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
into_hax.insert(b2);
}
const PhiAnalysis::RequiredSet& names = source->phis->getAllRequiredFor(block);
std::unordered_set<std::string> names;
for (const auto& s : source->phis->getAllRequiredFor(block)) {
names.insert(s);
if (source->phis->isPotentiallyUndefinedAfter(s, block->predecessors[0])) {
names.insert("!is_defined_" + s);
}
}
if (source->getScopeInfo()->createsClosure())
names.insert(CREATED_CLOSURE_NAME);
if (source->getScopeInfo()->takesClosure())
names.insert(PASSED_CLOSURE_NAME);
if (source->getScopeInfo()->takesGenerator())
names.insert(PASSED_GENERATOR_NAME);
for (const auto& s : names) {
// printf("adding guessed phi for %s\n", s.c_str());
ConcreteCompilerType* type = types->getTypeAtBlockStart(s, block);
ConcreteCompilerType* type = getTypeAtBlockStart(types, s, block);
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(type->llvmType(), block->predecessors.size(), s);
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(type, phi, true);
generator->giveLocalSymbol(s, var);
(*phis)[s] = std::make_pair(type, phi);
if (source->phis->isPotentiallyUndefinedAfter(s, block->predecessors[0])) {
std::string is_defined_name = "!is_defined_" + s;
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(BOOL->llvmType(), block->predecessors.size(),
is_defined_name);
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(BOOL, phi, true);
generator->giveLocalSymbol(is_defined_name, var);
(*phis)[is_defined_name] = std::make_pair(BOOL, phi);
}
}
} else {
assert(pred);
......@@ -746,7 +785,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
if (full_blocks.count(b2) == 0 && partial_blocks.count(b2) == 0)
continue;
// printf("%d %d %ld %ld\n", i, b2->idx, phi_ending_symbol_tables[b2]->size(), phis->size());
// printf("%d %d %ld %ld\n", b->idx, b2->idx, phi_ending_symbol_tables[b2]->size(), phis->size());
compareKeyset(phi_ending_symbol_tables[b2], phis);
assert(phi_ending_symbol_tables[b2]->size() == phis->size());
}
......@@ -762,13 +801,18 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
llvm::BasicBlock* off_ramp = llvm::BasicBlock::Create(g.context, "deopt_ramp", irstate->getLLVMFunction());
offramps.push_back(off_ramp);
llvm::BasicBlock* off_ramp_end = off_ramp;
IREmitter* emitter = createIREmitter(irstate, off_ramp_end);
IREmitter* emitter = createIREmitter(irstate, offramps[offramps.size() - 1]);
emitters.push_back(emitter);
block_guards[i]->branch->setSuccessor(1, off_ramp);
}
// Can't always add the phi incoming value right away, since we may have to create more
// basic blocks as part of type coercion.
// Intsead, just make a record of the phi node, value, and the location of the from-BB,
// which we won't read until after all new BBs have been added.
std::vector<std::tuple<llvm::PHINode*, llvm::Value*, llvm::BasicBlock*&> > phi_args;
for (PHITable::iterator it = phis->begin(); it != phis->end(); it++) {
llvm::PHINode* llvm_phi = it->second.second;
for (int j = 0; j < b->predecessors.size(); j++) {
......@@ -796,12 +840,33 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
llvm_phi->addIncoming(v->getValue(), osr_unbox_block);
}
std::string is_defined_name = "!is_defined_" + it->first;
for (int i = 0; i < block_guards.size(); i++) {
GuardList::BlockEntryGuard* guard = block_guards[i];
IREmitter* emitter = emitters[i];
ASSERT(phis->count("!is_defined_" + it->first) == 0,
"This class-check-creating behavior will segfault if the argument wasn't actually defined!");
auto is_defined_it = guard->symbol_table.find(is_defined_name);
ConcreteCompilerVariable* is_defined_var = nullptr;
if (is_defined_it != guard->symbol_table.end()) {
auto var = is_defined_it->second;
assert(var->getType() == BOOL);
is_defined_var = static_cast<ConcreteCompilerVariable*>(var);
}
llvm::BasicBlock* defined_prev = nullptr, * defined_convert = nullptr, * defined_join = nullptr;
if (is_defined_var) {
defined_prev = offramps[i];
defined_convert = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
defined_join = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
llvm::Value* is_defined_val = i1FromBool(*emitter, is_defined_var);
emitter->getBuilder()->CreateCondBr(is_defined_val, defined_convert, defined_join);
offramps[i] = defined_convert;
emitter->getBuilder()->SetInsertPoint(defined_convert);
}
CompilerVariable* unconverted = guard->symbol_table[it->first];
ConcreteCompilerVariable* v;
......@@ -854,13 +919,31 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
ASSERT(it->second.first == v->getType(), "");
assert(it->second.first->llvmType() == v->getValue()->getType());
llvm_phi->addIncoming(v->getValue(), offramps[i]);
llvm::Value* val = v->getValue();
if (defined_prev) {
emitter->getBuilder()->CreateBr(defined_join);
auto prev = offramps[i];
offramps[i] = defined_join;
emitter->getBuilder()->SetInsertPoint(defined_join);
auto phi = emitter->getBuilder()->CreatePHI(it->second.first->llvmType(), 2);
phi->addIncoming(llvm::UndefValue::get(phi->getType()), defined_prev);
phi->addIncoming(val, prev);
val = phi;
}
phi_args.emplace_back(llvm_phi, val, offramps[i]);
// TODO not sure if this is right:
unconverted->decvref(*emitter);
delete v;
}
}
for (auto t : phi_args) {
std::get<0>(t)->addIncoming(std::get<1>(t), std::get<2>(t));
}
for (int i = 0; i < block_guards.size(); i++) {
emitters[i]->getBuilder()->CreateBr(llvm_entry_blocks[b]);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment