Commit fb2eef6e authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #814 from kmod/throw_capis3

Be able to jit functions that throw CAPI exceptions
parents 24041726 8b122bfe
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "../test/integration/django"))
sys.path.append(os.path.join(os.path.dirname(__file__), "../test/testsuite/lib/django"))
from django.template.base import Origin, Template, Context, TemplateDoesNotExist
from django.conf import settings
......
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "../test/integration/pyxl/"))
sys.path.append(os.path.join(os.path.dirname(__file__), "../test/testsuite/lib/pyxl/"))
from pyxl.codec.register import pyxl_transform_string
......
......@@ -2,7 +2,7 @@
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "../test/integration/sqlalchemy/lib"))
sys.path.append(os.path.join(os.path.dirname(__file__), "../test/testsuite/lib/sqlalchemy/lib"))
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
......
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "../test/integration/sqlalchemy/lib"))
sys.path.append(os.path.join(os.path.dirname(__file__), "../test/testsuite/lib/sqlalchemy/lib"))
from sqlalchemy import Column, ForeignKey, Integer, String, Table, MetaData
from sqlalchemy.ext.declarative import declarative_base
......
......@@ -743,12 +743,19 @@ Box* ASTInterpreter::doOSR(AST_Jump* node) {
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_jitted_code");
CompiledFunction* partial_func = compilePartialFuncInternal(&exit);
auto arg_tuple = getTupleFromArgsArray(&arg_array[0], arg_array.size());
Box* r = partial_func->call(std::get<0>(arg_tuple), std::get<1>(arg_tuple), std::get<2>(arg_tuple),
std::get<3>(arg_tuple));
assert(r);
return r;
if (partial_func->exception_style == CXX) {
assert(r);
return r;
} else {
if (!r)
throwCAPIException();
return r;
}
}
Value ASTInterpreter::visit_invoke(AST_Invoke* node) {
......@@ -1725,14 +1732,24 @@ Box* astInterpretFunction(CLFunction* clfunc, int nargs, Box* closure, Box* gene
clfunc->dependent_interp_callsites.invalidateAll();
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_jitted_code");
Box* r;
if (closure && generator)
return optimized->closure_generator_call((BoxedClosure*)closure, (BoxedGenerator*)generator, arg1, arg2,
arg3, args);
r = optimized->closure_generator_call((BoxedClosure*)closure, (BoxedGenerator*)generator, arg1, arg2, arg3,
args);
else if (closure)
return optimized->closure_call((BoxedClosure*)closure, arg1, arg2, arg3, args);
r = optimized->closure_call((BoxedClosure*)closure, arg1, arg2, arg3, args);
else if (generator)
return optimized->generator_call((BoxedGenerator*)generator, arg1, arg2, arg3, args);
return optimized->call(arg1, arg2, arg3, args);
r = optimized->generator_call((BoxedGenerator*)generator, arg1, arg2, arg3, args);
else
r = optimized->call(arg1, arg2, arg3, args);
if (optimized->exception_style == CXX)
return r;
else {
if (!r)
throwCAPIException();
return r;
}
}
Box** vregs = NULL;
......
......@@ -337,9 +337,7 @@ public:
CompilerVariable* slice) override {
ConcreteCompilerVariable* converted_slice = slice->makeConverted(emitter, slice->getBoxType());
ExceptionStyle target_exception_style = CXX;
if (FORCE_LLVM_CAPI || info.unw_info.capi_exc_dest)
target_exception_style = CAPI;
ExceptionStyle target_exception_style = info.preferredExceptionStyle();
bool do_patchpoint = ENABLE_ICGETITEMS;
llvm::Value* rtn;
......@@ -500,9 +498,7 @@ CompilerVariable* UnknownType::getattr(IREmitter& emitter, const OpInfo& info, C
llvm::Value* rtn_val = NULL;
ExceptionStyle target_exception_style = CXX;
if (info.unw_info.capi_exc_dest || (!cls_only && FORCE_LLVM_CAPI))
target_exception_style = CAPI;
ExceptionStyle target_exception_style = cls_only ? CXX : info.preferredExceptionStyle();
llvm::Value* llvm_func;
void* raw_func;
......@@ -656,9 +652,7 @@ CompilerVariable* UnknownType::call(IREmitter& emitter, const OpInfo& info, Conc
bool pass_keywords = (argspec.num_keywords != 0);
int npassed_args = argspec.totalPassed();
ExceptionStyle exception_style = ((FORCE_LLVM_CAPI && !info.unw_info.cxx_exc_dest) || info.unw_info.capi_exc_dest)
? ExceptionStyle::CAPI
: ExceptionStyle::CXX;
ExceptionStyle exception_style = info.preferredExceptionStyle();
llvm::Value* func;
if (pass_keywords)
......@@ -691,10 +685,7 @@ CompilerVariable* UnknownType::callattr(IREmitter& emitter, const OpInfo& info,
bool pass_keywords = (flags.argspec.num_keywords != 0);
int npassed_args = flags.argspec.totalPassed();
ExceptionStyle exception_style = ((FORCE_LLVM_CAPI && !info.unw_info.cxx_exc_dest && !flags.null_on_nonexistent)
|| info.unw_info.capi_exc_dest)
? ExceptionStyle::CAPI
: ExceptionStyle::CXX;
ExceptionStyle exception_style = flags.null_on_nonexistent ? CXX : info.preferredExceptionStyle();
if (exception_style == CAPI)
assert(!flags.null_on_nonexistent); // Will conflict with CAPI's null-on-exception
......@@ -1522,7 +1513,7 @@ public:
if (canStaticallyResolveGetattrs()) {
Box* rtattr = typeLookup(cls, attr, nullptr);
if (rtattr == NULL) {
ExceptionStyle exception_style = (FORCE_LLVM_CAPI || info.unw_info.capi_exc_dest) ? CAPI : CXX;
ExceptionStyle exception_style = info.preferredExceptionStyle();
llvm::Value* raise_func = exception_style == CXX ? g.funcs.raiseAttributeErrorStr
: g.funcs.raiseAttributeErrorStrCapi;
llvm::CallSite call = emitter.createCall3(
......@@ -1619,15 +1610,13 @@ public:
"%d", info.unw_info.current_stmt->lineno);
CompiledFunction* cf = NULL;
CompiledFunction* best_exception_mismatch = NULL;
bool found = false;
// TODO have to find the right version.. similar to resolveclfunc?
for (int i = 0; i < cl->versions.size(); i++) {
cf = cl->versions[i];
assert(cf->spec->arg_types.size() == cl->numReceivedArgs());
if (cf->exception_style != exception_style)
continue;
bool fits = true;
for (int j = 0; j < args.size(); j++) {
if (!args[j]->canConvertTo(cf->spec->arg_types[j + 1])) {
......@@ -1638,14 +1627,22 @@ public:
if (!fits)
continue;
if (cf->exception_style != exception_style) {
if (!best_exception_mismatch)
best_exception_mismatch = cf;
continue;
}
found = true;
break;
}
if (!found && exception_style == CAPI) {
std::string name = g.func_addr_registry.getFuncNameAtAddress(cl->versions[0]->code, true);
RELEASE_ASSERT(0, "Please define a capi variant for %s", name.c_str());
if (!found) {
assert(best_exception_mismatch);
cf = best_exception_mismatch;
found = true;
}
RELEASE_ASSERT(found, "");
RELEASE_ASSERT(cf->code, "");
......@@ -1717,10 +1714,7 @@ public:
CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var, BoxedString* attr,
CallattrFlags flags, const std::vector<CompilerVariable*>& args,
const std::vector<BoxedString*>* keyword_names) override {
ExceptionStyle exception_style = CXX;
// Not safe to force-capi here since most of the functions won't have capi variants:
if (/*FORCE_LLVM_CAPI ||*/ info.unw_info.capi_exc_dest)
exception_style = CAPI;
ExceptionStyle exception_style = info.preferredExceptionStyle();
ConcreteCompilerVariable* called_constant = tryCallattrConstant(
emitter, info, var, attr, flags.cls_only, flags.argspec, args, keyword_names, NULL, exception_style);
......@@ -1783,9 +1777,7 @@ public:
static BoxedString* attr = internStringImmortal("__getitem__");
bool no_attribute = false;
ExceptionStyle exception_style = CXX;
if (FORCE_LLVM_CAPI || info.unw_info.capi_exc_dest)
exception_style = CAPI;
ExceptionStyle exception_style = info.preferredExceptionStyle();
ConcreteCompilerVariable* called_constant = tryCallattrConstant(
emitter, info, var, attr, true, ArgPassSpec(1, 0, 0, 0), { slice }, NULL, &no_attribute, exception_style);
......@@ -2293,9 +2285,7 @@ public:
rtn->incvref();
return rtn;
} else {
ExceptionStyle target_exception_style = CXX;
if (FORCE_LLVM_CAPI || info.unw_info.capi_exc_dest)
target_exception_style = CAPI;
ExceptionStyle target_exception_style = info.preferredExceptionStyle();
if (target_exception_style == CAPI) {
llvm::CallSite call = emitter.createCall(info.unw_info, g.funcs.raiseIndexErrorStrCapi,
......
......@@ -495,6 +495,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
std::unordered_map<CFGBlock*, ConcreteSymbolTable*> phi_ending_symbol_tables;
typedef std::unordered_map<InternedString, std::pair<ConcreteCompilerType*, llvm::PHINode*>> PHITable;
std::unordered_map<CFGBlock*, PHITable*> created_phis;
std::unordered_map<CFGBlock*, llvm::SmallVector<IRGenerator::ExceptionState, 2>> incoming_exception_state;
CFGBlock* initial_block = NULL;
if (entry_descriptor) {
......@@ -759,6 +760,11 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
}
}
auto exc_it = incoming_exception_state.find(block);
if (exc_it != incoming_exception_state.end()) {
generator->setIncomingExceptionState(exc_it->second);
}
// Generate loop safepoints on backedges.
for (CFGBlock* predecessor : block->predecessors) {
if (predecessor->idx > block->idx) {
......@@ -777,6 +783,15 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
phi_ending_symbol_tables[block] = ending_st.phi_symbol_table;
llvm_exit_blocks[block] = ending_st.ending_block;
if (ending_st.exception_state.size()) {
AST_stmt* last_stmt = block->body.back();
assert(last_stmt->type == AST_TYPE::Invoke);
CFGBlock* exc_block = ast_cast<AST_Invoke>(last_stmt)->exc_dest;
assert(!incoming_exception_state.count(exc_block));
incoming_exception_state.insert(std::make_pair(exc_block, ending_st.exception_state));
}
if (into_hax.count(block))
ASSERT(ending_st.symbol_table->size() == 0, "%d", block->idx);
}
......@@ -952,7 +967,7 @@ static std::string getUniqueFunctionName(std::string nameprefix, EffortLevel eff
CompiledFunction* doCompile(CLFunction* clfunc, SourceInfo* source, ParamNames* param_names,
const OSREntryDescriptor* entry_descriptor, EffortLevel effort,
FunctionSpecialization* spec, std::string nameprefix) {
ExceptionStyle exception_style, FunctionSpecialization* spec, std::string nameprefix) {
Timer _t("in doCompile");
Timer _t2;
long irgen_us = 0;
......@@ -1015,8 +1030,7 @@ CompiledFunction* doCompile(CLFunction* clfunc, SourceInfo* source, ParamNames*
}
}
CompiledFunction* cf = new CompiledFunction(NULL, spec, NULL, effort, ExceptionStyle::CXX, entry_descriptor);
CompiledFunction* cf = new CompiledFunction(NULL, spec, NULL, effort, exception_style, entry_descriptor);
// Make sure that the instruction memory keeps the module object alive.
// TODO: implement this for real
......
......@@ -20,6 +20,7 @@
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/IRBuilder.h"
#include "core/options.h"
#include "core/types.h"
namespace pyston {
......@@ -33,18 +34,18 @@ struct UnwindInfo {
public:
AST_stmt* current_stmt;
llvm::BasicBlock* capi_exc_dest;
llvm::BasicBlock* cxx_exc_dest;
llvm::BasicBlock* exc_dest;
bool hasHandler() const { return cxx_exc_dest != NULL || capi_exc_dest != NULL; }
bool hasHandler() const { return exc_dest != NULL; }
UnwindInfo(AST_stmt* current_stmt, llvm::BasicBlock* capi_exc_dest, llvm::BasicBlock* cxx_exc_dest)
: current_stmt(current_stmt), capi_exc_dest(capi_exc_dest), cxx_exc_dest(cxx_exc_dest) {}
UnwindInfo(AST_stmt* current_stmt, llvm::BasicBlock* exc_dest) : current_stmt(current_stmt), exc_dest(exc_dest) {}
ExceptionStyle preferredExceptionStyle() const;
// Risky! This means that we can't unwind from this location, and should be used in the
// rare case that there are language-specific reasons that the statement should not unwind
// (ex: loading function arguments into the appropriate scopes).
static UnwindInfo cantUnwind() { return UnwindInfo(NULL, NULL, NULL); }
static UnwindInfo cantUnwind() { return UnwindInfo(NULL, NULL); }
};
// TODO get rid of this
......@@ -112,7 +113,7 @@ bool isIsDefinedName(llvm::StringRef name);
CompiledFunction* doCompile(CLFunction* clfunc, SourceInfo* source, ParamNames* param_names,
const OSREntryDescriptor* entry_descriptor, EffortLevel effort,
FunctionSpecialization* spec, std::string nameprefix);
ExceptionStyle exception_style, FunctionSpecialization* spec, std::string nameprefix);
// A common pattern is to branch based off whether a variable is defined but only if it is
// potentially-undefined. If it is potentially-undefined, we have to generate control-flow
......@@ -146,6 +147,8 @@ public:
: effort(effort), type_recorder(type_recorder), unw_info(unw_info) {}
TypeRecorder* getTypeRecorder() const { return type_recorder; }
ExceptionStyle preferredExceptionStyle() const { return unw_info.preferredExceptionStyle(); }
};
}
......
......@@ -197,6 +197,12 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
ASSERT(f->versions.size() < 20, "%s %ld", name.c_str(), f->versions.size());
ExceptionStyle exception_style = CXX;
if (FORCE_LLVM_CAPI_THROWS)
exception_style = CAPI;
if (name == "next")
exception_style = CAPI;
if (VERBOSITY("irgen") >= 1) {
std::string s;
llvm::raw_string_ostream ss(s);
......@@ -223,7 +229,8 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
ss << "\033[" << colors[(int)effort] << ";1mDoing OSR-entry partial compile of " << source->fn << ":"
<< name << ", starting with backedge to block " << entry_descriptor->backedge->target->idx;
}
ss << " at effort level " << (int)effort << '\n';
ss << " at effort level " << (int)effort << " with exception style "
<< (exception_style == CXX ? "C++" : "CAPI") << '\n';
if (entry_descriptor && VERBOSITY("irgen") >= 2) {
for (const auto& p : entry_descriptor->args) {
......@@ -241,8 +248,7 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
}
CompiledFunction* cf = doCompile(f, source, &f->param_names, entry_descriptor, effort, spec, name);
CompiledFunction* cf = doCompile(f, source, &f->param_names, entry_descriptor, effort, exception_style, spec, name);
compileIR(cf, effort);
f->addVersion(cf);
......@@ -822,7 +828,9 @@ extern "C" CompiledFunction* reoptCompiledFuncInternal(CompiledFunction* cf) {
extern "C" char* reoptCompiledFunc(CompiledFunction* cf) {
return (char*)reoptCompiledFuncInternal(cf)->code;
CompiledFunction* new_cf = reoptCompiledFuncInternal(cf);
assert(new_cf->exception_style == cf->exception_style);
return (char*)new_cf->code;
}
CLFunction* createRTFunction(int num_args, int num_defaults, bool takes_varargs, bool takes_kwargs,
......
......@@ -94,67 +94,24 @@ llvm::Value* IRGenState::getScratchSpace(int min_bytes) {
return scratch_space;
}
// This function is where we decide whether to have a certain operation use CAPI or CXX exceptions.
// FIXME It's a bit messy at the moment because this requires coordinating between a couple different
// parts: we need to make sure that the associated landingpad will catch the right kind of exception,
// and we need to make sure that we can actually emit this statement using capi-exceptions.
// It doesn't really belong on the IRGenState, but it's here so that we can access this state from
// separate basic blocks (the IRGenerator only exists for a single bb).
ExceptionStyle IRGenState::getLandingpadStyle(AST_Invoke* invoke) {
assert(!landingpad_styles.count(invoke->exc_dest));
ExceptionStyle& r = landingpad_styles[invoke->exc_dest];
// printf("Added %d\n", invoke->exc_dest->idx);
r = CXX; // default
assert(invoke->stmt->cxx_exception_count == 0); // could be ok but would be unexpected
// First, check if we think it makes sense:
bool should = (invoke->cxx_exception_count >= 10 || invoke->stmt->type == AST_TYPE::Raise);
if (!should)
return r;
// Second, check if we are able to do it:
// (not all code paths support capi exceptions yet)
if (invoke->stmt->type == AST_TYPE::Raise) {
AST_Raise* raise_stmt = ast_cast<AST_Raise>(invoke->stmt);
// Currently can't do a re-raise with a capi exception:
if (raise_stmt->arg0 && !raise_stmt->arg2)
r = CAPI;
else
r = CXX;
return r;
}
ExceptionStyle UnwindInfo::preferredExceptionStyle() const {
if (FORCE_LLVM_CAPI_CALLS)
return CAPI;
AST_expr* expr = NULL;
if (invoke->stmt->type == AST_TYPE::Assign) {
expr = ast_cast<AST_Assign>(invoke->stmt)->value;
} else if (invoke->stmt->type == AST_TYPE::Expr) {
expr = ast_cast<AST_Expr>(invoke->stmt)->value;
}
if (!expr)
return r;
if (expr->type == AST_TYPE::Call) {
r = CAPI;
return r;
}
if (expr->type == AST_TYPE::Attribute || expr->type == AST_TYPE::Subscript) {
r = CAPI;
return r;
}
// Some expression type we haven't added yet -- might be worth looking into.
r = CXX;
return r;
}
ExceptionStyle IRGenState::getLandingpadStyle(CFGBlock* block) {
ASSERT(landingpad_styles.count(block), "%d", block->idx);
return landingpad_styles[block];
// TODO: I think this makes more sense as a relative percentage rather
// than an absolute threshold, but currently we don't count how many
// times a statement was executed but didn't throw.
//
// In theory this means that eventually anything that throws will be viewed
// as a highly-throwing statement, but I think that this is less bad than
// it might be because the denominator will be roughly fixed since we will
// tend to run this check after executing the statement a somewhat-fixed
// number of times.
// We might want to zero these out after we are done compiling, though.
if (current_stmt->cxx_exception_count >= 10)
return CAPI;
return CXX;
}
static llvm::Value* getClosureParentGep(IREmitter& emitter, llvm::Value* closure) {
......@@ -296,13 +253,26 @@ private:
llvm::CallSite emitCall(const UnwindInfo& unw_info, llvm::Value* callee, const std::vector<llvm::Value*>& args,
ExceptionStyle target_exception_style) {
if (unw_info.hasHandler() && target_exception_style == CXX) {
assert(unw_info.cxx_exc_dest);
if (target_exception_style == CXX && (unw_info.hasHandler() || irstate->getExceptionStyle() == CAPI)) {
// Create the invoke:
llvm::BasicBlock* normal_dest
= llvm::BasicBlock::Create(g.context, curblock->getName(), irstate->getLLVMFunction());
llvm::BasicBlock* final_exc_dest;
if (unw_info.hasHandler()) {
final_exc_dest = unw_info.exc_dest;
} else {
assert(irstate->getExceptionStyle() == CAPI && "shoudn't have bothered creating an invoke");
final_exc_dest = NULL; // signal to reraise as a capi exception
}
llvm::BasicBlock* exc_dest = irgenerator->getCXXExcDest(final_exc_dest);
normal_dest->moveAfter(curblock);
llvm::InvokeInst* rtn = getBuilder()->CreateInvoke(callee, normal_dest, unw_info.cxx_exc_dest, args);
llvm::InvokeInst* rtn = getBuilder()->CreateInvoke(callee, normal_dest, exc_dest, args);
// Normal case:
getBuilder()->SetInsertPoint(normal_dest);
curblock = normal_dest;
return rtn;
......@@ -493,37 +463,12 @@ public:
= llvm::BasicBlock::Create(g.context, curblock->getName(), irstate->getLLVMFunction());
normal_dest->moveAfter(curblock);
llvm::BasicBlock* exc_dest;
bool exc_caught;
if (unw_info.capi_exc_dest) {
exc_dest = unw_info.capi_exc_dest;
exc_caught = true;
} else {
exc_dest = llvm::BasicBlock::Create(g.context, curblock->getName() + "_exc", irstate->getLLVMFunction());
exc_dest->moveAfter(curblock);
exc_caught = false;
}
llvm::BasicBlock* exc_dest = irgenerator->getCAPIExcDest(unw_info.exc_dest, unw_info.current_stmt);
assert(returned_val->getType() == exc_val->getType());
llvm::Value* check_val = getBuilder()->CreateICmpEQ(returned_val, exc_val);
llvm::BranchInst* nullcheck = getBuilder()->CreateCondBr(check_val, exc_dest, normal_dest);
setCurrentBasicBlock(exc_dest);
getBuilder()->CreateCall2(g.funcs.capiExcCaughtInJit,
embedRelocatablePtr(unw_info.current_stmt, g.llvm_aststmt_type_ptr),
embedRelocatablePtr(irstate->getSourceInfo(), g.i8_ptr));
if (!exc_caught) {
if (unw_info.cxx_exc_dest) {
// TODO: I'm not sure this gets the tracebacks quite right. this is only for testing though:
assert(FORCE_LLVM_CAPI && "this shouldn't happen in non-FORCE mode");
createCall(unw_info, g.funcs.reraiseJitCapiExc);
} else {
getBuilder()->CreateCall(g.funcs.reraiseJitCapiExc);
}
getBuilder()->CreateUnreachable();
}
setCurrentBasicBlock(normal_dest);
}
......@@ -582,6 +527,19 @@ private:
CFGBlock* myblock;
TypeAnalysis* types;
// These are some special values used for passing exception data between blocks;
// this transfer is not explicitly represented in the CFG which is why it has special
// handling here. ie these variables are how we handle the special "invoke->landingpad"
// value transfer, which doesn't involve the normal symbol name handling.
//
// These are the values that are incoming to a landingpad block:
llvm::SmallVector<ExceptionState, 2> incoming_exc_state;
// These are the values that are outgoing of an invoke block:
llvm::SmallVector<ExceptionState, 2> outgoing_exc_state;
llvm::DenseMap<llvm::BasicBlock*, llvm::BasicBlock*> cxx_exc_dests;
llvm::DenseMap<llvm::BasicBlock*, llvm::BasicBlock*> capi_exc_dests;
llvm::DenseMap<llvm::BasicBlock*, AST_stmt*> capi_current_statements;
enum State {
RUNNING, // normal
DEAD, // passed a Return statement; still syntatically valid but the code should not be compiled
......@@ -645,7 +603,7 @@ private:
curblock = deopt_bb;
emitter.getBuilder()->SetInsertPoint(curblock);
llvm::Value* v = emitter.createCall2(UnwindInfo(current_statement, NULL, NULL), g.funcs.deopt,
llvm::Value* v = emitter.createCall2(UnwindInfo(current_statement, NULL), g.funcs.deopt,
embedRelocatablePtr(node, g.llvm_aststmt_type_ptr), node_value);
emitter.getBuilder()->CreateRet(v);
......@@ -695,62 +653,46 @@ private:
return boolFromI1(emitter, v);
}
case AST_LangPrimitive::LANDINGPAD: {
llvm::Value* exc_type;
llvm::Value* exc_value;
llvm::Value* exc_traceback;
if (irstate->getLandingpadStyle(myblock) == CXX) {
// llvm::Function* _personality_func = g.stdlib_module->getFunction("__py_personality_v0");
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 = 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();
exc_type = builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 0));
exc_value = builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 1));
exc_traceback
= builder->CreateLoad(builder->CreateConstInBoundsGEP2_32(excinfo_pointer_casted, 0, 2));
ConcreteCompilerVariable* exc_type;
ConcreteCompilerVariable* exc_value;
ConcreteCompilerVariable* exc_tb;
if (this->incoming_exc_state.size()) {
if (incoming_exc_state.size() == 1) {
exc_type = this->incoming_exc_state[0].exc_type;
exc_value = this->incoming_exc_state[0].exc_value;
exc_tb = this->incoming_exc_state[0].exc_tb;
} else {
llvm::PHINode* phi_exc_type
= emitter.getBuilder()->CreatePHI(g.llvm_value_type_ptr, incoming_exc_state.size());
llvm::PHINode* phi_exc_value
= emitter.getBuilder()->CreatePHI(g.llvm_value_type_ptr, incoming_exc_state.size());
llvm::PHINode* phi_exc_tb
= emitter.getBuilder()->CreatePHI(g.llvm_value_type_ptr, incoming_exc_state.size());
for (auto e : this->incoming_exc_state) {
phi_exc_type->addIncoming(e.exc_type->getValue(), e.from_block);
phi_exc_value->addIncoming(e.exc_value->getValue(), e.from_block);
phi_exc_tb->addIncoming(e.exc_tb->getValue(), e.from_block);
}
exc_type = new ConcreteCompilerVariable(UNKNOWN, phi_exc_type, true);
exc_value = new ConcreteCompilerVariable(UNKNOWN, phi_exc_value, true);
exc_tb = new ConcreteCompilerVariable(UNKNOWN, phi_exc_tb, true);
}
} else {
llvm::Value* exc_type_ptr
= new llvm::AllocaInst(g.llvm_value_type_ptr, getConstantInt(1, g.i64), "exc_type",
irstate->getLLVMFunction()->getEntryBlock().getFirstInsertionPt());
llvm::Value* exc_value_ptr
= new llvm::AllocaInst(g.llvm_value_type_ptr, getConstantInt(1, g.i64), "exc_value",
irstate->getLLVMFunction()->getEntryBlock().getFirstInsertionPt());
llvm::Value* exc_traceback_ptr
= new llvm::AllocaInst(g.llvm_value_type_ptr, getConstantInt(1, g.i64), "exc_traceback",
irstate->getLLVMFunction()->getEntryBlock().getFirstInsertionPt());
emitter.getBuilder()->CreateCall3(g.funcs.PyErr_Fetch, exc_type_ptr, exc_value_ptr,
exc_traceback_ptr);
// TODO: I think we should be doing this on a python raise() or when we enter a python catch:
emitter.getBuilder()->CreateCall3(g.funcs.PyErr_NormalizeException, exc_type_ptr, exc_value_ptr,
exc_traceback_ptr);
exc_type = emitter.getBuilder()->CreateLoad(exc_type_ptr);
exc_value = emitter.getBuilder()->CreateLoad(exc_value_ptr);
exc_traceback = emitter.getBuilder()->CreateLoad(exc_traceback_ptr);
// There can be no incoming exception if the irgenerator was able to prove that
// an exception would not get thrown.
// For example, the cfg code will conservatively assume that any name-access can
// trigger an exception, but the irgenerator will know that definitely-defined
// local symbols will not throw.
exc_type = undefVariable();
exc_value = undefVariable();
exc_tb = undefVariable();
}
assert(exc_type->getType() == g.llvm_value_type_ptr);
assert(exc_value->getType() == g.llvm_value_type_ptr);
assert(exc_traceback->getType() == g.llvm_value_type_ptr);
return makeTuple({ new ConcreteCompilerVariable(UNKNOWN, exc_type, true),
new ConcreteCompilerVariable(UNKNOWN, exc_value, true),
new ConcreteCompilerVariable(UNKNOWN, exc_traceback, true) });
// clear this out to signal that we consumed them:
this->incoming_exc_state.clear();
return makeTuple({ exc_type, exc_value, exc_tb });
}
case AST_LangPrimitive::LOCALS: {
return new ConcreteCompilerVariable(UNKNOWN, irstate->getBoxedLocalsVar(), true);
......@@ -2355,7 +2297,7 @@ private:
// but ommitting the first argument is *not* the same as passing None.
ExceptionStyle target_exception_style = CXX;
if (unw_info.capi_exc_dest || (FORCE_LLVM_CAPI && node->arg0 && !node->arg2))
if (unw_info.preferredExceptionStyle() == CAPI && (node->arg0 && !node->arg2))
target_exception_style = CAPI;
if (node->arg0 == NULL) {
......@@ -2451,15 +2393,7 @@ private:
assert(!unw_info.hasHandler());
AST_Invoke* invoke = ast_cast<AST_Invoke>(node);
ExceptionStyle landingpad_style = irstate->getLandingpadStyle(invoke);
if (landingpad_style == CXX)
doStmt(invoke->stmt, UnwindInfo(node, NULL, entry_blocks[invoke->exc_dest]));
else {
// print_ast(invoke);
// printf(" (%d exceptions)\n", invoke->cxx_exception_count);
doStmt(invoke->stmt, UnwindInfo(node, entry_blocks[invoke->exc_dest], NULL));
}
doStmt(invoke->stmt, UnwindInfo(node, entry_blocks[invoke->exc_dest]));
assert(state == RUNNING || state == DEAD);
if (state == RUNNING) {
......@@ -2634,17 +2568,20 @@ public:
SymbolTable* st = new SymbolTable(symbol_table);
ConcreteSymbolTable* phi_st = new ConcreteSymbolTable();
// This should have been consumed:
assert(incoming_exc_state.empty());
if (myblock->successors.size() == 0) {
for (auto& p : *st) {
p.second->decvref(emitter);
}
st->clear();
symbol_table.clear();
return EndingState(st, phi_st, curblock);
return EndingState(st, phi_st, curblock, outgoing_exc_state);
} else if (myblock->successors.size() > 1) {
// Since there are no critical edges, all successors come directly from this node,
// so there won't be any required phis.
return EndingState(st, phi_st, curblock);
return EndingState(st, phi_st, curblock, outgoing_exc_state);
}
assert(myblock->successors.size() == 1); // other cases should have been handled
......@@ -2654,7 +2591,7 @@ public:
// If the next block has a single predecessor, don't have to
// emit any phis.
// Should probably not emit no-op jumps like this though.
return EndingState(st, phi_st, curblock);
return EndingState(st, phi_st, curblock, outgoing_exc_state);
}
// We have one successor, but they have more than one predecessor.
......@@ -2687,7 +2624,7 @@ public:
++it;
}
}
return EndingState(st, phi_st, curblock);
return EndingState(st, phi_st, curblock, outgoing_exc_state);
}
void giveLocalSymbol(InternedString name, CompilerVariable* var) override {
......@@ -2834,7 +2771,7 @@ public:
doSafePoint(block->body[i]);
#endif
doStmt(block->body[i], UnwindInfo(block->body[i], NULL, NULL));
doStmt(block->body[i], UnwindInfo(block->body[i], NULL));
}
if (VERBOSITY("irgenerator") >= 2) { // print ending symbol table
printf(" %d fini:", block->idx);
......@@ -2847,7 +2784,137 @@ public:
void doSafePoint(AST_stmt* next_statement) override {
// Unwind info is always needed in allowGLReadPreemption if it has any chance of
// running arbitrary code like finalizers.
emitter.createCall(UnwindInfo(next_statement, NULL, NULL), g.funcs.allowGLReadPreemption);
emitter.createCall(UnwindInfo(next_statement, NULL), g.funcs.allowGLReadPreemption);
}
// Create a (or reuse an existing) block that will catch a CAPI exception, and then forward
// it to the "final_dest" block. ie final_dest is a block corresponding to the IR level
// LANDINGPAD, and this function will createa helper block that fetches the exception.
// As a special-case, a NULL value for final_dest means that this helper block should
// instead propagate the exception out of the function.
llvm::BasicBlock* getCAPIExcDest(llvm::BasicBlock* final_dest, AST_stmt* current_stmt) {
llvm::BasicBlock*& capi_exc_dest = capi_exc_dests[final_dest];
if (capi_exc_dest) {
assert(capi_current_statements[final_dest] == current_stmt);
return capi_exc_dest;
}
llvm::BasicBlock* orig_block = curblock;
capi_exc_dest = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
capi_current_statements[final_dest] = current_stmt;
emitter.setCurrentBasicBlock(capi_exc_dest);
emitter.getBuilder()->CreateCall2(g.funcs.capiExcCaughtInJit,
embedRelocatablePtr(current_stmt, g.llvm_aststmt_type_ptr),
embedRelocatablePtr(irstate->getSourceInfo(), g.i8_ptr));
if (!final_dest) {
// Propagate the exception out of the function:
if (irstate->getExceptionStyle() == CXX) {
emitter.getBuilder()->CreateCall(g.funcs.reraiseJitCapiExc);
emitter.getBuilder()->CreateUnreachable();
} else {
emitter.getBuilder()->CreateRet(getNullPtr(g.llvm_value_type_ptr));
}
} else {
// Catch the exception and forward to final_dest:
llvm::Value* exc_type_ptr
= new llvm::AllocaInst(g.llvm_value_type_ptr, getConstantInt(1, g.i64), "exc_type",
irstate->getLLVMFunction()->getEntryBlock().getFirstInsertionPt());
llvm::Value* exc_value_ptr
= new llvm::AllocaInst(g.llvm_value_type_ptr, getConstantInt(1, g.i64), "exc_value",
irstate->getLLVMFunction()->getEntryBlock().getFirstInsertionPt());
llvm::Value* exc_traceback_ptr
= new llvm::AllocaInst(g.llvm_value_type_ptr, getConstantInt(1, g.i64), "exc_traceback",
irstate->getLLVMFunction()->getEntryBlock().getFirstInsertionPt());
emitter.getBuilder()->CreateCall3(g.funcs.PyErr_Fetch, exc_type_ptr, exc_value_ptr, exc_traceback_ptr);
// TODO: I think we should be doing this on a python raise() or when we enter a python catch:
emitter.getBuilder()->CreateCall3(g.funcs.PyErr_NormalizeException, exc_type_ptr, exc_value_ptr,
exc_traceback_ptr);
llvm::Value* exc_type = emitter.getBuilder()->CreateLoad(exc_type_ptr);
llvm::Value* exc_value = emitter.getBuilder()->CreateLoad(exc_value_ptr);
llvm::Value* exc_traceback = emitter.getBuilder()->CreateLoad(exc_traceback_ptr);
addOutgoingExceptionState(
IRGenerator::ExceptionState(capi_exc_dest, new ConcreteCompilerVariable(UNKNOWN, exc_type, true),
new ConcreteCompilerVariable(UNKNOWN, exc_value, true),
new ConcreteCompilerVariable(UNKNOWN, exc_traceback, true)));
emitter.getBuilder()->CreateBr(final_dest);
}
emitter.setCurrentBasicBlock(orig_block);
return capi_exc_dest;
}
llvm::BasicBlock* getCXXExcDest(llvm::BasicBlock* final_dest) {
llvm::BasicBlock*& cxx_exc_dest = cxx_exc_dests[final_dest];
if (cxx_exc_dest)
return cxx_exc_dest;
llvm::BasicBlock* orig_block = curblock;
cxx_exc_dest = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
emitter.getBuilder()->SetInsertPoint(cxx_exc_dest);
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 = 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));
if (final_dest) {
// Catch the exception and forward to final_dest:
addOutgoingExceptionState(ExceptionState(cxx_exc_dest,
new ConcreteCompilerVariable(UNKNOWN, exc_type, true),
new ConcreteCompilerVariable(UNKNOWN, exc_value, true),
new ConcreteCompilerVariable(UNKNOWN, exc_traceback, true)));
builder->CreateBr(final_dest);
} else {
// Propagate the exception out of the function.
// We shouldn't be hitting this case if the current function is CXX-style; then we should have
// just not created an Invoke and let the exception machinery propagate it for us.
assert(irstate->getExceptionStyle() == CAPI);
builder->CreateCall3(g.funcs.PyErr_Restore, exc_type, exc_value, exc_traceback);
builder->CreateRet(getNullPtr(g.llvm_value_type_ptr));
}
emitter.setCurrentBasicBlock(orig_block);
return cxx_exc_dest;
}
void addOutgoingExceptionState(ExceptionState exception_state) override {
this->outgoing_exc_state.push_back(exception_state);
}
void setIncomingExceptionState(llvm::SmallVector<ExceptionState, 2> exc_state) override {
assert(this->incoming_exc_state.empty());
this->incoming_exc_state = std::move(exc_state);
}
};
......
......@@ -72,8 +72,6 @@ private:
llvm::Value* frame_info_arg;
int scratch_size;
llvm::DenseMap<CFGBlock*, ExceptionStyle> landingpad_styles;
public:
IRGenState(CLFunction* clfunc, CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<PhiAnalysis> phis,
ParamNames* param_names, GCBuilder* gc, llvm::MDNode* func_dbg_info);
......@@ -82,6 +80,8 @@ public:
CompiledFunction* getCurFunction() { return cf; }
CLFunction* getCL() { return clfunc; }
ExceptionStyle getExceptionStyle() { return cf->exception_style; }
llvm::Function* getLLVMFunction() { return cf->func; }
EffortLevel getEffortLevel() { return cf->effort; }
......@@ -107,15 +107,19 @@ public:
ParamNames* getParamNames() { return param_names; }
void setFrameInfoArgument(llvm::Value* v) { frame_info_arg = v; }
ExceptionStyle getLandingpadStyle(AST_Invoke* invoke);
ExceptionStyle getLandingpadStyle(CFGBlock* block);
};
// turns CFGBlocks into LLVM IR
class IRGenerator {
private:
public:
struct ExceptionState {
llvm::BasicBlock* from_block;
ConcreteCompilerVariable* exc_type, *exc_value, *exc_tb;
ExceptionState(llvm::BasicBlock* from_block, ConcreteCompilerVariable* exc_type,
ConcreteCompilerVariable* exc_value, ConcreteCompilerVariable* exc_tb)
: from_block(from_block), exc_type(exc_type), exc_value(exc_value), exc_tb(exc_tb) {}
};
struct EndingState {
// symbol_table records which Python variables are bound to what CompilerVariables at the end of this block.
// phi_symbol_table records the ones that will need to be `phi'd.
......@@ -123,8 +127,14 @@ public:
SymbolTable* symbol_table;
ConcreteSymbolTable* phi_symbol_table;
llvm::BasicBlock* ending_block;
EndingState(SymbolTable* symbol_table, ConcreteSymbolTable* phi_symbol_table, llvm::BasicBlock* ending_block)
: symbol_table(symbol_table), phi_symbol_table(phi_symbol_table), ending_block(ending_block) {}
llvm::SmallVector<ExceptionState, 2> exception_state;
EndingState(SymbolTable* symbol_table, ConcreteSymbolTable* phi_symbol_table, llvm::BasicBlock* ending_block,
llvm::ArrayRef<ExceptionState> exception_state)
: symbol_table(symbol_table),
phi_symbol_table(phi_symbol_table),
ending_block(ending_block),
exception_state(exception_state.begin(), exception_state.end()) {}
};
virtual ~IRGenerator() {}
......@@ -139,6 +149,10 @@ public:
virtual void doSafePoint(AST_stmt* next_statement) = 0;
virtual void addFrameStackmapArgs(PatchpointInfo* pp, AST_stmt* current_stmt,
std::vector<llvm::Value*>& stackmap_args) = 0;
virtual void addOutgoingExceptionState(ExceptionState exception_state) = 0;
virtual void setIncomingExceptionState(llvm::SmallVector<ExceptionState, 2> exc_state) = 0;
virtual llvm::BasicBlock* getCXXExcDest(llvm::BasicBlock* final_dest) = 0;
virtual llvm::BasicBlock* getCAPIExcDest(llvm::BasicBlock* final_dest, AST_stmt* current_stmt) = 0;
};
class IREmitter;
......
......@@ -311,6 +311,7 @@ void initGlobalFuncs(GlobalState& g) {
GET(raise3_capi);
GET(PyErr_Fetch);
GET(PyErr_NormalizeException);
GET(PyErr_Restore);
GET(capiExcCaughtInJit);
GET(reraiseJitCapiExc);
GET(deopt);
......
......@@ -51,7 +51,7 @@ struct GlobalFuncs {
llvm::Value* __cxa_end_catch;
llvm::Value* raise0, *raise3, *raise3_capi;
llvm::Value* PyErr_Fetch, *PyErr_NormalizeException, *capiExcCaughtInJit, *reraiseJitCapiExc;
llvm::Value* PyErr_Fetch, *PyErr_NormalizeException, *PyErr_Restore, *capiExcCaughtInJit, *reraiseJitCapiExc;
llvm::Value* deopt;
llvm::Value* div_float_float, *floordiv_float_float, *mod_float_float, *pow_float_float;
......
......@@ -45,8 +45,10 @@ bool USE_REGALLOC_BASIC = true;
bool PAUSE_AT_ABORT = false;
bool ENABLE_TRACEBACKS = true;
// Forces the llvm jit to use capi exceptions whenever it can, as opposed to whenever it thinks
// it is faster:
bool FORCE_LLVM_CAPI = false;
// it is faster. The CALLS version is for calls that the llvm jit will make, and the THROWS version
// is for the exceptions it will throw.
bool FORCE_LLVM_CAPI_CALLS = false;
bool FORCE_LLVM_CAPI_THROWS = false;
int OSR_THRESHOLD_INTERPRETER = 25;
int REOPT_THRESHOLD_INTERPRETER = 25;
......
......@@ -39,7 +39,7 @@ extern int MAX_OBJECT_CACHE_ENTRIES;
extern bool SHOW_DISASM, FORCE_INTERPRETER, FORCE_OPTIMIZE, PROFILE, DUMPJIT, TRAP, USE_STRIPPED_STDLIB,
CONTINUE_AFTER_FATAL, ENABLE_INTERPRETER, ENABLE_BASELINEJIT, ENABLE_PYPA_PARSER, USE_REGALLOC_BASIC,
PAUSE_AT_ABORT, ENABLE_TRACEBACKS, ASSEMBLY_LOGGING, FORCE_LLVM_CAPI;
PAUSE_AT_ABORT, ENABLE_TRACEBACKS, ASSEMBLY_LOGGING, FORCE_LLVM_CAPI_CALLS, FORCE_LLVM_CAPI_THROWS;
extern bool ENABLE_ICS, ENABLE_ICGENERICS, ENABLE_ICGETITEMS, ENABLE_ICSETITEMS, ENABLE_ICDELITEMS, ENABLE_ICBINEXPS,
ENABLE_ICNONZEROS, ENABLE_ICCALLSITES, ENABLE_ICSETATTRS, ENABLE_ICGETATTRS, ENALBE_ICDELATTRS, ENABLE_ICGETGLOBALS,
......
......@@ -117,19 +117,33 @@ Box* generatorIter(Box* s) {
}
// called from both generatorHasNext and generatorSend/generatorNext (but only if generatorHasNext hasn't been called)
static void generatorSendInternal(BoxedGenerator* self, Box* v) {
template <ExceptionStyle S> static bool generatorSendInternal(BoxedGenerator* self, Box* v) noexcept(S == CAPI) {
STAT_TIMER(t0, "us_timer_generator_switching", 0);
if (!self->returnContext && v != None)
raiseExcHelper(TypeError, "can't send non-None value to a just-started generator");
if (!self->returnContext && v != None) {
if (S == CAPI) {
PyErr_SetString(TypeError, "can't send non-None value to a just-started generator");
return true;
} else
raiseExcHelper(TypeError, "can't send non-None value to a just-started generator");
}
if (self->running)
raiseExcHelper(ValueError, "generator already executing");
if (self->running) {
if (S == CAPI) {
PyErr_SetString(ValueError, "generator already executing");
return true;
} else
raiseExcHelper(ValueError, "generator already executing");
}
// check if the generator already exited
if (self->entryExited) {
freeGeneratorStack(self);
raiseExcHelper(StopIteration, (const char*)nullptr);
if (S == CAPI) {
PyErr_SetObject(StopIteration, None);
return true;
} else
raiseExcHelper(StopIteration, (const char*)nullptr);
}
self->returnValue = v;
......@@ -158,9 +172,14 @@ static void generatorSendInternal(BoxedGenerator* self, Box* v) {
if (self->exception.type) {
freeGeneratorStack(self);
// don't raise StopIteration exceptions because those are handled specially.
if (!self->exception.matches(StopIteration))
throw self->exception;
return;
if (!self->exception.matches(StopIteration)) {
if (S == CAPI) {
setCAPIException(self->exception);
return true;
} else
throw self->exception;
}
return false;
}
if (self->entryExited) {
......@@ -169,18 +188,21 @@ static void generatorSendInternal(BoxedGenerator* self, Box* v) {
// We could directly create the StopIteration exception but we delay creating it because often the caller is not
// interested in the exception (=generatorHasnext). If we really need it we will create it inside generatorSend.
self->exception = ExcInfo(NULL, NULL, NULL);
return;
return false;
}
return false;
}
Box* generatorSend(Box* s, Box* v) {
template <ExceptionStyle S> static Box* generatorSend(Box* s, Box* v) noexcept(S == CAPI) {
assert(s->cls == generator_cls);
BoxedGenerator* self = static_cast<BoxedGenerator*>(s);
if (self->iterated_from__hasnext__)
Py_FatalError(".throw called on generator last advanced with __hasnext__");
generatorSendInternal(self, v);
bool exc = generatorSendInternal<S>(self, v);
if (S == CAPI && exc)
return NULL;
// throw StopIteration if the generator exited
if (self->entryExited) {
......@@ -195,9 +217,19 @@ Box* generatorSend(Box* s, Box* v) {
ExcInfo old_exc = self->exception;
// Clear the exception for GC purposes:
self->exception = ExcInfo(nullptr, nullptr, nullptr);
if (old_exc.type == NULL)
raiseExcHelper(StopIteration, (const char*)nullptr);
throw old_exc;
if (old_exc.type == NULL) {
if (S == CAPI) {
PyErr_SetObject(StopIteration, None);
return NULL;
} else
raiseExcHelper(StopIteration, (const char*)nullptr);
} else {
if (S == CAPI) {
setCAPIException(old_exc);
return NULL;
} else
throw old_exc;
}
}
return self->returnValue;
......@@ -223,7 +255,7 @@ Box* generatorThrow(Box* s, BoxedClass* exc_cls, Box* exc_val = nullptr, Box** a
throw exc_info;
self->exception = exc_info;
return generatorSend(self, None);
return generatorSend<CXX>(self, None);
}
Box* generatorClose(Box* s) {
......@@ -245,7 +277,7 @@ Box* generatorClose(Box* s) {
assert(0); // unreachable
}
Box* generatorNext(Box* s) {
template <ExceptionStyle S> static Box* generatorNext(Box* s) noexcept(S == CAPI) {
assert(s->cls == generator_cls);
BoxedGenerator* self = static_cast<BoxedGenerator*>(s);
......@@ -254,7 +286,7 @@ Box* generatorNext(Box* s) {
return self->returnValue;
}
return generatorSend(s, None);
return generatorSend<S>(s, None);
}
i1 generatorHasnextUnboxed(Box* s) {
......@@ -262,7 +294,7 @@ i1 generatorHasnextUnboxed(Box* s) {
BoxedGenerator* self = static_cast<BoxedGenerator*>(s);
if (!self->iterated_from__hasnext__) {
generatorSendInternal(self, None);
generatorSendInternal<CXX>(self, None);
self->iterated_from__hasnext__ = true;
}
......@@ -455,13 +487,16 @@ void setupGenerator() {
new BoxedFunction(boxRTFunction((void*)generatorIter, typeFromClass(generator_cls), 1)));
generator_cls->giveAttr("close", new BoxedFunction(boxRTFunction((void*)generatorClose, UNKNOWN, 1)));
generator_cls->giveAttr("next", new BoxedFunction(boxRTFunction((void*)generatorNext, UNKNOWN, 1)));
auto generator_next = boxRTFunction((void*)generatorNext<CXX>, UNKNOWN, 1, ParamNames::empty(), CXX);
addRTFunction(generator_next, (void*)generatorNext<CAPI>, UNKNOWN, CAPI);
generator_cls->giveAttr("next", new BoxedFunction(generator_next));
CLFunction* hasnext = boxRTFunction((void*)generatorHasnextUnboxed, BOOL, 1);
addRTFunction(hasnext, (void*)generatorHasnext, BOXED_BOOL);
generator_cls->giveAttr("__hasnext__", new BoxedFunction(hasnext));
generator_cls->giveAttr("send", new BoxedFunction(boxRTFunction((void*)generatorSend, UNKNOWN, 2)));
generator_cls->giveAttr("send", new BoxedFunction(boxRTFunction((void*)generatorSend<CXX>, UNKNOWN, 2)));
auto gthrow = new BoxedFunction(boxRTFunction((void*)generatorThrow, UNKNOWN, 4, 2, false, false), { NULL, NULL });
generator_cls->giveAttr("throw", gthrow);
......
......@@ -129,6 +129,7 @@ void force() {
FORCE(raise3_capi);
FORCE(PyErr_Fetch);
FORCE(PyErr_NormalizeException);
FORCE(PyErr_Restore);
FORCE(capiExcCaughtInJit);
FORCE(reraiseJitCapiExc);
FORCE(deopt);
......
......@@ -262,6 +262,10 @@ extern "C" void dumpEx(void* p, int levels) {
printf("Has %ld function versions\n", cl->versions.size());
for (CompiledFunction* cf : cl->versions) {
bool got_name;
if (cf->exception_style == CXX)
printf("CXX style: ");
else
printf("CAPI style: ");
std::string name = g.func_addr_registry.getFuncNameAtAddress(cf->code, true, &got_name);
if (got_name)
printf("%s\n", name.c_str());
......
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