Commit 61dc4620 authored by Marius Wachtler's avatar Marius Wachtler Committed by Kevin Modzelewski

generate a decref info table and fix the bjit

we need this map to know which object refcounter we have to decrement in case an exception gets throwen inside an IC or the bjit.
in addition fix all bjit related problems
parent 4eb5b941
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "asm_writing/assembler.h" #include "asm_writing/assembler.h"
#include "asm_writing/mc_writer.h" #include "asm_writing/mc_writer.h"
#include "codegen/patchpoints.h" #include "codegen/patchpoints.h"
#include "codegen/unwinding.h"
#include "core/common.h" #include "core/common.h"
#include "core/options.h" #include "core/options.h"
#include "core/types.h" #include "core/types.h"
...@@ -53,6 +54,7 @@ void ICInvalidator::invalidateAll() { ...@@ -53,6 +54,7 @@ void ICInvalidator::invalidateAll() {
void ICSlotInfo::clear() { void ICSlotInfo::clear() {
ic->clear(this); ic->clear(this);
decref_infos.clear();
} }
ICSlotRewrite::ICSlotRewrite(ICInfo* ic, const char* debug_name) ICSlotRewrite::ICSlotRewrite(ICInfo* ic, const char* debug_name)
...@@ -82,7 +84,8 @@ uint8_t* ICSlotRewrite::getSlotStart() { ...@@ -82,7 +84,8 @@ uint8_t* ICSlotRewrite::getSlotStart() {
return (uint8_t*)ic->start_addr + ic_entry->idx * ic->getSlotSize(); return (uint8_t*)ic->start_addr + ic_entry->idx * ic->getSlotSize();
} }
void ICSlotRewrite::commit(CommitHook* hook, std::vector<void*> gc_references) { void ICSlotRewrite::commit(CommitHook* hook, std::vector<void*> gc_references,
std::vector<std::pair<uint64_t, std::vector<Location>>> decref_infos) {
bool still_valid = true; bool still_valid = true;
for (int i = 0; i < dependencies.size(); i++) { for (int i = 0; i < dependencies.size(); i++) {
int orig_version = dependencies[i].second; int orig_version = dependencies[i].second;
...@@ -130,6 +133,21 @@ void ICSlotRewrite::commit(CommitHook* hook, std::vector<void*> gc_references) { ...@@ -130,6 +133,21 @@ void ICSlotRewrite::commit(CommitHook* hook, std::vector<void*> gc_references) {
megamorphic_ics.log(); megamorphic_ics.log();
} }
// deregister old decref infos
ic_entry->decref_infos.clear();
// register new decref info
for (auto&& decref_info : decref_infos) {
// add decref locations which are always to decref inside this IC
auto&& merged_locations = decref_info.second;
merged_locations.insert(merged_locations.end(), ic->ic_global_decref_locations.begin(),
ic->ic_global_decref_locations.end());
if (merged_locations.empty())
continue;
ic_entry->decref_infos.emplace_back(decref_info.first, std::move(merged_locations));
}
llvm::sys::Memory::InvalidateInstructionCache(slot_start, ic->getSlotSize()); llvm::sys::Memory::InvalidateInstructionCache(slot_start, ic->getSlotSize());
} }
...@@ -207,7 +225,8 @@ void deregisterGCTrackedICInfo(ICInfo* ic) { ...@@ -207,7 +225,8 @@ void deregisterGCTrackedICInfo(ICInfo* ic) {
ICInfo::ICInfo(void* start_addr, void* slowpath_rtn_addr, void* continue_addr, StackInfo stack_info, int num_slots, ICInfo::ICInfo(void* start_addr, void* slowpath_rtn_addr, void* continue_addr, StackInfo stack_info, int num_slots,
int slot_size, llvm::CallingConv::ID calling_conv, LiveOutSet _live_outs, int slot_size, llvm::CallingConv::ID calling_conv, LiveOutSet _live_outs,
assembler::GenericRegister return_register, TypeRecorder* type_recorder) assembler::GenericRegister return_register, TypeRecorder* type_recorder,
std::vector<Location> ic_global_decref_locations)
: next_slot_to_try(0), : next_slot_to_try(0),
stack_info(stack_info), stack_info(stack_info),
num_slots(num_slots), num_slots(num_slots),
...@@ -219,6 +238,7 @@ ICInfo::ICInfo(void* start_addr, void* slowpath_rtn_addr, void* continue_addr, S ...@@ -219,6 +238,7 @@ ICInfo::ICInfo(void* start_addr, void* slowpath_rtn_addr, void* continue_addr, S
retry_in(0), retry_in(0),
retry_backoff(1), retry_backoff(1),
times_rewritten(0), times_rewritten(0),
ic_global_decref_locations(std::move(ic_global_decref_locations)),
start_addr(start_addr), start_addr(start_addr),
slowpath_rtn_addr(slowpath_rtn_addr), slowpath_rtn_addr(slowpath_rtn_addr),
continue_addr(continue_addr) { continue_addr(continue_addr) {
...@@ -226,6 +246,8 @@ ICInfo::ICInfo(void* start_addr, void* slowpath_rtn_addr, void* continue_addr, S ...@@ -226,6 +246,8 @@ ICInfo::ICInfo(void* start_addr, void* slowpath_rtn_addr, void* continue_addr, S
for (int i = 0; i < num_slots; i++) { for (int i = 0; i < num_slots; i++) {
slots.emplace_back(this, i); slots.emplace_back(this, i);
} }
if (slowpath_rtn_addr && !this->ic_global_decref_locations.empty())
slowpath_decref_info = DecrefInfo((uint64_t)slowpath_rtn_addr, this->ic_global_decref_locations);
#if MOVING_GC #if MOVING_GC
assert(ics_list.count(this) == 0); assert(ics_list.count(this) == 0);
...@@ -238,9 +260,21 @@ ICInfo::~ICInfo() { ...@@ -238,9 +260,21 @@ ICInfo::~ICInfo() {
#endif #endif
} }
DecrefInfo::DecrefInfo(uint64_t ip, std::vector<Location> locations) : ip(ip) {
addDecrefInfoEntry(ip, std::move(locations));
}
void DecrefInfo::reset() {
if (ip) {
removeDecrefInfoEntry(ip);
ip = 0;
}
}
std::unique_ptr<ICInfo> registerCompiledPatchpoint(uint8_t* start_addr, uint8_t* slowpath_start_addr, std::unique_ptr<ICInfo> registerCompiledPatchpoint(uint8_t* start_addr, uint8_t* slowpath_start_addr,
uint8_t* continue_addr, uint8_t* slowpath_rtn_addr, uint8_t* continue_addr, uint8_t* slowpath_rtn_addr,
const ICSetupInfo* ic, StackInfo stack_info, LiveOutSet live_outs) { const ICSetupInfo* ic, StackInfo stack_info, LiveOutSet live_outs,
std::vector<Location> decref_info) {
assert(slowpath_start_addr - start_addr >= ic->num_slots * ic->slot_size); assert(slowpath_start_addr - start_addr >= ic->num_slots * ic->slot_size);
assert(slowpath_rtn_addr > slowpath_start_addr); assert(slowpath_rtn_addr > slowpath_start_addr);
assert(slowpath_rtn_addr <= start_addr + ic->totalSize()); assert(slowpath_rtn_addr <= start_addr + ic->totalSize());
...@@ -276,8 +310,9 @@ std::unique_ptr<ICInfo> registerCompiledPatchpoint(uint8_t* start_addr, uint8_t* ...@@ -276,8 +310,9 @@ std::unique_ptr<ICInfo> registerCompiledPatchpoint(uint8_t* start_addr, uint8_t*
writer.jmp(JumpDestination::fromStart(slowpath_start_addr - start)); writer.jmp(JumpDestination::fromStart(slowpath_start_addr - start));
} }
ICInfo* icinfo = new ICInfo(start_addr, slowpath_rtn_addr, continue_addr, stack_info, ic->num_slots, ic->slot_size, ICInfo* icinfo
ic->getCallingConvention(), std::move(live_outs), return_register, ic->type_recorder); = new ICInfo(start_addr, slowpath_rtn_addr, continue_addr, stack_info, ic->num_slots, ic->slot_size,
ic->getCallingConvention(), std::move(live_outs), return_register, ic->type_recorder, decref_info);
assert(!ics_by_return_addr.count(slowpath_rtn_addr)); assert(!ics_by_return_addr.count(slowpath_rtn_addr));
ics_by_return_addr[slowpath_rtn_addr] = icinfo; ics_by_return_addr[slowpath_rtn_addr] = icinfo;
...@@ -355,6 +390,17 @@ ICInfo* ICInfo::getICInfoForNode(AST* node) { ...@@ -355,6 +390,17 @@ ICInfo* ICInfo::getICInfoForNode(AST* node) {
void ICInfo::associateNodeWithICInfo(AST* node) { void ICInfo::associateNodeWithICInfo(AST* node) {
ics_by_ast_node[node] = this; ics_by_ast_node[node] = this;
} }
void ICInfo::appendDecrefInfosTo(std::vector<DecrefInfo>& dest_decref_infos) {
if (slowpath_decref_info.ip)
dest_decref_infos.emplace_back(std::move(slowpath_decref_info));
for (auto&& slot : slots) {
for (DecrefInfo& decref_info : slot.decref_infos) {
dest_decref_infos.emplace_back(std::move(decref_info));
assert(decref_info.ip == 0 && "this can only happen if we copied instead of moved the value");
}
slot.decref_infos.clear();
}
}
void clearAllICs() { void clearAllICs() {
for (auto&& p : ics_by_ast_node) { for (auto&& p : ics_by_ast_node) {
......
...@@ -35,6 +35,24 @@ class ICInvalidator; ...@@ -35,6 +35,24 @@ class ICInvalidator;
#define IC_INVALDITION_HEADER_SIZE 6 #define IC_INVALDITION_HEADER_SIZE 6
#define IC_MEGAMORPHIC_THRESHOLD 100 #define IC_MEGAMORPHIC_THRESHOLD 100
// This registers a decref info in the constructor and deregisters it in the destructor.
struct DecrefInfo {
uint64_t ip;
DecrefInfo() : ip(0) {}
DecrefInfo(uint64_t ip, std::vector<Location> locations);
// we only allow moves
DecrefInfo(DecrefInfo&& other) : ip(0) { std::swap(other.ip, ip); }
DecrefInfo& operator=(DecrefInfo&& other) {
std::swap(other.ip, ip);
return *this;
}
~DecrefInfo() { reset(); }
void reset();
};
struct ICSlotInfo { struct ICSlotInfo {
public: public:
ICSlotInfo(ICInfo* ic, int idx) : ic(ic), idx(idx), num_inside(0) {} ICSlotInfo(ICInfo* ic, int idx) : ic(ic), idx(idx), num_inside(0) {}
...@@ -44,6 +62,7 @@ public: ...@@ -44,6 +62,7 @@ public:
int num_inside; // the number of stack frames that are currently inside this slot int num_inside; // the number of stack frames that are currently inside this slot
std::vector<void*> gc_references; std::vector<void*> gc_references;
std::vector<DecrefInfo> decref_infos;
void clear(); void clear();
}; };
...@@ -85,7 +104,8 @@ public: ...@@ -85,7 +104,8 @@ public:
ICSlotInfo* prepareEntry(); ICSlotInfo* prepareEntry();
void addDependenceOn(ICInvalidator&); void addDependenceOn(ICInvalidator&);
void commit(CommitHook* hook, std::vector<void*> gc_references); void commit(CommitHook* hook, std::vector<void*> gc_references,
std::vector<std::pair<uint64_t, std::vector<Location>>> decref_infos);
void abort(); void abort();
const ICInfo* getICInfo() { return ic; } const ICInfo* getICInfo() { return ic; }
...@@ -117,13 +137,20 @@ private: ...@@ -117,13 +137,20 @@ private:
int retry_in, retry_backoff; int retry_in, retry_backoff;
int times_rewritten; int times_rewritten;
DecrefInfo slowpath_decref_info;
// This is a vector of locations which always need to get decrefed inside this IC.
// Calls inside the ICSlots may need to decref additional locations but they will always contain at least the IC
// global ones.
std::vector<Location> ic_global_decref_locations;
// for ICSlotRewrite: // for ICSlotRewrite:
ICSlotInfo* pickEntryForRewrite(const char* debug_name); ICSlotInfo* pickEntryForRewrite(const char* debug_name);
public: public:
ICInfo(void* start_addr, void* slowpath_rtn_addr, void* continue_addr, StackInfo stack_info, int num_slots, ICInfo(void* start_addr, void* slowpath_rtn_addr, void* continue_addr, StackInfo stack_info, int num_slots,
int slot_size, llvm::CallingConv::ID calling_conv, LiveOutSet live_outs, int slot_size, llvm::CallingConv::ID calling_conv, LiveOutSet live_outs,
assembler::GenericRegister return_register, TypeRecorder* type_recorder); assembler::GenericRegister return_register, TypeRecorder* type_recorder,
std::vector<Location> ic_global_decref_locations);
~ICInfo(); ~ICInfo();
void* const start_addr, *const slowpath_rtn_addr, *const continue_addr; void* const start_addr, *const slowpath_rtn_addr, *const continue_addr;
...@@ -152,6 +179,8 @@ public: ...@@ -152,6 +179,8 @@ public:
static ICInfo* getICInfoForNode(AST* node); static ICInfo* getICInfoForNode(AST* node);
void associateNodeWithICInfo(AST* node); void associateNodeWithICInfo(AST* node);
void appendDecrefInfosTo(std::vector<DecrefInfo>& dest_decref_infos);
}; };
void registerGCTrackedICInfo(ICInfo* ic); void registerGCTrackedICInfo(ICInfo* ic);
...@@ -159,9 +188,10 @@ void deregisterGCTrackedICInfo(ICInfo* ic); ...@@ -159,9 +188,10 @@ void deregisterGCTrackedICInfo(ICInfo* ic);
class ICSetupInfo; class ICSetupInfo;
struct CompiledFunction; struct CompiledFunction;
std::unique_ptr<ICInfo> registerCompiledPatchpoint(uint8_t* start_addr, uint8_t* slowpath_start_addr, std::unique_ptr<ICInfo>
uint8_t* continue_addr, uint8_t* slowpath_rtn_addr, registerCompiledPatchpoint(uint8_t* start_addr, uint8_t* slowpath_start_addr, uint8_t* continue_addr,
const ICSetupInfo*, StackInfo stack_info, LiveOutSet live_outs); uint8_t* slowpath_rtn_addr, const ICSetupInfo*, StackInfo stack_info, LiveOutSet live_outs,
std::vector<Location> ic_global_decref_locations = std::vector<Location>());
void deregisterCompiledPatchpoint(ICInfo* ic); void deregisterCompiledPatchpoint(ICInfo* ic);
ICInfo* getICInfo(void* rtn_addr); ICInfo* getICInfo(void* rtn_addr);
......
...@@ -1186,6 +1186,9 @@ void Rewriter::_call(RewriterVar* result, bool has_side_effects, void* func_addr ...@@ -1186,6 +1186,9 @@ void Rewriter::_call(RewriterVar* result, bool has_side_effects, void* func_addr
assert(assembler->hasFailed() || asm_address == (uint64_t)assembler->curInstPointer()); assert(assembler->hasFailed() || asm_address == (uint64_t)assembler->curInstPointer());
} }
// TODO: we don't need to generate the decref info for calls which can't throw
registerDecrefInfoHere();
if (!failed) { if (!failed) {
assert(vars_by_location.count(assembler::RAX) == 0); assert(vars_by_location.count(assembler::RAX) == 0);
...@@ -1198,6 +1201,33 @@ void Rewriter::_call(RewriterVar* result, bool has_side_effects, void* func_addr ...@@ -1198,6 +1201,33 @@ void Rewriter::_call(RewriterVar* result, bool has_side_effects, void* func_addr
result->releaseIfNoUses(); result->releaseIfNoUses();
} }
std::vector<Location> Rewriter::getDecrefLocations() {
std::vector<Location> decref_infos;
for (RewriterVar& var : vars) {
if (var.locations.size() && var.needsDecref()) {
// TODO: add code to handle other location types and choose best location if there are several
Location l = *var.locations.begin();
if (l.type == Location::Scratch) {
// convert to stack based location because later on we may not know the offset of the scratch area from
// the SP.
assert(indirectFor(l).offset % 8 == 0);
decref_infos.emplace_back(Location::Stack, indirectFor(l).offset / 8);
} else if (l.type == Location::Register) {
decref_infos.emplace_back(l);
} else
RELEASE_ASSERT(0, "not implemented");
}
}
return decref_infos;
}
void Rewriter::registerDecrefInfoHere() {
std::vector<Location> decref_locations = getDecrefLocations();
auto call_offset = assembler->bytesWritten();
uint64_t ip = (uint64_t)rewrite->getSlotStart() + call_offset;
decref_infos.emplace_back(std::make_pair(ip, std::move(decref_locations)));
}
void Rewriter::abort() { void Rewriter::abort() {
STAT_TIMER(t0, "us_timer_rewriter", 10); STAT_TIMER(t0, "us_timer_rewriter", 10);
...@@ -1224,23 +1254,6 @@ RewriterVar* RewriterVar::setType(RefType type) { ...@@ -1224,23 +1254,6 @@ RewriterVar* RewriterVar::setType(RefType type) {
assert(this->reftype == RefType::UNKNOWN || this->reftype == type); assert(this->reftype == RefType::UNKNOWN || this->reftype == type);
if (this->reftype == RefType::UNKNOWN) { if (this->reftype == RefType::UNKNOWN) {
rewriter->addAction(
[=]() {
int num_needed_refs = this->num_refs_consumed - (this->refHandedOff() ? 1 : 0);
assert(num_needed_refs >= 0);
if (num_needed_refs > 0) {
if (rewriter->isDoneGuarding()) {
this->rewriter->_incref(this, num_needed_refs);
} else {
rewriter->pending_increfs.push_back(std::make_pair(this, num_needed_refs));
}
}
this->bumpUse();
// Register this as a NORMAL (ie non-MUTATION) action since we will be careful to not do any mutations
// if we are still in the guarding phase.
},
{ this }, ActionType::NORMAL);
this->reftype = type; this->reftype = type;
} }
...@@ -1272,9 +1285,17 @@ void RewriterVar::_release() { ...@@ -1272,9 +1285,17 @@ void RewriterVar::_release() {
this->locations.clear(); this->locations.clear();
} }
void RewriterVar::refConsumed() { void RewriterVar::refConsumed(RewriterAction* action) {
assert(reftype != RefType::UNKNOWN || (isConstant() && constant_value == 0));
num_refs_consumed++; num_refs_consumed++;
last_refconsumed_numuses = uses.size(); last_refconsumed_numuses = uses.size();
if (!action)
action = rewriter->getLastAction();
action->consumed_refs.emplace_back(this);
}
bool RewriterVar::needsDecref() {
return reftype == RefType::OWNED && !this->refHandedOff();
} }
void RewriterVar::bumpUse() { void RewriterVar::bumpUse() {
...@@ -1374,12 +1395,6 @@ void Rewriter::commit() { ...@@ -1374,12 +1395,6 @@ void Rewriter::commit() {
} }
} }
for (auto&& p : pending_increfs) {
// TODO use a single add rather than N inc's
for (int i = 0; i < p.second; i++) {
_incref(p.first);
}
}
assertConsistent(); assertConsistent();
}; };
...@@ -1395,6 +1410,21 @@ void Rewriter::commit() { ...@@ -1395,6 +1410,21 @@ void Rewriter::commit() {
// Now, start emitting assembly; check if we're dong guarding after each. // Now, start emitting assembly; check if we're dong guarding after each.
for (int i = 0; i < actions.size(); i++) { for (int i = 0; i < actions.size(); i++) {
// add increfs if required
for (auto&& var : actions[i].consumed_refs) {
if (var->refHandedOff()) {
// if this action is the one which the variable gets handed off we don't need todo anything
assert(var->last_refconsumed_numuses > 0 && var->last_refconsumed_numuses <= var->uses.size());
int last_used_action_id = var->uses[var->last_refconsumed_numuses - 1];
if (last_used_action_id == i)
continue;
assert(last_used_action_id >= i);
}
assert(isDoneGuarding());
_incref(var, 1);
}
actions[i].action(); actions[i].action();
if (failed) { if (failed) {
...@@ -1561,7 +1591,7 @@ void Rewriter::commit() { ...@@ -1561,7 +1591,7 @@ void Rewriter::commit() {
} }
#endif #endif
rewrite->commit(this, std::move(gc_references)); rewrite->commit(this, std::move(gc_references), std::move(decref_infos));
assert(gc_references.empty()); assert(gc_references.empty());
if (assembler->hasFailed()) { if (assembler->hasFailed()) {
...@@ -1827,6 +1857,8 @@ void Rewriter::_checkAndThrowCAPIException(RewriterVar* r, int64_t exc_val) { ...@@ -1827,6 +1857,8 @@ void Rewriter::_checkAndThrowCAPIException(RewriterVar* r, int64_t exc_val) {
assembler::ForwardJump jnz(*assembler, assembler::COND_NOT_ZERO); assembler::ForwardJump jnz(*assembler, assembler::COND_NOT_ZERO);
assembler->mov(assembler::Immediate((void*)throwCAPIException), assembler::R11); assembler->mov(assembler::Immediate((void*)throwCAPIException), assembler::R11);
assembler->callq(assembler::R11); assembler->callq(assembler::R11);
registerDecrefInfoHere();
} }
r->bumpUse(); r->bumpUse();
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "asm_writing/assembler.h" #include "asm_writing/assembler.h"
#include "asm_writing/icinfo.h" #include "asm_writing/icinfo.h"
#include "asm_writing/types.h"
#include "core/threading.h" #include "core/threading.h"
#include "core/types.h" #include "core/types.h"
...@@ -164,7 +165,8 @@ public: ...@@ -164,7 +165,8 @@ public:
// This should get called *after* the ref got consumed, ie something like // This should get called *after* the ref got consumed, ie something like
// r_array->setAttr(0, r_val); // r_array->setAttr(0, r_val);
// r_val->refConsumed() // r_val->refConsumed()
void refConsumed(); // if no action is specified it will assume the last action consumed the reference
void refConsumed(RewriterAction* action = NULL);
template <typename Src, typename Dst> inline RewriterVar* getAttrCast(int offset, Location loc = Location::any()); template <typename Src, typename Dst> inline RewriterVar* getAttrCast(int offset, Location loc = Location::any());
...@@ -202,6 +204,7 @@ private: ...@@ -202,6 +204,7 @@ private:
bool isDoneUsing() { return next_use == uses.size(); } bool isDoneUsing() { return next_use == uses.size(); }
bool hasScratchAllocation() const { return scratch_allocation.second > 0; } bool hasScratchAllocation() const { return scratch_allocation.second > 0; }
void resetHasScratchAllocation() { scratch_allocation = std::make_pair(0, 0); } void resetHasScratchAllocation() { scratch_allocation = std::make_pair(0, 0); }
bool needsDecref();
// Indicates if this variable is an arg, and if so, what location the arg is from. // Indicates if this variable is an arg, and if so, what location the arg is from.
bool is_arg; bool is_arg;
...@@ -287,6 +290,7 @@ public: ...@@ -287,6 +290,7 @@ public:
class RewriterAction { class RewriterAction {
public: public:
SmallFunction<56> action; SmallFunction<56> action;
std::vector<RewriterVar*> consumed_refs;
template <typename F> RewriterAction(F&& action) : action(std::forward<F>(action)) {} template <typename F> RewriterAction(F&& action) : action(std::forward<F>(action)) {}
...@@ -306,7 +310,7 @@ class Rewriter : public ICSlotRewrite::CommitHook { ...@@ -306,7 +310,7 @@ class Rewriter : public ICSlotRewrite::CommitHook {
private: private:
class RegionAllocator { class RegionAllocator {
public: public:
static const int BLOCK_SIZE = 200; // reserve a bit of space for list/malloc overhead static const int BLOCK_SIZE = 1024; // reserve a bit of space for list/malloc overhead
std::list<char[BLOCK_SIZE]> blocks; std::list<char[BLOCK_SIZE]> blocks;
int cur_offset = BLOCK_SIZE + 1; int cur_offset = BLOCK_SIZE + 1;
...@@ -397,7 +401,7 @@ protected: ...@@ -397,7 +401,7 @@ protected:
bool needs_invalidation_support = true); bool needs_invalidation_support = true);
std::deque<RewriterAction, RegionAllocatorAdaptor<RewriterAction>> actions; std::deque<RewriterAction, RegionAllocatorAdaptor<RewriterAction>> actions;
template <typename F> void addAction(F&& action, llvm::ArrayRef<RewriterVar*> vars, ActionType type) { template <typename F> RewriterAction* addAction(F&& action, llvm::ArrayRef<RewriterVar*> vars, ActionType type) {
assertPhaseCollecting(); assertPhaseCollecting();
for (RewriterVar* var : vars) { for (RewriterVar* var : vars) {
assert(var != NULL); assert(var != NULL);
...@@ -408,7 +412,7 @@ protected: ...@@ -408,7 +412,7 @@ protected:
} else if (type == ActionType::GUARD) { } else if (type == ActionType::GUARD) {
if (added_changing_action) { if (added_changing_action) {
failed = true; failed = true;
return; return NULL;
} }
for (RewriterVar* arg : args) { for (RewriterVar* arg : args) {
arg->uses.push_back(actions.size()); arg->uses.push_back(actions.size());
...@@ -417,10 +421,17 @@ protected: ...@@ -417,10 +421,17 @@ protected:
last_guard_action = (int)actions.size(); last_guard_action = (int)actions.size();
} }
actions.emplace_back(std::forward<F>(action)); actions.emplace_back(std::forward<F>(action));
return &actions.back();
} }
RewriterAction* getLastAction() {
assert(!actions.empty());
return &actions.back();
}
bool added_changing_action; bool added_changing_action;
bool marked_inside_ic; bool marked_inside_ic;
std::vector<void*> gc_references; std::vector<void*> gc_references;
std::vector<std::pair<uint64_t, std::vector<Location>>> decref_infos;
bool done_guarding; bool done_guarding;
bool isDoneGuarding() { bool isDoneGuarding() {
...@@ -550,6 +561,10 @@ public: ...@@ -550,6 +561,10 @@ public:
#else #else
void comment(const llvm::Twine& msg) {} void comment(const llvm::Twine& msg) {}
#endif #endif
// returns a vector of locations of variables which need to get decrefed if the last action throwes
std::vector<Location> getDecrefLocations();
// calls getDecrefLocations and registers the current assembler address with the retrieved decref info
void registerDecrefInfoHere();
void trap(); void trap();
RewriterVar* loadConst(int64_t val, Location loc = Location::any()); RewriterVar* loadConst(int64_t val, Location loc = Location::any());
......
...@@ -66,7 +66,7 @@ extern "C" Box* executeInnerAndSetupFrame(ASTInterpreter& interpreter, CFGBlock* ...@@ -66,7 +66,7 @@ extern "C" Box* executeInnerAndSetupFrame(ASTInterpreter& interpreter, CFGBlock*
*/ */
class ASTInterpreter { class ASTInterpreter {
public: public:
ASTInterpreter(FunctionMetadata* md, Box** vregs, int num_vregs); ASTInterpreter(FunctionMetadata* md, Box** vregs, int num_vregs, FrameInfo* deopt_frame_info = NULL);
void initArguments(BoxedClosure* closure, BoxedGenerator* generator, Box* arg1, Box* arg2, Box* arg3, Box** args); void initArguments(BoxedClosure* closure, BoxedGenerator* generator, Box* arg1, Box* arg2, Box* arg3, Box** args);
...@@ -183,7 +183,6 @@ public: ...@@ -183,7 +183,6 @@ public:
void setPassedClosure(Box* closure); void setPassedClosure(Box* closure);
void setCreatedClosure(Box* closure); void setCreatedClosure(Box* closure);
void setBoxedLocals(STOLEN(Box*)); void setBoxedLocals(STOLEN(Box*));
void setFrameInfo(const FrameInfo* frame_info);
void setGlobals(Box* globals); void setGlobals(Box* globals);
friend struct pyston::ASTInterpreterJitInterface; friend struct pyston::ASTInterpreterJitInterface;
...@@ -215,7 +214,8 @@ void ASTInterpreter::setPassedClosure(Box* closure) { ...@@ -215,7 +214,8 @@ void ASTInterpreter::setPassedClosure(Box* closure) {
void ASTInterpreter::setCreatedClosure(Box* closure) { void ASTInterpreter::setCreatedClosure(Box* closure) {
assert(!this->created_closure); // This should only used for initialization assert(!this->created_closure); // This should only used for initialization
assert(closure->cls == closure_cls); assert(closure->cls == closure_cls);
this->created_closure = static_cast<BoxedClosure*>(closure); // we have to incref the closure because the interpreter destructor will decref it
this->created_closure = static_cast<BoxedClosure*>(incref(closure));
} }
void ASTInterpreter::setBoxedLocals(Box* boxedLocals) { void ASTInterpreter::setBoxedLocals(Box* boxedLocals) {
...@@ -223,20 +223,12 @@ void ASTInterpreter::setBoxedLocals(Box* boxedLocals) { ...@@ -223,20 +223,12 @@ void ASTInterpreter::setBoxedLocals(Box* boxedLocals) {
this->frame_info.boxedLocals = boxedLocals; this->frame_info.boxedLocals = boxedLocals;
} }
void ASTInterpreter::setFrameInfo(const FrameInfo* frame_info) {
Box** vregs = this->frame_info.vregs;
int num_vregs = this->frame_info.num_vregs;
this->frame_info = *frame_info;
this->frame_info.vregs = vregs;
this->frame_info.num_vregs = num_vregs;
}
void ASTInterpreter::setGlobals(Box* globals) { void ASTInterpreter::setGlobals(Box* globals) {
assert(!this->frame_info.globals); assert(!this->frame_info.globals);
this->frame_info.globals = incref(globals); this->frame_info.globals = incref(globals);
} }
ASTInterpreter::ASTInterpreter(FunctionMetadata* md, Box** vregs, int num_vregs) ASTInterpreter::ASTInterpreter(FunctionMetadata* md, Box** vregs, int num_vregs, FrameInfo* deopt_frame_info)
: current_block(0), : current_block(0),
frame_info(ExcInfo(NULL, NULL, NULL)), frame_info(ExcInfo(NULL, NULL, NULL)),
edgecount(0), edgecount(0),
...@@ -251,6 +243,15 @@ ASTInterpreter::ASTInterpreter(FunctionMetadata* md, Box** vregs, int num_vregs) ...@@ -251,6 +243,15 @@ ASTInterpreter::ASTInterpreter(FunctionMetadata* md, Box** vregs, int num_vregs)
should_jit(false) { should_jit(false) {
scope_info = source_info->getScopeInfo(); scope_info = source_info->getScopeInfo();
if (deopt_frame_info) {
// copy over all fields and clear the deopt frame info
frame_info = *deopt_frame_info;
for (int i = 0; i < deopt_frame_info->num_vregs; ++i)
Py_XDECREF(deopt_frame_info->vregs[i]);
memset(deopt_frame_info, 0, sizeof(*deopt_frame_info));
}
frame_info.vregs = vregs; frame_info.vregs = vregs;
frame_info.md = md; frame_info.md = md;
frame_info.num_vregs = num_vregs; frame_info.num_vregs = num_vregs;
...@@ -470,6 +471,8 @@ void ASTInterpreter::doStore(AST_Name* node, STOLEN(Value) value) { ...@@ -470,6 +471,8 @@ void ASTInterpreter::doStore(AST_Name* node, STOLEN(Value) value) {
if (jit) { if (jit) {
if (!closure) { if (!closure) {
bool is_live = source_info->getLiveness()->isLiveAtEnd(name, current_block); bool is_live = source_info->getLiveness()->isLiveAtEnd(name, current_block);
// HACK: this disable the 'this variable is dead at the end of this block' optimization
is_live = true;
if (is_live) if (is_live)
jit->emitSetLocal(name, node->vreg, closure, value); jit->emitSetLocal(name, node->vreg, closure, value);
else else
...@@ -2006,9 +2009,7 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e ...@@ -2006,9 +2009,7 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e
memset(vregs, 0, sizeof(Box*) * num_vregs); memset(vregs, 0, sizeof(Box*) * num_vregs);
} }
ASTInterpreter interpreter(md, vregs, num_vregs); ASTInterpreter interpreter(md, vregs, num_vregs, frame_state.frame_info);
if (source_info->scoping->areGlobalsFromModule())
interpreter.setGlobals(source_info->parent_module);
for (const auto& p : *frame_state.locals) { for (const auto& p : *frame_state.locals) {
assert(p.first->cls == str_cls); assert(p.first->cls == str_cls);
...@@ -2016,7 +2017,8 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e ...@@ -2016,7 +2017,8 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e
if (name == PASSED_GENERATOR_NAME) { if (name == PASSED_GENERATOR_NAME) {
interpreter.setGenerator(p.second); interpreter.setGenerator(p.second);
} else if (name == PASSED_CLOSURE_NAME) { } else if (name == PASSED_CLOSURE_NAME) {
interpreter.setPassedClosure(p.second); // this should have already got set because its stored in the frame info
assert(p.second == interpreter.getFrameInfo()->passed_closure);
} else if (name == CREATED_CLOSURE_NAME) { } else if (name == CREATED_CLOSURE_NAME) {
interpreter.setCreatedClosure(p.second); interpreter.setCreatedClosure(p.second);
} else { } else {
...@@ -2025,8 +2027,6 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e ...@@ -2025,8 +2027,6 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e
} }
} }
interpreter.setFrameInfo(frame_state.frame_info);
CFGBlock* start_block = NULL; CFGBlock* start_block = NULL;
AST_stmt* starting_statement = NULL; AST_stmt* starting_statement = NULL;
while (true) { while (true) {
...@@ -2043,7 +2043,6 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e ...@@ -2043,7 +2043,6 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e
auto expr = ast_cast<AST_Expr>(enclosing_stmt); auto expr = ast_cast<AST_Expr>(enclosing_stmt);
RELEASE_ASSERT(expr->value == after_expr, "%p %p", expr->value, after_expr); RELEASE_ASSERT(expr->value == after_expr, "%p %p", expr->value, after_expr);
assert(expr->value == after_expr); assert(expr->value == after_expr);
assert(0 && "check refcounting");
break; break;
} else if (enclosing_stmt->type == AST_TYPE::Invoke) { } else if (enclosing_stmt->type == AST_TYPE::Invoke) {
auto invoke = ast_cast<AST_Invoke>(enclosing_stmt); auto invoke = ast_cast<AST_Invoke>(enclosing_stmt);
......
This diff is collapsed.
...@@ -146,6 +146,9 @@ private: ...@@ -146,6 +146,9 @@ private:
assembler::Assembler a; assembler::Assembler a;
bool is_currently_writing; bool is_currently_writing;
bool asm_failed; bool asm_failed;
// this contains all the decref infos the bjit generated inside the memory block,
// this allows us to deregister them when we release the code
std::vector<DecrefInfo> decref_infos;
public: public:
JitCodeBlock(llvm::StringRef name); JitCodeBlock(llvm::StringRef name);
...@@ -153,7 +156,7 @@ public: ...@@ -153,7 +156,7 @@ public:
std::unique_ptr<JitFragmentWriter> newFragment(CFGBlock* block, int patch_jump_offset = 0); std::unique_ptr<JitFragmentWriter> newFragment(CFGBlock* block, int patch_jump_offset = 0);
bool shouldCreateNewBlock() const { return asm_failed || a.bytesLeft() < 128; } bool shouldCreateNewBlock() const { return asm_failed || a.bytesLeft() < 128; }
void fragmentAbort(bool not_enough_space); void fragmentAbort(bool not_enough_space);
void fragmentFinished(int bytes_witten, int num_bytes_overlapping, void* next_fragment_start); void fragmentFinished(int bytes_witten, int num_bytes_overlapping, void* next_fragment_start, ICInfo& ic_info);
}; };
class JitFragmentWriter : public Rewriter { class JitFragmentWriter : public Rewriter {
...@@ -199,6 +202,7 @@ private: ...@@ -199,6 +202,7 @@ private:
std::unique_ptr<ICSetupInfo> ic; std::unique_ptr<ICSetupInfo> ic;
StackInfo stack_info; StackInfo stack_info;
AST* node; AST* node;
std::vector<Location> decref_infos;
}; };
llvm::SmallVector<PPInfo, 8> pp_infos; llvm::SmallVector<PPInfo, 8> pp_infos;
...@@ -284,8 +288,15 @@ private: ...@@ -284,8 +288,15 @@ private:
uint64_t asUInt(InternedString s); uint64_t asUInt(InternedString s);
#endif #endif
RewriterVar* emitPPCall(void* func_addr, llvm::ArrayRef<RewriterVar*> args, int num_slots, int slot_size,
AST* ast_node = NULL, TypeRecorder* type_recorder = NULL); // use this function when one emits a call where one argument is variable created with allocArgs(vars).
// it let's one specify the additional uses the call has which are unknown to the rewriter because it is hidden in
// the allocArgs call.
RewriterVar* emitCallWithAllocatedArgs(void* func_addr, const llvm::ArrayRef<RewriterVar*> args,
const llvm::ArrayRef<RewriterVar*> additional_uses);
std::pair<RewriterVar*, RewriterAction*> emitPPCall(void* func_addr, llvm::ArrayRef<RewriterVar*> args,
int num_slots, int slot_size, AST* ast_node = NULL,
TypeRecorder* type_recorder = NULL);
static void assertNameDefinedHelper(const char* id); static void assertNameDefinedHelper(const char* id);
static Box* callattrHelper(Box* obj, BoxedString* attr, CallattrFlags flags, TypeRecorder* type_recorder, static Box* callattrHelper(Box* obj, BoxedString* attr, CallattrFlags flags, TypeRecorder* type_recorder,
......
...@@ -498,7 +498,7 @@ public: ...@@ -498,7 +498,7 @@ public:
Box* deserializeFromFrame(const FrameVals& vals) override { Box* deserializeFromFrame(const FrameVals& vals) override {
assert(vals.size() == 1); assert(vals.size() == 1);
return reinterpret_cast<Box*>(vals[0]); return incref(reinterpret_cast<Box*>(vals[0]));
} }
std::vector<CompilerVariable*> unpack(IREmitter& emitter, const OpInfo& info, VAR* var, int num_into) override { std::vector<CompilerVariable*> unpack(IREmitter& emitter, const OpInfo& info, VAR* var, int num_into) override {
...@@ -1615,6 +1615,7 @@ public: ...@@ -1615,6 +1615,7 @@ public:
std::string debugName() override { return "phony(" + ConcreteCompilerType::debugName() + ")"; } std::string debugName() override { return "phony(" + ConcreteCompilerType::debugName() + ")"; }
CompilerType* getUsableType() override { return usable_type; } CompilerType* getUsableType() override { return usable_type; }
ConcreteCompilerType* getBoxType() override { return getUsableType()->getBoxType(); }
llvm::Type* llvmType() override { return t; } llvm::Type* llvmType() override { return t; }
...@@ -2190,7 +2191,7 @@ public: ...@@ -2190,7 +2191,7 @@ public:
Box* deserializeFromFrame(const FrameVals& vals) override { Box* deserializeFromFrame(const FrameVals& vals) override {
assert(vals.size() == 1); assert(vals.size() == 1);
return reinterpret_cast<Box*>(vals[0]); return incref(reinterpret_cast<Box*>(vals[0]));
} }
}; };
std::unordered_map<BoxedClass*, NormalObjectType*> NormalObjectType::made; std::unordered_map<BoxedClass*, NormalObjectType*> NormalObjectType::made;
...@@ -2221,7 +2222,7 @@ public: ...@@ -2221,7 +2222,7 @@ public:
Box* deserializeFromFrame(const FrameVals& vals) override { Box* deserializeFromFrame(const FrameVals& vals) override {
assert(vals.size() == 1); assert(vals.size() == 1);
return reinterpret_cast<Box*>(vals[0]); return incref(reinterpret_cast<Box*>(vals[0]));
} }
} _CLOSURE; } _CLOSURE;
ConcreteCompilerType* CLOSURE = &_CLOSURE; ConcreteCompilerType* CLOSURE = &_CLOSURE;
...@@ -2236,7 +2237,7 @@ public: ...@@ -2236,7 +2237,7 @@ public:
Box* deserializeFromFrame(const FrameVals& vals) override { Box* deserializeFromFrame(const FrameVals& vals) override {
assert(vals.size() == numFrameArgs()); assert(vals.size() == numFrameArgs());
return reinterpret_cast<Box*>(vals[0]); return incref(reinterpret_cast<Box*>(vals[0]));
} }
} _GENERATOR; } _GENERATOR;
ConcreteCompilerType* GENERATOR = &_GENERATOR; ConcreteCompilerType* GENERATOR = &_GENERATOR;
......
...@@ -429,6 +429,9 @@ static inline unw_word_t get_cursor_ip(unw_cursor_t* cursor) { ...@@ -429,6 +429,9 @@ static inline unw_word_t get_cursor_ip(unw_cursor_t* cursor) {
static inline unw_word_t get_cursor_bp(unw_cursor_t* cursor) { static inline unw_word_t get_cursor_bp(unw_cursor_t* cursor) {
return get_cursor_reg(cursor, UNW_TDEP_BP); return get_cursor_reg(cursor, UNW_TDEP_BP);
} }
static inline unw_word_t get_cursor_sp(unw_cursor_t* cursor) {
return get_cursor_reg(cursor, UNW_REG_SP);
}
// if the given ip/bp correspond to a jitted frame or // if the given ip/bp correspond to a jitted frame or
// ASTInterpreter::execute_inner frame, return true and return the // ASTInterpreter::execute_inner frame, return true and return the
...@@ -507,6 +510,34 @@ static FrameInfo* getTopFrameInfo() { ...@@ -507,6 +510,34 @@ static FrameInfo* getTopFrameInfo() {
return (FrameInfo*)cur_thread_state.frame_info; return (FrameInfo*)cur_thread_state.frame_info;
} }
llvm::DenseMap<uint64_t /*ip*/, std::vector<Location>> decref_infos;
void executeDecrefs(unw_cursor_t* cursor) {
unw_word_t ip = get_cursor_ip(cursor);
auto it = decref_infos.find(ip);
if (it == decref_infos.end())
return;
for (Location& l : it->second) {
Box* b = NULL;
if (l.type == Location::Stack) {
unw_word_t sp = get_cursor_sp(cursor);
b = ((Box**)sp)[l.stack_offset];
} else {
RELEASE_ASSERT(0, "not implemented");
}
Py_XDECREF(b);
}
}
void addDecrefInfoEntry(uint64_t ip, std::vector<Location> location) {
assert(!decref_infos.count(ip) && "why is there already an entry??");
decref_infos[ip] = std::move(location);
}
void removeDecrefInfoEntry(uint64_t ip) {
decref_infos.erase(ip);
}
class PythonUnwindSession { class PythonUnwindSession {
ExcInfo exc_info; ExcInfo exc_info;
PythonStackExtractor pystack_extractor; PythonStackExtractor pystack_extractor;
...@@ -539,6 +570,8 @@ public: ...@@ -539,6 +570,8 @@ public:
prev_frame_info = NULL; prev_frame_info = NULL;
} }
executeDecrefs(cursor);
PythonFrameIteratorImpl frame_iter; PythonFrameIteratorImpl frame_iter;
bool found_frame = pystack_extractor.handleCFrame(cursor, &frame_iter); bool found_frame = pystack_extractor.handleCFrame(cursor, &frame_iter);
if (found_frame) { if (found_frame) {
...@@ -830,7 +863,8 @@ DeoptState getDeoptState() { ...@@ -830,7 +863,8 @@ DeoptState getDeoptState() {
if (!v) if (!v)
continue; continue;
d->d[incref(p.first.getBox())] = v; assert(!d->d.count(p.first.getBox()));
d->d[incref(p.first.getBox())] = incref(v);
} }
for (const auto& p : cf->location_map->names) { for (const auto& p : cf->location_map->names) {
...@@ -851,6 +885,7 @@ DeoptState getDeoptState() { ...@@ -851,6 +885,7 @@ DeoptState getDeoptState() {
vals.push_back(frame_iter->readLocation(loc)); vals.push_back(frame_iter->readLocation(loc));
} }
// this returns an owned reference so we don't incref it
Box* v = e->type->deserializeFromFrame(vals); Box* v = e->type->deserializeFromFrame(vals);
// printf("%s: (pp id %ld) %p\n", p.first().c_str(), e._debug_pp_id, v); // printf("%s: (pp id %ld) %p\n", p.first().c_str(), e._debug_pp_id, v);
d->d[boxString(p.first())] = v; d->d[boxString(p.first())] = v;
......
...@@ -97,6 +97,9 @@ ExcInfo* getFrameExcInfo(); ...@@ -97,6 +97,9 @@ ExcInfo* getFrameExcInfo();
// but just as slow if it's not. // but just as slow if it's not.
void updateFrameExcInfoIfNeeded(ExcInfo* latest); void updateFrameExcInfoIfNeeded(ExcInfo* latest);
void addDecrefInfoEntry(uint64_t ip, std::vector<class Location> location);
void removeDecrefInfoEntry(uint64_t ip);
struct FrameStackState { struct FrameStackState {
// This includes all # variables (but not the ! ones). // This includes all # variables (but not the ! ones).
// Therefore, it's not the same as the BoxedLocals. // Therefore, it's not the same as the BoxedLocals.
......
...@@ -30,7 +30,7 @@ bool LOG_BJIT_ASSEMBLY = 0; ...@@ -30,7 +30,7 @@ bool LOG_BJIT_ASSEMBLY = 0;
bool FORCE_INTERPRETER = false; bool FORCE_INTERPRETER = false;
bool FORCE_OPTIMIZE = false; bool FORCE_OPTIMIZE = false;
bool ENABLE_INTERPRETER = true; bool ENABLE_INTERPRETER = true;
bool ENABLE_BASELINEJIT = false; // XXX bool ENABLE_BASELINEJIT = true;
bool CONTINUE_AFTER_FATAL = false; bool CONTINUE_AFTER_FATAL = false;
bool SHOW_DISASM = false; bool SHOW_DISASM = false;
...@@ -50,8 +50,8 @@ bool ENABLE_TRACEBACKS = true; ...@@ -50,8 +50,8 @@ bool ENABLE_TRACEBACKS = true;
bool FORCE_LLVM_CAPI_CALLS = false; bool FORCE_LLVM_CAPI_CALLS = false;
bool FORCE_LLVM_CAPI_THROWS = false; bool FORCE_LLVM_CAPI_THROWS = false;
int OSR_THRESHOLD_INTERPRETER = 5; // XXX int OSR_THRESHOLD_INTERPRETER = 25;
int REOPT_THRESHOLD_INTERPRETER = 1; // XXX int REOPT_THRESHOLD_INTERPRETER = 25;
int OSR_THRESHOLD_BASELINE = 2500; int OSR_THRESHOLD_BASELINE = 2500;
int REOPT_THRESHOLD_BASELINE = 1500; int REOPT_THRESHOLD_BASELINE = 1500;
int OSR_THRESHOLD_T2 = 10000; int OSR_THRESHOLD_T2 = 10000;
......
# expected: statfail
# skip-if: '-O' in EXTRA_JIT_ARGS or '-n' in EXTRA_JIT_ARGS # skip-if: '-O' in EXTRA_JIT_ARGS or '-n' in EXTRA_JIT_ARGS
# statcheck: 4 == noninit_count('num_deopt') # statcheck: 4 == noninit_count('num_deopt')
# this used to hit an abort in our LLVM tier codegen # this used to hit an abort in our LLVM tier codegen
......
def f(x):
float(x)
try:
f(1)
f(1)
f(1)
f("str")
except Exception as e:
print e
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