Commit 21b20e3b authored by Kevin Modzelewski's avatar Kevin Modzelewski

Generate cxx "fixups" on-demand

fixups aka the stubs that decref whatever's needed when an exception is thrown

I looked into this because most (75%?) of the refcounting overhead
comes from the cxx fixups.  Previously we would always generate them in
the IRGenerator, regardless of whether they were needed.  Now they are
generated in the refcounter, which knows whether they are needed or not.

Unfortunately it looks like they are usually needed, so the gains here
aren't that great (saves about 10% llvm instructions whereas cxx fixups
in general added about 400% more llvm instructions).

I think this is still a good change because it's also necessary in order to use
Marius's EH stuff.

I think the cost of the fixups is mostly related to the cost of the decrefs
that it adds, so even though most of the refcounting overhead seems to be due to
adding the cxx fixups, reducing general decref overhead might reduce cxx fixup overhead
parent 7b95f4d5
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#ifndef PYSTON_CODEGEN_IRGEN_H #ifndef PYSTON_CODEGEN_IRGEN_H
#define PYSTON_CODEGEN_IRGEN_H #define PYSTON_CODEGEN_IRGEN_H
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/ExecutionEngine/ObjectCache.h" #include "llvm/ExecutionEngine/ObjectCache.h"
#include "llvm/IR/CallSite.h" #include "llvm/IR/CallSite.h"
...@@ -216,13 +217,16 @@ private: ...@@ -216,13 +217,16 @@ private:
llvm::DenseMap<llvm::Instruction*, llvm::SmallVector<llvm::Value*, 4>> refs_consumed; llvm::DenseMap<llvm::Instruction*, llvm::SmallVector<llvm::Value*, 4>> refs_consumed;
llvm::DenseMap<llvm::Instruction*, llvm::SmallVector<llvm::Value*, 4>> refs_used; llvm::DenseMap<llvm::Instruction*, llvm::SmallVector<llvm::Value*, 4>> refs_used;
llvm::ValueMap<llvm::Value*, RefcountState> vars; llvm::ValueMap<llvm::Value*, RefcountState> vars;
llvm::DenseSet<llvm::Instruction*> may_throw;
public: public:
llvm::Value* setType(llvm::Value* v, RefType reftype); llvm::Value* setType(llvm::Value* v, RefType reftype);
llvm::Value* setNullable(llvm::Value* v, bool nullable = true); llvm::Value* setNullable(llvm::Value* v, bool nullable = true);
void refConsumed(llvm::Value* v, llvm::Instruction*); void refConsumed(llvm::Value* v, llvm::Instruction*);
void refUsed(llvm::Value* v, llvm::Instruction*); void refUsed(llvm::Value* v, llvm::Instruction*);
void setMayThrow(llvm::Instruction*);
static void addRefcounts(IRGenState* state); static void addRefcounts(IRGenState* state);
bool isNullable(llvm::Value* v);
}; };
} }
......
...@@ -447,7 +447,7 @@ private: ...@@ -447,7 +447,7 @@ private:
if (unw_info.exc_dest == NO_CXX_INTERCEPTION) { if (unw_info.exc_dest == NO_CXX_INTERCEPTION) {
needs_cxx_interception = false; needs_cxx_interception = false;
} else { } else {
bool needs_refcounting_fixup = true; bool needs_refcounting_fixup = false;
needs_cxx_interception = (target_exception_style == CXX && (needs_refcounting_fixup || unw_info.hasHandler() needs_cxx_interception = (target_exception_style == CXX && (needs_refcounting_fixup || unw_info.hasHandler()
|| irstate->getExceptionStyle() == CAPI)); || irstate->getExceptionStyle() == CAPI));
} }
...@@ -487,6 +487,9 @@ private: ...@@ -487,6 +487,9 @@ private:
} else { } else {
llvm::CallInst* cs = getBuilder()->CreateCall(callee, args); llvm::CallInst* cs = getBuilder()->CreateCall(callee, args);
if (target_exception_style == CXX)
irstate->getRefcounts()->setMayThrow(cs);
if (target_exception_style == CAPI) if (target_exception_style == CAPI)
checkAndPropagateCapiException(unw_info, cs, capi_exc_value); checkAndPropagateCapiException(unw_info, cs, capi_exc_value);
...@@ -1035,7 +1038,8 @@ private: ...@@ -1035,7 +1038,8 @@ private:
auto inst = emitter.createCall(UnwindInfo::cantUnwind(), g.funcs.setFrameExcInfo, auto inst = emitter.createCall(UnwindInfo::cantUnwind(), g.funcs.setFrameExcInfo,
{ frame_info, converted_type->getValue(), converted_value->getValue(), { frame_info, converted_type->getValue(), converted_value->getValue(),
converted_traceback->getValue() }); converted_traceback->getValue() },
NOEXC);
emitter.refConsumed(converted_type->getValue(), inst); emitter.refConsumed(converted_type->getValue(), inst);
emitter.refConsumed(converted_value->getValue(), inst); emitter.refConsumed(converted_value->getValue(), inst);
emitter.refConsumed(converted_traceback->getValue(), inst); emitter.refConsumed(converted_traceback->getValue(), inst);
...@@ -3065,31 +3069,8 @@ public: ...@@ -3065,31 +3069,8 @@ public:
emitter.getBuilder()->SetInsertPoint(cxx_exc_dest); emitter.getBuilder()->SetInsertPoint(cxx_exc_dest);
llvm::Function* _personality_func = g.stdlib_module->getFunction("__gxx_personality_v0"); llvm::Value* exc_type, *exc_value, *exc_traceback;
assert(_personality_func); std::tie(exc_type, exc_value, exc_traceback) = createLandingpad(cxx_exc_dest);
llvm::Value* personality_func
= g.cur_module->getOrInsertFunction(_personality_func->getName(), _personality_func->getFunctionType());
assert(personality_func);
llvm::LandingPadInst* landing_pad = emitter.getBuilder()->CreateLandingPad(
llvm::StructType::create(std::vector<llvm::Type*>{ g.i8_ptr, g.i64 }), personality_func, 1);
landing_pad->addClause(getNullPtr(g.i8_ptr));
llvm::Value* cxaexc_pointer = emitter.getBuilder()->CreateExtractValue(landing_pad, { 0 });
llvm::Function* std_module_catch = g.stdlib_module->getFunction("__cxa_begin_catch");
auto begin_catch_func
= g.cur_module->getOrInsertFunction(std_module_catch->getName(), std_module_catch->getFunctionType());
assert(begin_catch_func);
llvm::Value* excinfo_pointer = emitter.getBuilder()->CreateCall(begin_catch_func, cxaexc_pointer);
llvm::Value* excinfo_pointer_casted
= emitter.getBuilder()->CreateBitCast(excinfo_pointer, g.llvm_excinfo_type->getPointerTo());
auto* builder = emitter.getBuilder();
llvm::Value* exc_type = builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 0));
llvm::Value* exc_value = builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 1));
llvm::Value* exc_traceback
= builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 2));
emitter.setType(exc_type, RefType::OWNED); emitter.setType(exc_type, RefType::OWNED);
emitter.setType(exc_value, RefType::OWNED); emitter.setType(exc_value, RefType::OWNED);
emitter.setType(exc_traceback, RefType::OWNED); emitter.setType(exc_traceback, RefType::OWNED);
...@@ -3101,23 +3082,24 @@ public: ...@@ -3101,23 +3082,24 @@ public:
new ConcreteCompilerVariable(UNKNOWN, exc_value), new ConcreteCompilerVariable(UNKNOWN, exc_value),
new ConcreteCompilerVariable(UNKNOWN, exc_traceback))); new ConcreteCompilerVariable(UNKNOWN, exc_traceback)));
builder->CreateBr(final_dest); emitter.getBuilder()->CreateBr(final_dest);
} else if (irstate->getExceptionStyle() == CAPI) { } else if (irstate->getExceptionStyle() == CAPI) {
auto call_inst = builder->CreateCall3(g.funcs.PyErr_Restore, exc_type, exc_value, exc_traceback); auto call_inst
= emitter.getBuilder()->CreateCall3(g.funcs.PyErr_Restore, exc_type, exc_value, exc_traceback);
irstate->getRefcounts()->refConsumed(exc_type, call_inst); irstate->getRefcounts()->refConsumed(exc_type, call_inst);
irstate->getRefcounts()->refConsumed(exc_value, call_inst); irstate->getRefcounts()->refConsumed(exc_value, call_inst);
irstate->getRefcounts()->refConsumed(exc_traceback, call_inst); irstate->getRefcounts()->refConsumed(exc_traceback, call_inst);
builder->CreateCall(g.funcs.deinitFrame, irstate->getFrameInfoVar()); emitter.getBuilder()->CreateCall(g.funcs.deinitFrame, irstate->getFrameInfoVar());
builder->CreateRet(getNullPtr(g.llvm_value_type_ptr)); emitter.getBuilder()->CreateRet(getNullPtr(g.llvm_value_type_ptr));
} else { } else {
// auto call_inst = emitter.createCall3(UnwindInfo(unw_info.current_stmt, NO_CXX_INTERCEPTION), // auto call_inst = emitter.createCall3(UnwindInfo(unw_info.current_stmt, NO_CXX_INTERCEPTION),
// g.funcs.rawThrow, exc_type, exc_value, exc_traceback); // g.funcs.rawReraise, exc_type, exc_value, exc_traceback);
auto call_inst = emitter.getBuilder()->CreateCall3(g.funcs.rawThrow, exc_type, exc_value, exc_traceback); auto call_inst = emitter.getBuilder()->CreateCall3(g.funcs.rawReraise, exc_type, exc_value, exc_traceback);
irstate->getRefcounts()->refConsumed(exc_type, call_inst); irstate->getRefcounts()->refConsumed(exc_type, call_inst);
irstate->getRefcounts()->refConsumed(exc_value, call_inst); irstate->getRefcounts()->refConsumed(exc_value, call_inst);
irstate->getRefcounts()->refConsumed(exc_traceback, call_inst); irstate->getRefcounts()->refConsumed(exc_traceback, call_inst);
builder->CreateUnreachable(); emitter.getBuilder()->CreateUnreachable();
} }
emitter.setCurrentBasicBlock(orig_block); emitter.setCurrentBasicBlock(orig_block);
...@@ -3135,6 +3117,37 @@ public: ...@@ -3135,6 +3117,37 @@ public:
} }
}; };
std::tuple<llvm::Value*, llvm::Value*, llvm::Value*> createLandingpad(llvm::BasicBlock* bb) {
assert(bb->begin() == bb->end());
llvm::IRBuilder<true> builder(bb);
llvm::Function* _personality_func = g.stdlib_module->getFunction("__gxx_personality_v0");
assert(_personality_func);
llvm::Value* personality_func
= g.cur_module->getOrInsertFunction(_personality_func->getName(), _personality_func->getFunctionType());
assert(personality_func);
llvm::LandingPadInst* landing_pad = builder.CreateLandingPad(
llvm::StructType::create(std::vector<llvm::Type*>{ g.i8_ptr, g.i64 }), personality_func, 1);
landing_pad->addClause(getNullPtr(g.i8_ptr));
llvm::Value* cxaexc_pointer = builder.CreateExtractValue(landing_pad, { 0 });
llvm::Function* std_module_catch = g.stdlib_module->getFunction("__cxa_begin_catch");
auto begin_catch_func
= g.cur_module->getOrInsertFunction(std_module_catch->getName(), std_module_catch->getFunctionType());
assert(begin_catch_func);
llvm::Value* excinfo_pointer = builder.CreateCall(begin_catch_func, cxaexc_pointer);
llvm::Value* excinfo_pointer_casted = builder.CreateBitCast(excinfo_pointer, g.llvm_excinfo_type->getPointerTo());
llvm::Value* exc_type = builder.CreateLoad(builder.CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 0));
llvm::Value* exc_value = builder.CreateLoad(builder.CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 1));
llvm::Value* exc_traceback = builder.CreateLoad(builder.CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 2));
return std::make_tuple(exc_type, exc_value, exc_traceback);
}
IRGenerator* createIRGenerator(IRGenState* irstate, std::unordered_map<CFGBlock*, llvm::BasicBlock*>& entry_blocks, IRGenerator* createIRGenerator(IRGenState* irstate, std::unordered_map<CFGBlock*, llvm::BasicBlock*>& entry_blocks,
CFGBlock* myblock, TypeAnalysis* types) { CFGBlock* myblock, TypeAnalysis* types) {
return new IRGeneratorImpl(irstate, entry_blocks, myblock, types); return new IRGeneratorImpl(irstate, entry_blocks, myblock, types);
......
...@@ -172,6 +172,8 @@ public: ...@@ -172,6 +172,8 @@ public:
virtual CFGBlock* getCFGBlock() = 0; virtual CFGBlock* getCFGBlock() = 0;
}; };
std::tuple<llvm::Value*, llvm::Value*, llvm::Value*> createLandingpad(llvm::BasicBlock*);
class IREmitter; class IREmitter;
class AST_Call; class AST_Call;
IREmitter* createIREmitter(IRGenState* irstate, llvm::BasicBlock*& curblock, IRGenerator* irgenerator = NULL); IREmitter* createIREmitter(IRGenState* irstate, llvm::BasicBlock*& curblock, IRGenerator* irgenerator = NULL);
......
...@@ -70,6 +70,11 @@ llvm::Value* RefcountTracker::setNullable(llvm::Value* v, bool nullable) { ...@@ -70,6 +70,11 @@ llvm::Value* RefcountTracker::setNullable(llvm::Value* v, bool nullable) {
return v; return v;
} }
bool RefcountTracker::isNullable(llvm::Value* v) {
assert(vars.count(v));
return vars.lookup(v).nullable;
}
void RefcountTracker::refConsumed(llvm::Value* v, llvm::Instruction* inst) { void RefcountTracker::refConsumed(llvm::Value* v, llvm::Instruction* inst) {
assert(this->vars[v].reftype != RefType::UNKNOWN); assert(this->vars[v].reftype != RefType::UNKNOWN);
...@@ -82,6 +87,12 @@ void RefcountTracker::refUsed(llvm::Value* v, llvm::Instruction* inst) { ...@@ -82,6 +87,12 @@ void RefcountTracker::refUsed(llvm::Value* v, llvm::Instruction* inst) {
this->refs_used[inst].push_back(v); this->refs_used[inst].push_back(v);
} }
void RefcountTracker::setMayThrow(llvm::Instruction* inst) {
assert(!may_throw.count(inst));
this->may_throw.insert(inst);
}
void remapPhis(llvm::BasicBlock* in_block, llvm::BasicBlock* from_block, llvm::BasicBlock* new_from_block) { void remapPhis(llvm::BasicBlock* in_block, llvm::BasicBlock* from_block, llvm::BasicBlock* new_from_block) {
for (llvm::Instruction& i : *in_block) { for (llvm::Instruction& i : *in_block) {
llvm::Instruction* I = &i; llvm::Instruction* I = &i;
...@@ -227,6 +238,8 @@ void addDecrefs(llvm::Value* v, bool nullable, int num_refs, llvm::Instruction* ...@@ -227,6 +238,8 @@ void addDecrefs(llvm::Value* v, bool nullable, int num_refs, llvm::Instruction*
raise(SIGTRAP); raise(SIGTRAP);
} }
// TODO -- assert that v isn't a constant None? Implies wasted extra increfs/decrefs
if (isa<ConstantPointerNull>(v)) { if (isa<ConstantPointerNull>(v)) {
assert(nullable); assert(nullable);
return; return;
...@@ -319,6 +332,51 @@ void addDecrefs(llvm::Value* v, bool nullable, int num_refs, llvm::Instruction* ...@@ -319,6 +332,51 @@ void addDecrefs(llvm::Value* v, bool nullable, int num_refs, llvm::Instruction*
builder.SetInsertPoint(continue_block); builder.SetInsertPoint(continue_block);
} }
void addCXXFixup(llvm::Instruction* inst, const llvm::SmallVector<llvm::TrackingVH<llvm::Value>, 4>& to_decref,
RefcountTracker* rt) {
// inst->getParent()->getParent()->dump();
// inst->dump();
ASSERT(!llvm::isa<llvm::InvokeInst>(inst),
"don't need a fixup here!"); // could either not ask for the fixup or maybe just skip it here
assert(llvm::isa<llvm::CallInst>(inst));
llvm::CallInst* call = llvm::cast<llvm::CallInst>(inst);
llvm::BasicBlock* cur_block = inst->getParent();
llvm::BasicBlock* continue_block = cur_block->splitBasicBlock(inst);
llvm::BasicBlock* fixup_block
= llvm::BasicBlock::Create(g.context, "cxx_fixup", inst->getParent()->getParent(), continue_block);
assert(llvm::isa<llvm::BranchInst>(cur_block->getTerminator()));
cur_block->getTerminator()->eraseFromParent();
// llvm::SmallVector<llvm::Value*, 4> args(call->arg_begin(), call->arg_end());
llvm::SmallVector<llvm::Value*, 4> args(call->arg_operands().begin(), call->arg_operands().end());
llvm::InvokeInst* new_invoke
= InvokeInst::Create(call->getCalledValue(), continue_block, fixup_block, args, call->getName(), cur_block);
new_invoke->setAttributes(call->getAttributes());
new_invoke->setDebugLoc(call->getDebugLoc());
assert(!call->hasMetadataOtherThanDebugLoc());
call->replaceAllUsesWith(new_invoke);
call->eraseFromParent();
llvm::Value* exc_type, *exc_value, *exc_traceback;
std::tie(exc_type, exc_value, exc_traceback) = createLandingpad(fixup_block);
llvm::IRBuilder<true> builder(fixup_block);
auto rethrow = builder.CreateCall3(g.funcs.rawReraise, exc_type, exc_value, exc_traceback);
builder.CreateUnreachable();
// fixup_block->dump();
for (auto&& v : to_decref) {
addDecrefs(v, rt->isNullable(v), 1, rethrow);
}
// new_invoke->getParent()->getParent()->dump();
}
// TODO: this should be cleaned up and moved to src/core/ // TODO: this should be cleaned up and moved to src/core/
template <typename K, typename V> class OrderedMap { template <typename K, typename V> class OrderedMap {
private: private:
...@@ -641,7 +699,7 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -641,7 +699,7 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
#endif #endif
struct RefOp { struct RefOp {
llvm::Value* operand; llvm::TrackingVH<llvm::Value> operand;
bool nullable; bool nullable;
int num_refs; int num_refs;
...@@ -660,6 +718,12 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -660,6 +718,12 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
llvm::SmallVector<RefOp, 4> increfs; llvm::SmallVector<RefOp, 4> increfs;
llvm::SmallVector<RefOp, 4> decrefs; llvm::SmallVector<RefOp, 4> decrefs;
struct CXXFixup {
llvm::Instruction* inst;
llvm::SmallVector<llvm::TrackingVH<llvm::Value>, 4> to_decref;
};
llvm::SmallVector<CXXFixup, 4> cxx_fixups;
}; };
llvm::DenseMap<llvm::BasicBlock*, RefState> states; llvm::DenseMap<llvm::BasicBlock*, RefState> states;
...@@ -668,12 +732,13 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -668,12 +732,13 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
orderer.add(&BB); orderer.add(&BB);
} }
std::vector<llvm::Instruction*> tracked_instructions; std::vector<llvm::InvokeInst*> invokes;
for (auto&& II : llvm::inst_range(f)) { for (auto&& II : llvm::inst_range(f)) {
llvm::Instruction* inst = &II; llvm::Instruction* inst = &II;
if (!rt->vars.count(inst)) if (!rt->vars.count(inst))
continue; continue;
tracked_instructions.push_back(inst); if (auto ii = dyn_cast<InvokeInst>(inst))
invokes.push_back(ii);
} }
while (llvm::BasicBlock* bb = orderer.pop()) { while (llvm::BasicBlock* bb = orderer.pop()) {
...@@ -703,6 +768,7 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -703,6 +768,7 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
state.ending_refs.clear(); state.ending_refs.clear();
state.increfs.clear(); state.increfs.clear();
state.decrefs.clear(); state.decrefs.clear();
state.cxx_fixups.clear();
// Compute the incoming refstate based on the refstate of any successor nodes // Compute the incoming refstate based on the refstate of any successor nodes
llvm::SmallVector<llvm::BasicBlock*, 4> successors; llvm::SmallVector<llvm::BasicBlock*, 4> successors;
...@@ -782,9 +848,55 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -782,9 +848,55 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
// - the phi-node-generator is supposed to handle that by putting a refConsumed on the terminator of the // - the phi-node-generator is supposed to handle that by putting a refConsumed on the terminator of the
// previous block // previous block
// - that refConsumed will caus a use as well. // - that refConsumed will caus a use as well.
auto inst = &I;
if (!isa<InvokeInst>(inst) && rt->vars.count(&I)) {
const auto&& rstate = rt->vars.lookup(inst);
int starting_refs = (rstate.reftype == RefType::OWNED ? 1 : 0);
if (state.ending_refs[inst] != starting_refs) {
llvm::Instruction* insertion_pt = NULL;
llvm::BasicBlock* insertion_block = NULL, * insertion_from_block = NULL;
insertion_pt = inst->getNextNode();
while (llvm::isa<llvm::PHINode>(insertion_pt)) {
insertion_pt = insertion_pt->getNextNode();
}
if (state.ending_refs[inst] < starting_refs) {
assert(rstate.reftype == RefType::OWNED);
state.decrefs.push_back(RefOp({ inst, rstate.nullable, starting_refs - state.ending_refs[inst],
insertion_pt, insertion_block, insertion_from_block }));
} else {
state.increfs.push_back(RefOp({ inst, rstate.nullable, state.ending_refs[inst] - starting_refs,
insertion_pt, insertion_block, insertion_from_block }));
}
}
state.ending_refs.erase(inst);
}
if (llvm::isa<llvm::PHINode>(&I)) if (llvm::isa<llvm::PHINode>(&I))
continue; continue;
// If we are about to insert a CXX fixup, do the increfs after the call, rather than trying to push
// them before the call and having to insert decrefs on the fixup path.
if (rt->may_throw.count(&I)) {
llvm::SmallVector<llvm::Value*, 4> to_erase;
for (auto&& p : state.ending_refs) {
int needed_refs = (rt->vars.lookup(p.first).reftype == RefType::OWNED ? 1 : 0);
if (p.second > needed_refs) {
state.increfs.push_back(RefOp({ p.first, rt->vars.lookup(p.first).nullable,
p.second - needed_refs, I.getNextNode(), NULL, NULL }));
}
state.ending_refs[p.first] = needed_refs;
if (needed_refs == 0)
to_erase.push_back(p.first);
}
for (auto v : to_erase)
state.ending_refs.erase(v);
}
llvm::DenseMap<llvm::Value*, int> num_consumed_by_inst; llvm::DenseMap<llvm::Value*, int> num_consumed_by_inst;
OrderedMap<llvm::Value*, int> num_times_as_op; OrderedMap<llvm::Value*, int> num_times_as_op;
...@@ -807,6 +919,7 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -807,6 +919,7 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
num_times_as_op[op]++; num_times_as_op[op]++;
} }
// First, calculate anything we need to keep alive through the end of the function call:
for (auto&& p : num_times_as_op) { for (auto&& p : num_times_as_op) {
auto& op = p.first; auto& op = p.first;
...@@ -816,7 +929,7 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -816,7 +929,7 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
num_consumed = it->second; num_consumed = it->second;
if (num_times_as_op[op] > num_consumed) { if (num_times_as_op[op] > num_consumed) {
if (rt->vars[op].reftype == RefType::OWNED) { if (rt->vars.lookup(op).reftype == RefType::OWNED) {
if (state.ending_refs[op] == 0) { if (state.ending_refs[op] == 0) {
// llvm::outs() << "Last use of " << *op << " is at " << I << "; adding a decref after\n"; // llvm::outs() << "Last use of " << *op << " is at " << I << "; adding a decref after\n";
...@@ -839,6 +952,34 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -839,6 +952,34 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
} }
} }
} }
}
if (rt->may_throw.count(&I)) {
// TODO: pump out any increfs rather than pushing them before this.
state.cxx_fixups.emplace_back();
auto&& fixup = state.cxx_fixups.back();
fixup.inst = &I;
for (auto&& p : state.ending_refs) {
for (int i = 0; i < p.second; i++) {
assert(rt->vars.count(p.first));
fixup.to_decref.push_back(p.first);
}
}
if (fixup.to_decref.empty())
state.cxx_fixups.pop_back();
}
// Lastly, take care of any stolen refs. This happens regardless of whether an exception gets thrown,
// so it goes after that handling (since we are processing in reverse).
for (auto&& p : num_times_as_op) {
auto& op = p.first;
auto&& it = num_consumed_by_inst.find(op);
int num_consumed = 0;
if (it != num_consumed_by_inst.end())
num_consumed = it->second;
if (num_consumed) if (num_consumed)
state.ending_refs[op] += num_consumed; state.ending_refs[op] += num_consumed;
...@@ -855,43 +996,32 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -855,43 +996,32 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
} }
// size_t hash = 0; // Invokes are special. Handle them here by treating them as if they happened in their normal-dest block.
// Handle variables that were defined in this BB: for (InvokeInst* ii : invokes) {
for (auto&& inst : tracked_instructions) { const auto&& rstate = rt->vars.lookup(ii);
const auto&& rstate = rt->vars.lookup(inst);
// hash = hash * 31 + std::hash<llvm::StringRef>()(inst->getName());
// Invokes are special. Handle them here by treating them as if they happened in their normal-dest block. if (ii->getNormalDest() == &BB) {
llvm::InvokeInst* ii = llvm::dyn_cast<llvm::InvokeInst>(inst); // TODO: duplicated with the non-invoke code
if ((!ii && inst->getParent() == &BB) || (ii && ii->getNormalDest() == &BB)) {
int starting_refs = (rstate.reftype == RefType::OWNED ? 1 : 0); int starting_refs = (rstate.reftype == RefType::OWNED ? 1 : 0);
if (state.ending_refs[inst] != starting_refs) { if (state.ending_refs[ii] != starting_refs) {
llvm::Instruction* insertion_pt = NULL; llvm::Instruction* insertion_pt = NULL;
llvm::BasicBlock* insertion_block = NULL, * insertion_from_block = NULL; llvm::BasicBlock* insertion_block = NULL, * insertion_from_block = NULL;
if (ii) {
insertion_block = bb;
insertion_from_block = inst->getParent();
} else {
insertion_pt = inst->getNextNode();
while (llvm::isa<llvm::PHINode>(insertion_pt)) {
insertion_pt = insertion_pt->getNextNode();
}
}
if (state.ending_refs[inst] < starting_refs) { insertion_block = bb;
insertion_from_block = ii->getParent();
if (state.ending_refs[ii] < starting_refs) {
assert(rstate.reftype == RefType::OWNED); assert(rstate.reftype == RefType::OWNED);
state.decrefs.push_back(RefOp({ inst, rstate.nullable, starting_refs - state.ending_refs[inst], state.decrefs.push_back(RefOp({ ii, rstate.nullable, starting_refs - state.ending_refs[ii],
insertion_pt, insertion_block, insertion_from_block })); insertion_pt, insertion_block, insertion_from_block }));
} else { } else {
state.increfs.push_back(RefOp({ inst, rstate.nullable, state.ending_refs[inst] - starting_refs, state.increfs.push_back(RefOp({ ii, rstate.nullable, state.ending_refs[ii] - starting_refs,
insertion_pt, insertion_block, insertion_from_block })); insertion_pt, insertion_block, insertion_from_block }));
} }
} }
state.ending_refs.erase(inst); state.ending_refs.erase(ii);
} }
} }
// errs() << "DETERMINISM: rt.vars name hash: " << hash << '\n';
// If this is the entry block, finish dealing with the ref state rather than handing off to a predecessor // If this is the entry block, finish dealing with the ref state rather than handing off to a predecessor
if (&BB == &BB.getParent()->front()) { if (&BB == &BB.getParent()->front()) {
...@@ -964,24 +1094,23 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) { ...@@ -964,24 +1094,23 @@ void RefcountTracker::addRefcounts(IRGenState* irstate) {
for (auto bb : basic_blocks) { for (auto bb : basic_blocks) {
auto&& state = states[bb]; auto&& state = states[bb];
for (auto& op : state.increfs) { for (auto& op : state.increfs) {
assert(rt->vars.count(op.operand));
auto insertion_pt = op.insertion_inst; auto insertion_pt = op.insertion_inst;
if (!insertion_pt) if (!insertion_pt)
insertion_pt = findInsertionPoint(op.insertion_bb, op.insertion_from_bb, insertion_pts); insertion_pt = findInsertionPoint(op.insertion_bb, op.insertion_from_bb, insertion_pts);
addIncrefs(op.operand, op.nullable, op.num_refs, insertion_pt); addIncrefs(op.operand, op.nullable, op.num_refs, insertion_pt);
} }
for (auto& op : state.decrefs) { for (auto& op : state.decrefs) {
assert(rt->vars.count(op.operand));
auto insertion_pt = op.insertion_inst; auto insertion_pt = op.insertion_inst;
if (!insertion_pt) if (!insertion_pt)
insertion_pt = findInsertionPoint(op.insertion_bb, op.insertion_from_bb, insertion_pts); insertion_pt = findInsertionPoint(op.insertion_bb, op.insertion_from_bb, insertion_pts);
addDecrefs(op.operand, op.nullable, op.num_refs, insertion_pt); addDecrefs(op.operand, op.nullable, op.num_refs, insertion_pt);
} }
}
if (VERBOSITY() >= 2) { for (auto&& fixup : state.cxx_fixups) {
fprintf(stderr, "After refcounts:\n"); addCXXFixup(fixup.inst, fixup.to_decref, rt);
fprintf(stderr, "\033[35m"); }
dumpPrettyIR(f);
fprintf(stderr, "\033[0m");
} }
long us = _t.end(); long us = _t.end();
......
...@@ -315,7 +315,7 @@ void initGlobalFuncs(GlobalState& g) { ...@@ -315,7 +315,7 @@ void initGlobalFuncs(GlobalState& g) {
GET(raise0_capi); GET(raise0_capi);
GET(raise3); GET(raise3);
GET(raise3_capi); GET(raise3_capi);
GET(rawThrow); GET(rawReraise);
GET(PyErr_Fetch); GET(PyErr_Fetch);
GET(PyErr_NormalizeException); GET(PyErr_NormalizeException);
GET(PyErr_Restore); GET(PyErr_Restore);
......
...@@ -51,7 +51,7 @@ struct GlobalFuncs { ...@@ -51,7 +51,7 @@ struct GlobalFuncs {
llvm::Value* boxedLocalsSet, *boxedLocalsGet, *boxedLocalsDel; llvm::Value* boxedLocalsSet, *boxedLocalsGet, *boxedLocalsDel;
llvm::Value* __cxa_end_catch; llvm::Value* __cxa_end_catch;
llvm::Value* raise0, *raise0_capi, *raise3, *raise3_capi, *rawThrow; llvm::Value* raise0, *raise0_capi, *raise3, *raise3_capi, *rawReraise;
llvm::Value* PyErr_Fetch, *PyErr_NormalizeException, *PyErr_Restore, *caughtCapiException, *reraiseCapiExcAsCxx; llvm::Value* PyErr_Fetch, *PyErr_NormalizeException, *PyErr_Restore, *caughtCapiException, *reraiseCapiExcAsCxx;
llvm::Value* deopt; llvm::Value* deopt;
llvm::Value* checkRefs; llvm::Value* checkRefs;
......
...@@ -282,8 +282,7 @@ extern "C" void reraiseCapiExcAsCxx() { ...@@ -282,8 +282,7 @@ extern "C" void reraiseCapiExcAsCxx() {
throw e; throw e;
} }
// XXX rename this extern "C" void rawReraise(Box* type, Box* value, Box* tb) {
extern "C" void rawThrow(Box* type, Box* value, Box* tb) {
startReraise(); startReraise();
throw ExcInfo(type, value, tb); throw ExcInfo(type, value, tb);
} }
......
...@@ -130,7 +130,7 @@ void force() { ...@@ -130,7 +130,7 @@ void force() {
FORCE(raise0_capi); FORCE(raise0_capi);
FORCE(raise3); FORCE(raise3);
FORCE(raise3_capi); FORCE(raise3_capi);
FORCE(rawThrow); FORCE(rawReraise);
FORCE(PyErr_Fetch); FORCE(PyErr_Fetch);
FORCE(PyErr_NormalizeException); FORCE(PyErr_NormalizeException);
FORCE(PyErr_Restore); FORCE(PyErr_Restore);
......
...@@ -37,7 +37,7 @@ extern "C" void raise0(ExcInfo* frame_exc_info) __attribute__((__noreturn__)); ...@@ -37,7 +37,7 @@ extern "C" void raise0(ExcInfo* frame_exc_info) __attribute__((__noreturn__));
extern "C" void raise0_capi(ExcInfo* frame_exc_info) noexcept; extern "C" void raise0_capi(ExcInfo* frame_exc_info) noexcept;
extern "C" void raise3(Box*, Box*, Box*) __attribute__((__noreturn__)); extern "C" void raise3(Box*, Box*, Box*) __attribute__((__noreturn__));
extern "C" void raise3_capi(Box*, Box*, Box*) noexcept; extern "C" void raise3_capi(Box*, Box*, Box*) noexcept;
extern "C" void rawThrow(Box*, Box*, Box*) __attribute__((__noreturn__)); extern "C" void rawReraise(Box*, Box*, Box*) __attribute__((__noreturn__));
void raiseExc(STOLEN(Box*) exc_obj) __attribute__((__noreturn__)); void raiseExc(STOLEN(Box*) exc_obj) __attribute__((__noreturn__));
void _printStacktrace(); void _printStacktrace();
......
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