Commit b29380d9 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Switch frame introspection to use vregs

parent 08704150
...@@ -172,7 +172,7 @@ public: ...@@ -172,7 +172,7 @@ public:
Box** getVRegs() { return vregs; } Box** getVRegs() { return vregs; }
const ScopeInfo* getScopeInfo() { return scope_info; } const ScopeInfo* getScopeInfo() { return scope_info; }
void addSymbol(InternedString name, Box* value, bool allow_duplicates); void addSymbol(int vreg, Box* value, bool allow_duplicates);
void setGenerator(Box* gen); void setGenerator(Box* gen);
void setPassedClosure(Box* closure); void setPassedClosure(Box* closure);
void setCreatedClosure(Box* closure); void setCreatedClosure(Box* closure);
...@@ -182,8 +182,8 @@ public: ...@@ -182,8 +182,8 @@ public:
friend struct pyston::ASTInterpreterJitInterface; friend struct pyston::ASTInterpreterJitInterface;
}; };
void ASTInterpreter::addSymbol(InternedString name, Box* new_value, bool allow_duplicates) { void ASTInterpreter::addSymbol(int vreg, Box* new_value, bool allow_duplicates) {
Box*& value = vregs[getVRegInfo().getVReg(name)]; Box*& value = vregs[vreg];
Box* old_value = value; Box* old_value = value;
value = incref(new_value); value = incref(new_value);
if (allow_duplicates) if (allow_duplicates)
...@@ -2076,6 +2076,10 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e ...@@ -2076,6 +2076,10 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e
ASTInterpreter interpreter(md, vregs, frame_state.frame_info); ASTInterpreter interpreter(md, vregs, frame_state.frame_info);
for (const auto& p : *frame_state.locals) { for (const auto& p : *frame_state.locals) {
if (p.first->cls == int_cls) {
int vreg = static_cast<BoxedInt*>(p.first)->n;
interpreter.addSymbol(vreg, p.second, false);
} else {
assert(p.first->cls == str_cls); assert(p.first->cls == str_cls);
auto name = static_cast<BoxedString*>(p.first)->s(); auto name = static_cast<BoxedString*>(p.first)->s();
if (name == PASSED_GENERATOR_NAME) { if (name == PASSED_GENERATOR_NAME) {
...@@ -2086,8 +2090,8 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e ...@@ -2086,8 +2090,8 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e
} else if (name == CREATED_CLOSURE_NAME) { } else if (name == CREATED_CLOSURE_NAME) {
interpreter.setCreatedClosure(p.second); interpreter.setCreatedClosure(p.second);
} else { } else {
InternedString interned = md->source->getInternedStrings().get(name); RELEASE_ASSERT(0, "");
interpreter.addSymbol(interned, p.second, false); }
} }
} }
...@@ -2101,7 +2105,7 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e ...@@ -2101,7 +2105,7 @@ extern "C" Box* astInterpretDeoptFromASM(FunctionMetadata* md, AST_expr* after_e
assert(asgn->targets[0]->type == AST_TYPE::Name); assert(asgn->targets[0]->type == AST_TYPE::Name);
auto name = ast_cast<AST_Name>(asgn->targets[0]); auto name = ast_cast<AST_Name>(asgn->targets[0]);
assert(name->id.s()[0] == '#'); assert(name->id.s()[0] == '#');
interpreter.addSymbol(name->id, expr_val, true); interpreter.addSymbol(name->vreg, expr_val, true);
break; break;
} else if (enclosing_stmt->type == AST_TYPE::Expr) { } else if (enclosing_stmt->type == AST_TYPE::Expr) {
auto expr = ast_cast<AST_Expr>(enclosing_stmt); auto expr = ast_cast<AST_Expr>(enclosing_stmt);
......
...@@ -76,9 +76,8 @@ BORROWED(BoxedCode*) FunctionMetadata::getCode() { ...@@ -76,9 +76,8 @@ BORROWED(BoxedCode*) FunctionMetadata::getCode() {
void FunctionMetadata::addVersion(CompiledFunction* compiled) { void FunctionMetadata::addVersion(CompiledFunction* compiled) {
assert(compiled); assert(compiled);
assert((compiled->spec != NULL) + (compiled->entry_descriptor != NULL) == 1); assert((compiled->spec != NULL) + (compiled->entry_descriptor != NULL) == 1);
assert(compiled->md == NULL); assert(compiled->md);
assert(compiled->code); assert(compiled->code);
compiled->md = this;
if (compiled->entry_descriptor == NULL) { if (compiled->entry_descriptor == NULL) {
bool could_have_speculations = (source.get() != NULL); bool could_have_speculations = (source.get() != NULL);
......
...@@ -1100,7 +1100,7 @@ CompiledFunction* doCompile(FunctionMetadata* md, SourceInfo* source, ParamNames ...@@ -1100,7 +1100,7 @@ CompiledFunction* doCompile(FunctionMetadata* md, SourceInfo* source, ParamNames
llvm_arg_types.push_back(g.llvm_value_type_ptr->getPointerTo()); llvm_arg_types.push_back(g.llvm_value_type_ptr->getPointerTo());
} }
CompiledFunction* cf = new CompiledFunction(NULL, spec, NULL, effort, exception_style, entry_descriptor); CompiledFunction* cf = new CompiledFunction(md, spec, NULL, effort, exception_style, entry_descriptor);
setPointersInCodeStorage(&cf->pointers_in_code); setPointersInCodeStorage(&cf->pointers_in_code);
// Make sure that the instruction memory keeps the module object alive. // Make sure that the instruction memory keeps the module object alive.
......
...@@ -682,10 +682,10 @@ void CompiledFunction::speculationFailed() { ...@@ -682,10 +682,10 @@ void CompiledFunction::speculationFailed() {
} }
std::unordered_set<CompiledFunction*> all_compiled_functions; std::unordered_set<CompiledFunction*> all_compiled_functions;
CompiledFunction::CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, void* code, EffortLevel effort, CompiledFunction::CompiledFunction(FunctionMetadata* md, FunctionSpecialization* spec, void* code, EffortLevel effort,
ExceptionStyle exception_style, const OSREntryDescriptor* entry_descriptor) ExceptionStyle exception_style, const OSREntryDescriptor* entry_descriptor)
: md(NULL), : md(md),
func(func), func(NULL),
effort(effort), effort(effort),
exception_style(exception_style), exception_style(exception_style),
spec(spec), spec(spec),
...@@ -829,6 +829,6 @@ void FunctionMetadata::addVersion(void* f, ConcreteCompilerType* rtn_type, ...@@ -829,6 +829,6 @@ void FunctionMetadata::addVersion(void* f, ConcreteCompilerType* rtn_type,
#endif #endif
FunctionSpecialization* spec = new FunctionSpecialization(processType(rtn_type), arg_types); FunctionSpecialization* spec = new FunctionSpecialization(processType(rtn_type), arg_types);
addVersion(new CompiledFunction(NULL, spec, f, EffortLevel::MAXIMAL, exception_style, NULL)); addVersion(new CompiledFunction(this, spec, f, EffortLevel::MAXIMAL, exception_style, NULL));
} }
} }
...@@ -62,7 +62,7 @@ IRGenState::IRGenState(FunctionMetadata* md, CompiledFunction* cf, SourceInfo* s ...@@ -62,7 +62,7 @@ IRGenState::IRGenState(FunctionMetadata* md, CompiledFunction* cf, SourceInfo* s
stmt(NULL), stmt(NULL),
scratch_size(0) { scratch_size(0) {
assert(cf->func); assert(cf->func);
assert(!cf->md); // in this case don't need to pass in sourceinfo assert(cf->md->source.get() == source_info); // I guess this is duplicate now
} }
IRGenState::~IRGenState() { IRGenState::~IRGenState() {
...@@ -2757,43 +2757,45 @@ public: ...@@ -2757,43 +2757,45 @@ public:
// For deopts we need to add the compiler created names to the stackmap // For deopts we need to add the compiler created names to the stackmap
if (ENABLE_FRAME_INTROSPECTION && pp->isDeopt()) { if (ENABLE_FRAME_INTROSPECTION && pp->isDeopt()) {
// TODO: don't need to use a sorted symbol table if we're explicitly recording the names!
// nice for debugging though.
typedef std::pair<InternedString, CompilerVariable*> Entry;
std::vector<Entry> sorted_symbol_table;
for (auto&& p : symbol_table) {
if (p.second)
sorted_symbol_table.push_back(Entry(vregs.getName(p.first), p.second));
}
// TODO: at some point it would be nice to pass these separately
auto source = irstate->getSourceInfo(); auto source = irstate->getSourceInfo();
if (source->is_generator) if (source->is_generator)
sorted_symbol_table.push_back( stackmap_args.push_back(irstate->getPassedGenerator());
Entry(source->getInternedStrings().get(PASSED_GENERATOR_NAME),
new ConcreteCompilerVariable(GENERATOR, irstate->getPassedGenerator())));
auto scoping = source->getScopeInfo(); auto scoping = source->getScopeInfo();
if (scoping->takesClosure()) if (scoping->takesClosure())
sorted_symbol_table.push_back( stackmap_args.push_back(irstate->getPassedClosure());
Entry(source->getInternedStrings().get(PASSED_CLOSURE_NAME),
new ConcreteCompilerVariable(CLOSURE, irstate->getPassedClosure())));
if (scoping->createsClosure()) if (scoping->createsClosure())
sorted_symbol_table.push_back( stackmap_args.push_back(irstate->getCreatedClosure());
Entry(source->getInternedStrings().get(CREATED_CLOSURE_NAME),
new ConcreteCompilerVariable(CLOSURE, irstate->getCreatedClosure()))); typedef std::pair<InternedString, CompilerVariable*> Entry;
for (auto&& p : symbol_table) {
int vreg = p.first;
if (!p.second)
continue;
std::sort(sorted_symbol_table.begin(), sorted_symbol_table.end(),
[](const Entry& lhs, const Entry& rhs) { return lhs.first < rhs.first; });
for (const auto& p : sorted_symbol_table) {
// We never have to include non compiler generated vars because the user visible variables are stored // We never have to include non compiler generated vars because the user visible variables are stored
// inside the vregs array. // inside the vregs array.
if (!p.first.isCompilerCreatedName()) if (vregs.isUserVisibleVReg(vreg)) {
assert(!vregs.getName(p.first).isCompilerCreatedName());
continue; continue;
}
CompilerVariable* v = p.second; CompilerVariable* v = p.second;
v->serializeToFrame(stackmap_args); v->serializeToFrame(stackmap_args);
pp->addFrameVar(p.first.s(), v->getType()); pp->addFrameVar(p.first, v->getType());
}
for (auto&& p : definedness_vars) {
if (!p.second)
continue;
if (vregs.isUserVisibleVReg(p.first))
continue;
assert(symbol_table[p.first]);
stackmap_args.push_back(p.second);
pp->addPotentiallyUndefined(p.first);
} }
} }
......
...@@ -30,10 +30,6 @@ ...@@ -30,10 +30,6 @@
namespace pyston { namespace pyston {
void PatchpointInfo::addFrameVar(llvm::StringRef name, CompilerType* type) {
frame_vars.push_back(FrameVarInfo({.name = name, .type = type }));
}
int ICSetupInfo::totalSize() const { int ICSetupInfo::totalSize() const {
if (isDeopt()) if (isDeopt())
return DEOPT_CALL_ONLY_SIZE; return DEOPT_CALL_ONLY_SIZE;
...@@ -74,30 +70,46 @@ bool StackMap::Record::Location::operator==(const StackMap::Record::Location& rh ...@@ -74,30 +70,46 @@ bool StackMap::Record::Location::operator==(const StackMap::Record::Location& rh
} }
void PatchpointInfo::parseLocationMap(StackMap::Record* r, LocationMap* map) { void PatchpointInfo::parseLocationMap(StackMap::Record* r, LocationMap* map) {
if (!this->isDeopt())
return;
assert(r->locations.size() == totalStackmapArgs()); assert(r->locations.size() == totalStackmapArgs());
int cur_arg = frameStackmapArgsStart(); int cur_arg = frameStackmapArgsStart();
// printf("parsing pp %ld:\n", reinterpret_cast<int64_t>(this)); // printf("parsing pp %ld:\n", reinterpret_cast<int64_t>(this));
for (FrameVarInfo& frame_var : frame_vars) { auto parse_type = [&](CompilerType* type) {
int num_args = frame_var.type->numFrameArgs(); int num_args = type->numFrameArgs();
llvm::SmallVector<StackMap::Record::Location, 1> locations; llvm::SmallVector<StackMap::Record::Location, 1> locations;
locations.append(r->locations.data() + cur_arg, r->locations.data() + cur_arg + num_args); locations.append(r->locations.data() + cur_arg, r->locations.data() + cur_arg + num_args);
cur_arg += num_args;
// printf("%s %d %d\n", frame_var.name.c_str(), r->locations[cur_arg].type, r->locations[cur_arg].regnum); return LocationMap::LocationTable::LocationEntry({._debug_pp_id = (uint64_t) this,
map->names[frame_var.name].locations.push_back(
LocationMap::LocationTable::LocationEntry({._debug_pp_id = (uint64_t) this,
.offset = r->offset, .offset = r->offset,
.length = patchpointSize(), .length = patchpointSize(),
.type = frame_var.type, .type = type,
.locations = std::move(locations) })); .locations = std::move(locations) });
};
cur_arg += num_args;
auto&& source = parentFunction()->md->source;
if (source->is_generator)
map->generator.locations.push_back(parse_type(GENERATOR));
if (source->getScopeInfo()->takesClosure())
map->passed_closure.locations.push_back(parse_type(CLOSURE));
if (source->getScopeInfo()->createsClosure())
map->created_closure.locations.push_back(parse_type(CLOSURE));
for (FrameVarInfo& frame_var : frame_info_desc.vars) {
map->vars[frame_var.vreg].locations.push_back(parse_type(frame_var.type));
} }
assert(cur_arg - frameStackmapArgsStart() == numFrameStackmapArgs()); for (int vreg : frame_info_desc.potentially_undefined) {
map->definedness_vars[vreg].locations.push_back(parse_type(BOOL));
}
ASSERT(cur_arg - frameStackmapArgsStart() == numFrameStackmapArgs(), "%d %d %d", cur_arg, frameStackmapArgsStart(),
numFrameStackmapArgs());
} }
static int extractScratchOffset(PatchpointInfo* pp, StackMap::Record* r) { static int extractScratchOffset(PatchpointInfo* pp, StackMap::Record* r) {
......
...@@ -96,9 +96,13 @@ public: ...@@ -96,9 +96,13 @@ public:
struct PatchpointInfo { struct PatchpointInfo {
public: public:
struct FrameVarInfo { struct FrameVarInfo {
llvm::StringRef name; int vreg;
CompilerType* type; CompilerType* type;
}; };
struct FrameInfoDesc {
std::vector<FrameVarInfo> vars;
llvm::SmallVector<int, 2> potentially_undefined;
};
private: private:
CompiledFunction* const parent_cf; CompiledFunction* const parent_cf;
...@@ -106,10 +110,10 @@ private: ...@@ -106,10 +110,10 @@ private:
int num_ic_stackmap_args; int num_ic_stackmap_args;
int num_frame_stackmap_args; int num_frame_stackmap_args;
bool is_frame_info_stackmap; bool is_frame_info_stackmap;
std::vector<FrameVarInfo> frame_vars;
unsigned int id; unsigned int id;
FrameInfoDesc frame_info_desc;
PatchpointInfo(CompiledFunction* parent_cf, const ICSetupInfo* icinfo, int num_ic_stackmap_args) PatchpointInfo(CompiledFunction* parent_cf, const ICSetupInfo* icinfo, int num_ic_stackmap_args)
: parent_cf(parent_cf), : parent_cf(parent_cf),
icinfo(icinfo), icinfo(icinfo),
...@@ -125,7 +129,7 @@ public: ...@@ -125,7 +129,7 @@ public:
int patchpointSize(); int patchpointSize();
CompiledFunction* parentFunction() { return parent_cf; } CompiledFunction* parentFunction() { return parent_cf; }
const std::vector<FrameVarInfo>& getFrameVars() { return frame_vars; } FrameInfoDesc& getFrameDesc() { return frame_info_desc; }
int scratchStackmapArg() { return 0; } int scratchStackmapArg() { return 0; }
int scratchSize() { return isDeopt() ? MAX_FRAME_SPILLS * sizeof(void*) : 96; } int scratchSize() { return isDeopt() ? MAX_FRAME_SPILLS * sizeof(void*) : 96; }
...@@ -133,7 +137,11 @@ public: ...@@ -133,7 +137,11 @@ public:
bool isFrameInfoStackmap() const { return is_frame_info_stackmap; } bool isFrameInfoStackmap() const { return is_frame_info_stackmap; }
int numFrameSpillsSupported() const { return isDeopt() ? MAX_FRAME_SPILLS : 0; } int numFrameSpillsSupported() const { return isDeopt() ? MAX_FRAME_SPILLS : 0; }
void addFrameVar(llvm::StringRef name, CompilerType* type); void addFrameVar(int vreg, CompilerType* type) {
frame_info_desc.vars.push_back(FrameVarInfo({.vreg = vreg, .type = type }));
}
void addPotentiallyUndefined(int vreg) { frame_info_desc.potentially_undefined.push_back(vreg); }
void setNumFrameArgs(int num_frame_args) { void setNumFrameArgs(int num_frame_args) {
assert(num_frame_stackmap_args == -1); assert(num_frame_stackmap_args == -1);
num_frame_stackmap_args = num_frame_args; num_frame_stackmap_args = num_frame_args;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringMap.h"
...@@ -101,7 +102,9 @@ public: ...@@ -101,7 +102,9 @@ public:
} }
}; };
llvm::StringMap<LocationTable> names; llvm::DenseMap<int, LocationTable> vars;
LocationTable generator, passed_closure, created_closure;
llvm::DenseMap<int, LocationTable> definedness_vars;
}; };
StackMap* parseStackMap(); StackMap* parseStackMap();
......
...@@ -329,7 +329,9 @@ public: ...@@ -329,7 +329,9 @@ public:
} }
} }
llvm::ArrayRef<StackMap::Record::Location> findLocations(llvm::StringRef name) { #if 0
//llvm::ArrayRef<StackMap::Record::Location> findLocations(llvm::StringRef name) {
llvm::ArrayRef<StackMap::Record::Location> findLocations(const LocationMap::LocationTable& table) {
assert(id.type == PythonFrameId::COMPILED); assert(id.type == PythonFrameId::COMPILED);
CompiledFunction* cf = getCF(); CompiledFunction* cf = getCF();
...@@ -338,15 +340,14 @@ public: ...@@ -338,15 +340,14 @@ public:
assert(ip > cf->code_start); assert(ip > cf->code_start);
unsigned offset = ip - cf->code_start; unsigned offset = ip - cf->code_start;
assert(cf->location_map); //const LocationMap::LocationTable& table = cf->location_map->names[name];
const LocationMap::LocationTable& table = cf->location_map->names[name];
auto entry = table.findEntry(offset); auto entry = table.findEntry(offset);
if (!entry) if (!entry)
return {}; return {};
assert(entry->locations.size()); assert(entry->locations.size());
return entry->locations; return entry->locations;
} }
#endif
AST_stmt* getCurrentStatement() { AST_stmt* getCurrentStatement() {
assert(getFrameInfo()->stmt); assert(getFrameInfo()->stmt);
...@@ -870,20 +871,37 @@ DeoptState getDeoptState() { ...@@ -870,20 +871,37 @@ DeoptState getDeoptState() {
// We have to detect + ignore any entries for variables that // We have to detect + ignore any entries for variables that
// could have been defined (so they have entries) but aren't (so the // could have been defined (so they have entries) but aren't (so the
// entries point to uninitialized memory). // entries point to uninitialized memory).
std::unordered_set<std::string> is_undefined; std::unordered_set<int> is_undefined;
for (const auto& p : cf->location_map->names) { auto readEntry = [&](const LocationMap::LocationTable::LocationEntry* e) {
if (!startswith(p.first(), "!is_defined_")) auto locs = e->locations;
continue;
llvm::SmallVector<uint64_t, 1> vals;
// printf("%s: %s\n", p.first().c_str(), e.type->debugName().c_str());
for (auto& loc : locs) {
vals.push_back(frame_iter->readLocation(loc));
}
// this returns an owned reference so we don't incref it
Box* v = e->type->deserializeFromFrame(vals);
return v;
};
if (auto e = cf->location_map->generator.findEntry(offset))
d->d[boxString(PASSED_GENERATOR_NAME)] = readEntry(e);
if (auto e = cf->location_map->passed_closure.findEntry(offset))
d->d[boxString(PASSED_CLOSURE_NAME)] = readEntry(e);
if (auto e = cf->location_map->created_closure.findEntry(offset))
d->d[boxString(CREATED_CLOSURE_NAME)] = readEntry(e);
for (const auto& p : cf->location_map->definedness_vars) {
auto e = p.second.findEntry(offset); auto e = p.second.findEntry(offset);
if (e) { if (e) {
auto locs = e->locations; Box* b = readEntry(e);
AUTO_DECREF(b);
assert(locs.size() == 1); if (b == Py_False)
uint64_t v = frame_iter->readLocation(locs[0]); is_undefined.insert(p.first);
if ((v & 1) == 0)
is_undefined.insert(p.first().substr(12));
} }
} }
...@@ -893,40 +911,23 @@ DeoptState getDeoptState() { ...@@ -893,40 +911,23 @@ DeoptState getDeoptState() {
// But deopts are so rare it's not really worth it. // But deopts are so rare it's not really worth it.
Box** vregs = frame_iter->getFrameInfo()->vregs; Box** vregs = frame_iter->getFrameInfo()->vregs;
for (const auto& p : cf->md->source->cfg->getVRegInfo().getUserVisibleSymVRegMap()) { for (const auto& p : cf->md->source->cfg->getVRegInfo().getUserVisibleSymVRegMap()) {
if (is_undefined.count(p.first.s())) if (is_undefined.count(p.second))
continue; assert(0);
Box* v = vregs[p.second]; Box* v = vregs[p.second];
if (!v) if (!v)
continue; continue;
assert(!d->d.count(p.first.getBox())); d->d[boxInt(p.second)] = incref(v);
d->d[incref(p.first.getBox())] = incref(v);
} }
for (const auto& p : cf->location_map->names) { for (const auto& p : cf->location_map->vars) {
if (p.first()[0] == '!') if (is_undefined.count(p.first))
continue;
if (is_undefined.count(p.first()))
continue; continue;
auto e = p.second.findEntry(offset); auto e = p.second.findEntry(offset);
if (e) { if (e)
auto locs = e->locations; d->d[boxInt(p.first)] = readEntry(e);
llvm::SmallVector<uint64_t, 1> vals;
// printf("%s: %s\n", p.first().c_str(), e.type->debugName().c_str());
for (auto& loc : locs) {
vals.push_back(frame_iter->readLocation(loc));
}
// this returns an owned reference so we don't incref it
Box* v = e->type->deserializeFromFrame(vals);
// printf("%s: (pp id %ld) %p\n", p.first().c_str(), e._debug_pp_id, v);
d->d[boxString(p.first())] = v;
}
} }
} else { } else {
abort(); abort();
......
...@@ -379,7 +379,7 @@ public: ...@@ -379,7 +379,7 @@ public:
// List of metadata objects for ICs inside this compilation // List of metadata objects for ICs inside this compilation
std::vector<ICInfo*> ics; std::vector<ICInfo*> ics;
CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, void* code, EffortLevel effort, CompiledFunction(FunctionMetadata* func, FunctionSpecialization* spec, void* code, EffortLevel effort,
ExceptionStyle exception_style, const OSREntryDescriptor* entry_descriptor); ExceptionStyle exception_style, const OSREntryDescriptor* entry_descriptor);
ConcreteCompilerType* getReturnType(); ConcreteCompilerType* getReturnType();
......
...@@ -137,6 +137,8 @@ extern "C" void xdecrefAndRethrow(void* cxa_ptr, int num, ...) { ...@@ -137,6 +137,8 @@ extern "C" void xdecrefAndRethrow(void* cxa_ptr, int num, ...) {
} }
extern "C" Box* deopt(AST_expr* expr, Box* value) { extern "C" Box* deopt(AST_expr* expr, Box* value) {
ASSERT(ENABLE_FRAME_INTROSPECTION, "deopt will not work with frame introspection turned off");
STAT_TIMER(t0, "us_timer_deopt", 10); STAT_TIMER(t0, "us_timer_deopt", 10);
static StatCounter num_deopt("num_deopt"); static StatCounter num_deopt("num_deopt");
......
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