Commit 7fe28ae0 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Refactor some common control-flow-creation code (NFC)

parent 7e8ba275
...@@ -289,6 +289,51 @@ static ConcreteCompilerType* getTypeAtBlockStart(TypeAnalysis* types, const std: ...@@ -289,6 +289,51 @@ static ConcreteCompilerType* getTypeAtBlockStart(TypeAnalysis* types, const std:
return types->getTypeAtBlockStart(name, block); return types->getTypeAtBlockStart(name, block);
} }
llvm::Value* handlePotentiallyUndefined(ConcreteCompilerVariable* is_defined_var, llvm::Type* rtn_type,
llvm::BasicBlock*& cur_block, IREmitter& emitter, bool speculate_undefined,
std::function<llvm::Value*(IREmitter&)> when_defined,
std::function<llvm::Value*(IREmitter&)> when_undefined) {
if (!is_defined_var)
return when_defined(emitter);
assert(is_defined_var->getType() == BOOL);
llvm::Value* is_defined_i1 = i1FromBool(emitter, is_defined_var);
llvm::BasicBlock* ifdefined_block = emitter.createBasicBlock();
ifdefined_block->moveAfter(cur_block);
llvm::BasicBlock* join_block = emitter.createBasicBlock();
join_block->moveAfter(ifdefined_block);
llvm::BasicBlock* undefined_block;
llvm::Value* val_if_undefined;
if (speculate_undefined) {
val_if_undefined = when_undefined(emitter);
undefined_block = cur_block;
emitter.getBuilder()->CreateCondBr(is_defined_i1, ifdefined_block, join_block);
} else {
undefined_block = emitter.createBasicBlock();
undefined_block->moveAfter(cur_block);
emitter.getBuilder()->CreateCondBr(is_defined_i1, ifdefined_block, undefined_block);
cur_block = undefined_block;
emitter.getBuilder()->SetInsertPoint(undefined_block);
val_if_undefined = when_undefined(emitter);
emitter.getBuilder()->CreateBr(join_block);
}
cur_block = ifdefined_block;
emitter.getBuilder()->SetInsertPoint(ifdefined_block);
llvm::Value* val_if_defined = when_defined(emitter);
emitter.getBuilder()->CreateBr(join_block);
cur_block = join_block;
emitter.getBuilder()->SetInsertPoint(join_block);
auto phi = emitter.getBuilder()->CreatePHI(rtn_type, 2);
phi->addIncoming(val_if_undefined, undefined_block);
phi->addIncoming(val_if_defined, ifdefined_block);
return phi;
}
static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_guards, const GuardList& in_guards, static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_guards, const GuardList& in_guards,
TypeAnalysis* types, const OSREntryDescriptor* entry_descriptor, const BlockSet& full_blocks, TypeAnalysis* types, const OSREntryDescriptor* entry_descriptor, const BlockSet& full_blocks,
const BlockSet& partial_blocks) { const BlockSet& partial_blocks) {
...@@ -314,12 +359,12 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua ...@@ -314,12 +359,12 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
llvm_entry_blocks[block] = llvm::BasicBlock::Create(g.context, buf, irstate->getLLVMFunction()); llvm_entry_blocks[block] = llvm::BasicBlock::Create(g.context, buf, irstate->getLLVMFunction());
} }
llvm::BasicBlock* osr_entry_block = NULL; // the function entry block, where we add the type guards llvm::BasicBlock* osr_entry_block = NULL; // the function entry block, where we add the type guards
llvm::BasicBlock* osr_unbox_block = NULL; // the block after type guards where we up/down-convert things llvm::BasicBlock* osr_unbox_block_end = NULL; // the block after type guards where we up/down-convert things
ConcreteSymbolTable* osr_syms = NULL; // syms after conversion ConcreteSymbolTable* osr_syms = NULL; // syms after conversion
if (entry_descriptor != NULL) { if (entry_descriptor != NULL) {
osr_unbox_block = llvm::BasicBlock::Create(g.context, "osr_unbox", irstate->getLLVMFunction(), llvm::BasicBlock* osr_unbox_block = llvm::BasicBlock::Create(g.context, "osr_unbox", irstate->getLLVMFunction(),
&irstate->getLLVMFunction()->getEntryBlock()); &irstate->getLLVMFunction()->getEntryBlock());
osr_entry_block = llvm::BasicBlock::Create(g.context, "osr_entry", irstate->getLLVMFunction(), osr_entry_block = llvm::BasicBlock::Create(g.context, "osr_entry", irstate->getLLVMFunction(),
&irstate->getLLVMFunction()->getEntryBlock()); &irstate->getLLVMFunction()->getEntryBlock());
assert(&irstate->getLLVMFunction()->getEntryBlock() == osr_entry_block); assert(&irstate->getLLVMFunction()->getEntryBlock() == osr_entry_block);
...@@ -329,8 +374,9 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua ...@@ -329,8 +374,9 @@ 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::BranchInst::Create(llvm_entry_blocks[entry_descriptor->backedge->target->idx], entry_block);
llvm::BasicBlock* osr_entry_block_end = osr_entry_block; llvm::BasicBlock* osr_entry_block_end = osr_entry_block;
osr_unbox_block_end = osr_unbox_block;
std::unique_ptr<IREmitter> entry_emitter(createIREmitter(irstate, osr_entry_block_end)); std::unique_ptr<IREmitter> entry_emitter(createIREmitter(irstate, osr_entry_block_end));
std::unique_ptr<IREmitter> unbox_emitter(createIREmitter(irstate, osr_unbox_block)); std::unique_ptr<IREmitter> unbox_emitter(createIREmitter(irstate, osr_unbox_block_end));
CFGBlock* target_block = entry_descriptor->backedge->target; CFGBlock* target_block = entry_descriptor->backedge->target;
...@@ -411,47 +457,34 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua ...@@ -411,47 +457,34 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
assert(p.first[0] != '!'); assert(p.first[0] != '!');
std::string is_defined_name = getIsDefinedName(p.first); std::string is_defined_name = getIsDefinedName(p.first);
llvm::BasicBlock* defined_join = nullptr, * defined_prev = nullptr, * defined_check = nullptr; llvm::Value* prev_guard_val = NULL;
ConcreteCompilerVariable* is_defined_var = NULL;
if (entry_descriptor->args.count(is_defined_name)) { if (entry_descriptor->args.count(is_defined_name)) {
// relying on the fact that we are iterating over the names in order // relying on the fact that we are iterating over the names in order
// and the fake names precede the real names: // and the fake names precede the real names:
assert(osr_syms->count(is_defined_name)); assert(osr_syms->count(is_defined_name));
ConcreteCompilerVariable* is_defined_var = (*osr_syms)[is_defined_name]; is_defined_var = (*osr_syms)[is_defined_name];
assert(is_defined_var->getType() == BOOL); 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);
if (guard_val) {
guard_val = entry_emitter->getBuilder()->CreateAnd(guard_val, type_check);
} else {
guard_val = type_check;
} }
// entry_emitter->getBuilder()->CreateCall(g.funcs.my_assert, type_check);
if (defined_join) { guard_val = handlePotentiallyUndefined(
entry_emitter->getBuilder()->CreateBr(defined_join); is_defined_var, g.i1, osr_entry_block_end, *entry_emitter, true,
[speculated_class, guard_val, &p, from_arg](IREmitter& emitter) {
osr_entry_block_end = defined_join; llvm::Value* type_check = ConcreteCompilerVariable(p.second, from_arg, true)
entry_emitter->getBuilder()->SetInsertPoint(defined_join); .makeClassCheck(emitter, speculated_class);
// printf("Making osr entry guard to make sure that %s is a %s (given as a
auto guard_phi = entry_emitter->getBuilder()->CreatePHI(g.i1, 2); // %s)\n", p.first.c_str(),
guard_phi->addIncoming(getConstantInt(0, g.i1), defined_prev); // getNameOfClass(speculated_class)->c_str(),
guard_phi->addIncoming(guard_val, defined_check); // p.second->debugName().c_str());
guard_val = guard_phi; if (guard_val) {
} return emitter.getBuilder()->CreateAnd(guard_val, type_check);
} else {
return type_check;
}
},
[guard_val](IREmitter& emitter) { return getConstantInt(0, g.i1); });
if (speculated_class == int_cls) { if (speculated_class == int_cls) {
v = unbox_emitter->getBuilder()->CreateCall(g.funcs.unboxInt, from_arg); v = unbox_emitter->getBuilder()->CreateCall(g.funcs.unboxInt, from_arg);
...@@ -837,7 +870,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua ...@@ -837,7 +870,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
assert(v->isGrabbed()); assert(v->isGrabbed());
ASSERT(it->second.first == v->getType(), ""); ASSERT(it->second.first == v->getType(), "");
llvm_phi->addIncoming(v->getValue(), osr_unbox_block); llvm_phi->addIncoming(v->getValue(), osr_unbox_block_end);
} }
std::string is_defined_name = getIsDefinedName(it->first); std::string is_defined_name = getIsDefinedName(it->first);
...@@ -854,86 +887,65 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua ...@@ -854,86 +887,65 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
is_defined_var = static_cast<ConcreteCompilerVariable*>(var); is_defined_var = static_cast<ConcreteCompilerVariable*>(var);
} }
llvm::BasicBlock* defined_prev = nullptr, * defined_convert = nullptr, * defined_join = nullptr; CompilerVariable* unconverted = NULL;
if (is_defined_var) { llvm::Value* val = handlePotentiallyUndefined(
defined_prev = offramps[i]; is_defined_var, it->second.first->llvmType(), offramps[i], *emitter, true,
[=, &unconverted](IREmitter& emitter) {
defined_convert = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction()); unconverted = guard->symbol_table[it->first];
defined_join = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction()); ConcreteCompilerVariable* v;
if (unconverted->canConvertTo(it->second.first)) {
llvm::Value* is_defined_val = i1FromBool(*emitter, is_defined_var); v = unconverted->makeConverted(emitter, it->second.first);
emitter->getBuilder()->CreateCondBr(is_defined_val, defined_convert, defined_join); assert(v);
assert(v->isGrabbed());
offramps[i] = defined_convert; } else {
emitter->getBuilder()->SetInsertPoint(defined_convert); // This path is for handling the case that we did no type analysis in the previous tier,
} // but in this tier we know that even in the deopt branch with no speculations, that
// the type is more refined than what we got from the previous tier.
CompilerVariable* unconverted = guard->symbol_table[it->first]; //
ConcreteCompilerVariable* v; // We're going to blindly assume that we're right about what the type should be.
if (unconverted->canConvertTo(it->second.first)) { assert(unconverted->getType() == UNKNOWN);
v = unconverted->makeConverted(*emitter, it->second.first); assert(strcmp(bb_type, "deopt") == 0);
assert(v);
assert(v->isGrabbed()); ConcreteCompilerVariable* converted = unconverted->makeConverted(emitter, UNKNOWN);
} else {
// This path is for handling the case that we did no type analysis in the previous tier, if (it->second.first->llvmType() == g.llvm_value_type_ptr) {
// but in this tier we know that even in the deopt branch with no speculations, that v = new ConcreteCompilerVariable(it->second.first, converted->getValue(), true);
// the type is more refined than what we got from the previous tier. } else if (it->second.first == FLOAT) {
// llvm::Value* unboxed
// We're going to blindly assume that we're right about what the type should be. = emitter.getBuilder()->CreateCall(g.funcs.unboxFloat, converted->getValue());
assert(unconverted->getType() == UNKNOWN); v = new ConcreteCompilerVariable(FLOAT, unboxed, true);
assert(strcmp(bb_type, "deopt") == 0); } else if (it->second.first == INT) {
llvm::Value* unboxed
ConcreteCompilerVariable* converted = unconverted->makeConverted(*emitter, UNKNOWN); = emitter.getBuilder()->CreateCall(g.funcs.unboxInt, converted->getValue());
v = new ConcreteCompilerVariable(INT, unboxed, true);
if (it->second.first->llvmType() == g.llvm_value_type_ptr) { } else {
v = new ConcreteCompilerVariable(it->second.first, converted->getValue(), true); printf("%s\n", it->second.first->debugName().c_str());
} else if (it->second.first == FLOAT) { abort();
llvm::Value* unboxed }
= emitter->getBuilder()->CreateCall(g.funcs.unboxFloat, converted->getValue());
v = new ConcreteCompilerVariable(FLOAT, unboxed, true); converted->decvref(emitter);
} else if (it->second.first == INT) {
llvm::Value* unboxed /*
= emitter->getBuilder()->CreateCall(g.funcs.unboxInt, converted->getValue()); if (speculated_class == int_cls) {
v = new ConcreteCompilerVariable(INT, unboxed, true); v = unbox_emitter->getBuilder()->CreateCall(g.funcs.unboxInt, from_arg);
} else { (new ConcreteCompilerVariable(BOXED_INT, from_arg, true))->decvref(*unbox_emitter);
printf("%s\n", it->second.first->debugName().c_str()); } else if (speculated_class == float_cls) {
abort(); v = unbox_emitter->getBuilder()->CreateCall(g.funcs.unboxFloat, from_arg);
} (new ConcreteCompilerVariable(BOXED_FLOAT, from_arg, true))->decvref(*unbox_emitter);
} else {
converted->decvref(*emitter); assert(phi_type == typeFromClass(speculated_class));
v = from_arg;
/* }
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);
} else if (speculated_class == float_cls) {
v = unbox_emitter->getBuilder()->CreateCall(g.funcs.unboxFloat, from_arg);
(new ConcreteCompilerVariable(BOXED_FLOAT, from_arg, true))->decvref(*unbox_emitter);
} else {
assert(phi_type == typeFromClass(speculated_class));
v = from_arg;
}
*/
}
ASSERT(it->second.first == v->getType(), "");
assert(it->second.first->llvmType() == v->getValue()->getType());
llvm::Value* val = v->getValue();
if (defined_prev) {
emitter->getBuilder()->CreateBr(defined_join);
auto prev = offramps[i];
offramps[i] = defined_join; ASSERT(it->second.first == v->getType(), "");
emitter->getBuilder()->SetInsertPoint(defined_join); assert(it->second.first->llvmType() == v->getValue()->getType());
auto phi = emitter->getBuilder()->CreatePHI(it->second.first->llvmType(), 2); return v->getValue();
phi->addIncoming(llvm::UndefValue::get(phi->getType()), defined_prev); },
phi->addIncoming(val, prev); [=](IREmitter& emitter) { return llvm::UndefValue::get(it->second.first->llvmType()); });
val = phi;
}
phi_args.emplace_back(llvm_phi, val, offramps[i]); phi_args.emplace_back(llvm_phi, val, offramps[i]);
......
...@@ -68,6 +68,7 @@ public: ...@@ -68,6 +68,7 @@ public:
virtual IRBuilder* getBuilder() = 0; virtual IRBuilder* getBuilder() = 0;
virtual GCBuilder* getGC() = 0; virtual GCBuilder* getGC() = 0;
virtual CompiledFunction* currentFunction() = 0; virtual CompiledFunction* currentFunction() = 0;
virtual llvm::BasicBlock* createBasicBlock(const char* name = "") = 0;
virtual llvm::Value* getScratch(int num_bytes) = 0; virtual llvm::Value* getScratch(int num_bytes) = 0;
virtual void releaseScratch(llvm::Value*) = 0; virtual void releaseScratch(llvm::Value*) = 0;
...@@ -96,6 +97,25 @@ bool isIsDefinedName(const std::string& name); ...@@ -96,6 +97,25 @@ bool isIsDefinedName(const std::string& name);
CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_descriptor, CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_descriptor,
EffortLevel::EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix); EffortLevel::EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix);
// A common pattern is to branch based off whether a variable is defined but only if it is
// potentially-undefined. If it is potentially-undefined, we have to generate control-flow
// that branches on the is_defined variable and then generate different code on those two paths;
// if the variable is guaranteed to be defined, we just want to emit the when_defined version.
//
// I suppose we could always emit both and let the LLVM optimizer fix it up for us, but for now
// do it the hard (and hopefully faster) way.
//
// - is_defined_var is allowed to be NULL, signifying that the variable is always defined.
// Otherwise it should be a BOOL variable that signifies if the variable is defined or not.
// - speculate_undefined means whether or not we should execute the when_undefined code generator
// in the current block (the one that we're in when calling this function); if set to true we will
// avoid generating a BB for the undefined case, which is useful if the "codegen" just returns
// an existing value or a constant.
llvm::Value* handlePotentiallyUndefined(ConcreteCompilerVariable* is_defined_var, llvm::Type* rtn_type,
llvm::BasicBlock*& cur_block, IREmitter& emitter, bool speculate_undefined,
std::function<llvm::Value*(IREmitter&)> when_defined,
std::function<llvm::Value*(IREmitter&)> when_undefined);
class TypeRecorder; class TypeRecorder;
class OpInfo { class OpInfo {
private: private:
......
...@@ -118,6 +118,10 @@ private: ...@@ -118,6 +118,10 @@ private:
} }
} }
llvm::BasicBlock* createBasicBlock(const char* name) override {
return llvm::BasicBlock::Create(g.context, name, irstate->getLLVMFunction());
}
llvm::CallSite emitPatchpoint(llvm::Type* return_type, const ICSetupInfo* pp, llvm::Value* func, llvm::CallSite emitPatchpoint(llvm::Type* return_type, const ICSetupInfo* pp, llvm::Value* func,
const std::vector<llvm::Value*>& args, const std::vector<llvm::Value*>& args,
const std::vector<llvm::Value*>& ic_stackmap_args, UnwindInfo unw_info) { const std::vector<llvm::Value*>& ic_stackmap_args, UnwindInfo unw_info) {
...@@ -837,33 +841,15 @@ private: ...@@ -837,33 +841,15 @@ private:
if (is_defined_var) { if (is_defined_var) {
// classdefs have different scoping rules than functions: // classdefs have different scoping rules than functions:
if (irstate->getSourceInfo()->ast->type == AST_TYPE::ClassDef) { if (irstate->getSourceInfo()->ast->type == AST_TYPE::ClassDef) {
llvm::BasicBlock* from_local llvm::Value* v = handlePotentiallyUndefined(
= llvm::BasicBlock::Create(g.context, "from_local", irstate->getLLVMFunction()); is_defined_var, g.llvm_value_type_ptr, curblock, emitter, false,
llvm::BasicBlock* from_global [=](IREmitter& emitter) {
= llvm::BasicBlock::Create(g.context, "from_global", irstate->getLLVMFunction()); CompilerVariable* local = symbol_table[node->id];
llvm::BasicBlock* join = llvm::BasicBlock::Create(g.context, "join", irstate->getLLVMFunction()); return local->makeConverted(emitter, local->getBoxType())->getValue();
},
emitter.getBuilder()->CreateCondBr(i1FromBool(emitter, is_defined_var), from_local, from_global); [=](IREmitter& emitter) { return _getGlobal(node, unw_info)->getValue(); });
emitter.getBuilder()->SetInsertPoint(from_local); return new ConcreteCompilerVariable(UNKNOWN, v, true);
curblock = from_local;
CompilerVariable* local = symbol_table[node->id];
ConcreteCompilerVariable* converted_local = local->makeConverted(emitter, local->getBoxType());
// don't decvref local here, because are manufacturing a new vref
emitter.getBuilder()->CreateBr(join);
emitter.getBuilder()->SetInsertPoint(from_global);
curblock = from_global;
ConcreteCompilerVariable* global = _getGlobal(node, unw_info);
emitter.getBuilder()->CreateBr(join);
emitter.getBuilder()->SetInsertPoint(join);
curblock = join;
llvm::PHINode* phi = emitter.getBuilder()->CreatePHI(g.llvm_value_type_ptr, 2, node->id);
phi->addIncoming(converted_local->getValue(), from_local);
phi->addIncoming(global->getValue(), from_global);
return new ConcreteCompilerVariable(UNKNOWN, phi, true);
} }
emitter.createCall(unw_info, g.funcs.assertNameDefined, emitter.createCall(unw_info, g.funcs.assertNameDefined,
......
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