Commit dbe78661 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #651 from undingen/inc_jit_rewriter2

Add a new JIT tier which is tightly coupled to the AST Interpreter
parents 1ef88c12 5358470c
......@@ -34,6 +34,7 @@ add_library(PYSTON_OBJECTS OBJECT ${OPTIONAL_SRCS}
capi/object.cpp
capi/typeobject.cpp
codegen/ast_interpreter.cpp
codegen/baseline_jit.cpp
codegen/codegen.cpp
codegen/compvars.cpp
codegen/entry.cpp
......
......@@ -109,7 +109,6 @@ void ICSlotRewrite::commit(CommitHook* hook) {
if (!do_commit)
return;
assert(assembler->isExactlyFull());
assert(!assembler->hasFailed());
for (int i = 0; i < dependencies.size(); i++) {
......
......@@ -62,9 +62,8 @@ private:
ICSlotInfo* ic_entry;
ICSlotRewrite(ICInfo* ic, const char* debug_name);
public:
ICSlotRewrite(ICInfo* ic, const char* debug_name);
~ICSlotRewrite();
assembler::Assembler* getAssembler() { return assembler; }
......
......@@ -958,7 +958,7 @@ void Rewriter::_call(RewriterVar* result, bool has_side_effects, void* func_addr
assembler->callq(r);
} else {
assembler->call(assembler::Immediate(offset));
assert(asm_address == (uint64_t)assembler->curInstPointer());
assert(assembler->hasFailed() || asm_address == (uint64_t)assembler->curInstPointer());
}
assert(vars_by_location.count(assembler::RAX) == 0);
......
......@@ -20,6 +20,7 @@
#include "analysis/function_analysis.h"
#include "analysis/scoping_analysis.h"
#include "codegen/baseline_jit.h"
#include "codegen/codegen.h"
#include "codegen/compvars.h"
#include "codegen/irgen.h"
......@@ -74,21 +75,6 @@ public:
static void deregister(void* frame_addr);
};
union Value {
bool b;
int64_t n;
double d;
Box* o;
Value(bool b) : b(b) {}
Value(int64_t n = 0) : n(n) {}
Value(double d) : d(d) {}
Value(Box* o) : o(o) {
if (DEBUG >= 2)
ASSERT(gc::isValidGCObject(o), "%p", o);
}
};
class ASTInterpreter : public Box {
public:
typedef ContiguousMap<InternedString, Box*> SymMap;
......@@ -108,9 +94,11 @@ public:
private:
Box* createFunction(AST* node, AST_arguments* args, const std::vector<AST_stmt*>& body);
Value doBinOp(Box* left, Box* right, int op, BinExpType exp_type);
Value doBinOp(Value left, Value right, int op, BinExpType exp_type);
void doStore(AST_expr* node, Value value);
void doStore(InternedString name, Value value);
Box* doOSR(AST_Jump* node);
Value getNone();
Value visit_assert(AST_Assert* node);
Value visit_assign(AST_Assign* node);
......@@ -156,14 +144,21 @@ private:
Value visit_jump(AST_Jump* node);
Value visit_langPrimitive(AST_LangPrimitive* node);
void startJITing(CFGBlock* block, int jump_offset = 0);
void abortJITing();
void finishJITing(CFGBlock* continue_block = NULL);
// this variables are used by the baseline JIT, make sure they have an offset < 0x80 so we can use shorter
// instructions
CFGBlock* next_block, *current_block;
AST_stmt* current_inst;
CompiledFunction* compiled_func;
SourceInfo* source_info;
ScopeInfo* scope_info;
PhiAnalysis* phis;
SymMap sym_table;
CFGBlock* next_block, *current_block;
AST_stmt* current_inst;
ExcInfo last_exception;
BoxedClosure* passed_closure, *created_closure;
BoxedGenerator* generator;
......@@ -173,6 +168,7 @@ private:
// This is either a module or a dict
Box* globals;
void* frame_addr; // used to clear entry inside the s_interpreterMap on destruction
std::unique_ptr<JitFragmentWriter> jit;
public:
DEFAULT_CLASS_SIMPLE(astinterpreter_cls);
......@@ -211,6 +207,7 @@ public:
}
friend class RegisterHelper;
friend struct pyston::ASTInterpreterJitInterface;
};
void ASTInterpreter::addSymbol(InternedString name, Box* value, bool allow_duplicates) {
......@@ -265,12 +262,12 @@ void ASTInterpreter::gcHandler(GCVisitor* visitor, Box* box) {
}
ASTInterpreter::ASTInterpreter(CompiledFunction* compiled_function)
: compiled_func(compiled_function),
: current_block(0),
current_inst(0),
compiled_func(compiled_function),
source_info(compiled_function->clfunc->source.get()),
scope_info(0),
phis(NULL),
current_block(0),
current_inst(0),
last_exception(NULL, NULL, NULL),
passed_closure(0),
created_closure(0),
......@@ -305,15 +302,15 @@ void ASTInterpreter::initArguments(int nargs, BoxedClosure* _closure, BoxedGener
int i = 0;
for (auto& name : param_names.args) {
doStore(source_info->getInternedStrings().get(name), argsArray[i++]);
doStore(source_info->getInternedStrings().get(name), Value(argsArray[i++], 0));
}
if (!param_names.vararg.str().empty()) {
doStore(source_info->getInternedStrings().get(param_names.vararg), argsArray[i++]);
doStore(source_info->getInternedStrings().get(param_names.vararg), Value(argsArray[i++], 0));
}
if (!param_names.kwarg.str().empty()) {
doStore(source_info->getInternedStrings().get(param_names.kwarg), argsArray[i++]);
doStore(source_info->getInternedStrings().get(param_names.kwarg), Value(argsArray[i++], 0));
}
}
......@@ -342,17 +339,59 @@ void RegisterHelper::deregister(void* frame_addr) {
s_interpreterMap.erase(frame_addr);
}
void ASTInterpreter::startJITing(CFGBlock* block, int jump_offset) {
assert(ENABLE_BASELINEJIT);
assert(!jit);
auto& code_blocks = compiled_func->code_blocks;
JitCodeBlock* code_block = NULL;
if (!code_blocks.empty())
code_block = code_blocks[code_blocks.size() - 1].get();
if (!code_block || code_block->shouldCreateNewBlock()) {
code_blocks.push_back(std::unique_ptr<JitCodeBlock>(new JitCodeBlock(source_info->getName())));
code_block = code_blocks[code_blocks.size() - 1].get();
jump_offset = 0;
}
jit = code_block->newFragment(block, jump_offset);
}
void ASTInterpreter::abortJITing() {
if (jit) {
jit->abortCompilation();
jit.reset();
}
}
void ASTInterpreter::finishJITing(CFGBlock* continue_block) {
if (!jit)
return;
int jump_offset = jit->finishCompilation();
jit.reset();
if (continue_block && !continue_block->code)
startJITing(continue_block, jump_offset);
}
Value ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_block, AST_stmt* start_at,
RegisterHelper* reg) {
void* frame_addr = __builtin_frame_address(0);
reg->doRegister(frame_addr, &interpreter);
Value v;
bool should_jit = false;
bool from_start = start_block == NULL && start_at == NULL;
assert((start_block == NULL) == (start_at == NULL));
if (start_block == NULL) {
start_block = interpreter.source_info->cfg->getStartingBlock();
start_at = start_block->body[0];
if (ENABLE_BASELINEJIT && interpreter.compiled_func->times_called >= REOPT_THRESHOLD_INTERPRETER
&& !start_block->code)
should_jit = true;
}
// Important that this happens after RegisterHelper:
......@@ -360,6 +399,7 @@ Value ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_
threading::allowGLReadPreemption();
interpreter.current_inst = NULL;
if (!from_start) {
interpreter.current_block = start_block;
bool started = false;
for (auto s : start_block->body) {
......@@ -372,13 +412,53 @@ Value ASTInterpreter::executeInner(ASTInterpreter& interpreter, CFGBlock* start_
interpreter.current_inst = s;
v = interpreter.visit_stmt(s);
}
} else {
if (should_jit)
interpreter.startJITing(start_block);
interpreter.next_block = start_block;
}
while (interpreter.next_block) {
interpreter.current_block = interpreter.next_block;
interpreter.next_block = 0;
if (ENABLE_BASELINEJIT && !interpreter.jit) {
CFGBlock* b = interpreter.current_block;
if (b->entry_code) {
should_jit = true;
try {
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_baseline_jitted_code");
std::pair<CFGBlock*, Box*> rtn = b->entry_code(&interpreter, b);
interpreter.next_block = rtn.first;
if (!interpreter.next_block)
return Value(rtn.second, 0);
} catch (ExcInfo e) {
AST_stmt* stmt = interpreter.getCurrentStatement();
if (stmt->type != AST_TYPE::Invoke)
throw e;
auto source = interpreter.getCF()->clfunc->source.get();
exceptionCaughtInInterpreter(
LineInfo(stmt->lineno, stmt->col_offset, source->fn, source->getName()), &e);
interpreter.next_block = ((AST_Invoke*)stmt)->exc_dest;
interpreter.last_exception = e;
}
continue;
}
}
if (ENABLE_BASELINEJIT && should_jit && !interpreter.jit) {
assert(!interpreter.current_block->code);
interpreter.startJITing(interpreter.current_block);
}
for (AST_stmt* s : interpreter.current_block->body) {
interpreter.current_inst = s;
if (interpreter.jit)
interpreter.jit->emitSetCurrentInst(s);
v = interpreter.visit_stmt(s);
}
}
......@@ -389,18 +469,17 @@ Value ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block
UNAVOIDABLE_STAT_TIMER(t0, "us_timer_in_interpreter");
RegisterHelper frame_registerer;
return executeInner(interpreter, start_block, start_at, &frame_registerer);
}
Value ASTInterpreter::doBinOp(Box* left, Box* right, int op, BinExpType exp_type) {
Value ASTInterpreter::doBinOp(Value left, Value right, int op, BinExpType exp_type) {
switch (exp_type) {
case BinExpType::AugBinOp:
return augbinop(left, right, op);
return Value(augbinop(left.o, right.o, op), jit ? jit->emitAugbinop(left, right, op) : NULL);
case BinExpType::BinOp:
return binop(left, right, op);
return Value(binop(left.o, right.o, op), jit ? jit->emitBinop(left, right, op) : NULL);
case BinExpType::Compare:
return compare(left, right, op);
return Value(compare(left.o, right.o, op), jit ? jit->emitCompare(left, right, op) : NULL);
default:
RELEASE_ASSERT(0, "not implemented");
}
......@@ -410,14 +489,30 @@ Value ASTInterpreter::doBinOp(Box* left, Box* right, int op, BinExpType exp_type
void ASTInterpreter::doStore(InternedString name, Value value) {
ScopeInfo::VarScopeType vst = scope_info->getScopeTypeOfName(name);
if (vst == ScopeInfo::VarScopeType::GLOBAL) {
if (jit)
jit->emitSetGlobal(globals, name.getBox(), value);
setGlobal(globals, name.getBox(), value.o);
} else if (vst == ScopeInfo::VarScopeType::NAME) {
if (jit)
jit->emitSetItemName(name.getBox(), value);
assert(frame_info.boxedLocals != NULL);
// TODO should probably pre-box the names when it's a scope that usesNameLookup
setitem(frame_info.boxedLocals, name.getBox(), value.o);
} else {
bool closure = vst == ScopeInfo::VarScopeType::CLOSURE;
if (jit) {
if (!closure) {
bool is_live = source_info->getLiveness()->isLiveAtEnd(name, current_block);
if (is_live)
jit->emitSetLocal(name, closure, value);
else
jit->emitSetBlockLocal(name, value);
} else
jit->emitSetLocal(name, closure, value);
}
sym_table[name] = value.o;
if (vst == ScopeInfo::VarScopeType::CLOSURE) {
if (closure) {
created_closure->elts[scope_info->getClosureOffset(name)] = value.o;
}
}
......@@ -429,87 +524,157 @@ void ASTInterpreter::doStore(AST_expr* node, Value value) {
doStore(name->id, value);
} else if (node->type == AST_TYPE::Attribute) {
AST_Attribute* attr = (AST_Attribute*)node;
pyston::setattr(visit_expr(attr->value).o, attr->attr.getBox(), value.o);
Value o = visit_expr(attr->value);
if (jit)
jit->emitSetAttr(o, attr->attr.getBox(), value);
pyston::setattr(o.o, attr->attr.getBox(), value.o);
} else if (node->type == AST_TYPE::Tuple) {
AST_Tuple* tuple = (AST_Tuple*)node;
Box** array = unpackIntoArray(value.o, tuple->elts.size());
RewriterVar* array_var = NULL;
if (jit)
array_var = jit->emitUnpackIntoArray(value, tuple->elts.size());
unsigned i = 0;
for (AST_expr* e : tuple->elts)
doStore(e, array[i++]);
for (AST_expr* e : tuple->elts) {
doStore(e, Value(array[i], jit ? array_var->getAttr(i * sizeof(void*)) : NULL));
++i;
}
} else if (node->type == AST_TYPE::List) {
AST_List* list = (AST_List*)node;
Box** array = unpackIntoArray(value.o, list->elts.size());
RewriterVar* array_var = NULL;
if (jit)
array_var = jit->emitUnpackIntoArray(value, list->elts.size());
unsigned i = 0;
for (AST_expr* e : list->elts)
doStore(e, array[i++]);
for (AST_expr* e : list->elts) {
doStore(e, Value(array[i], jit ? array_var->getAttr(i * sizeof(void*)) : NULL));
++i;
}
} else if (node->type == AST_TYPE::Subscript) {
AST_Subscript* subscript = (AST_Subscript*)node;
Value target = visit_expr(subscript->value);
Value slice = visit_expr(subscript->slice);
if (jit)
jit->emitSetItem(target, slice, value);
setitem(target.o, slice.o, value.o);
} else {
RELEASE_ASSERT(0, "not implemented");
}
}
Value ASTInterpreter::getNone() {
return Value(None, jit ? jit->imm(None) : NULL);
}
Value ASTInterpreter::visit_unaryop(AST_UnaryOp* node) {
Value operand = visit_expr(node->operand);
if (node->op_type == AST_TYPE::Not)
return boxBool(!nonzero(operand.o));
return Value(boxBool(!nonzero(operand.o)), jit ? jit->emitNotNonzero(operand) : NULL);
else
return unaryop(operand.o, node->op_type);
return Value(unaryop(operand.o, node->op_type), jit ? jit->emitUnaryop(operand, node->op_type) : NULL);
}
Value ASTInterpreter::visit_binop(AST_BinOp* node) {
Value left = visit_expr(node->left);
Value right = visit_expr(node->right);
return doBinOp(left.o, right.o, node->op_type, BinExpType::BinOp);
return doBinOp(left, right, node->op_type, BinExpType::BinOp);
}
Value ASTInterpreter::visit_slice(AST_Slice* node) {
Value lower = node->lower ? visit_expr(node->lower) : None;
Value upper = node->upper ? visit_expr(node->upper) : None;
Value step = node->step ? visit_expr(node->step) : None;
return createSlice(lower.o, upper.o, step.o);
Value lower = node->lower ? visit_expr(node->lower) : getNone();
Value upper = node->upper ? visit_expr(node->upper) : getNone();
Value step = node->step ? visit_expr(node->step) : getNone();
Value v;
if (jit)
v.var = jit->emitCreateSlice(lower, upper, step);
v.o = createSlice(lower.o, upper.o, step.o);
return v;
}
Value ASTInterpreter::visit_extslice(AST_ExtSlice* node) {
llvm::SmallVector<RewriterVar*, 8> items;
int num_slices = node->dims.size();
BoxedTuple* rtn = BoxedTuple::create(num_slices);
for (int i = 0; i < num_slices; ++i)
rtn->elts[i] = visit_expr(node->dims[i]).o;
return rtn;
for (int i = 0; i < num_slices; ++i) {
Value v = visit_expr(node->dims[i]);
rtn->elts[i] = v.o;
items.push_back(v);
}
return Value(rtn, jit ? jit->emitCreateTuple(items) : NULL);
}
Value ASTInterpreter::visit_branch(AST_Branch* node) {
Value v = visit_expr(node->test);
ASSERT(v.o == True || v.o == False, "Should have called NONZERO before this branch");
if (jit)
jit->emitSideExit(v, v.o, v.o == True ? node->iffalse : node->iftrue);
if (v.o == True)
next_block = node->iftrue;
else
next_block = node->iffalse;
if (jit) {
jit->emitJump(next_block);
finishJITing(next_block);
}
return Value();
}
Value ASTInterpreter::visit_jump(AST_Jump* node) {
bool backedge = node->target->idx < current_block->idx && compiled_func;
if (backedge)
if (backedge) {
threading::allowGLReadPreemption();
if (ENABLE_OSR && backedge && edgecount++ == OSR_THRESHOLD_INTERPRETER) {
bool can_osr = !FORCE_INTERPRETER && source_info->scoping->areGlobalsFromModule();
if (can_osr) {
if (jit)
jit->call(false, (void*)threading::allowGLReadPreemption);
}
if (jit) {
if (backedge)
jit->emitOSRPoint(node);
jit->emitJump(node->target);
finishJITing(node->target);
}
if (backedge)
++edgecount;
if (ENABLE_BASELINEJIT && backedge && edgecount == OSR_THRESHOLD_INTERPRETER && !jit && !node->target->code)
startJITing(node->target);
if (backedge && edgecount == OSR_THRESHOLD_BASELINE) {
Box* rtn = doOSR(node);
if (rtn)
return Value(rtn, NULL);
}
next_block = node->target;
return Value();
}
Box* ASTInterpreter::doOSR(AST_Jump* node) {
bool can_osr = ENABLE_OSR && !FORCE_INTERPRETER && source_info->scoping->areGlobalsFromModule();
if (!can_osr)
return NULL;
static StatCounter ast_osrs("num_ast_osrs");
ast_osrs.log();
// TODO: we will immediately want the liveness info again in the jit, we should pass
// it through.
std::unique_ptr<LivenessAnalysis> liveness = computeLivenessInfo(source_info->cfg);
LivenessAnalysis* liveness = source_info->getLiveness();
std::unique_ptr<PhiAnalysis> phis
= computeRequiredPhis(compiled_func->clfunc->param_names, source_info->cfg, liveness.get(), scope_info);
= computeRequiredPhis(compiled_func->clfunc->param_names, source_info->cfg, liveness, scope_info);
std::vector<InternedString> dead_symbols;
for (auto& it : sym_table) {
......@@ -554,7 +719,6 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
}
// Manually free these here, since we might not return from this scope for a long time.
liveness.reset(nullptr);
phis.reset(nullptr);
// LLVM has a limit on the number of operands a machine instruction can have (~255),
......@@ -562,8 +726,7 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
if (sorted_symbol_table.size() > 225) {
static StatCounter times_osr_cancel("num_osr_cancel_too_many_syms");
times_osr_cancel.log();
next_block = node->target;
return Value();
return nullptr;
}
if (generator)
......@@ -616,12 +779,8 @@ Value ASTInterpreter::visit_jump(AST_Jump* node) {
// creating the Value.
if (compiled_func->getReturnType() != VOID)
assert(r);
return (intptr_t)r;
}
}
next_block = node->target;
return Value();
return r ? r : None;
}
Value ASTInterpreter::visit_invoke(AST_Invoke* node) {
......@@ -629,7 +788,13 @@ Value ASTInterpreter::visit_invoke(AST_Invoke* node) {
try {
v = visit_stmt(node->stmt);
next_block = node->normal_dest;
if (jit) {
jit->emitJump(next_block);
finishJITing(next_block);
}
} catch (ExcInfo e) {
abortJITing();
auto source = getCF()->clfunc->source.get();
exceptionCaughtInInterpreter(LineInfo(node->lineno, node->col_offset, source->fn, source->getName()), &e);
......@@ -642,7 +807,9 @@ Value ASTInterpreter::visit_invoke(AST_Invoke* node) {
}
Value ASTInterpreter::visit_clsAttribute(AST_ClsAttribute* node) {
return getclsattr(visit_expr(node->value).o, node->attr.getBox());
Value obj = visit_expr(node->value);
BoxedString* attr = node->attr.getBox();
return Value(getclsattr(obj.o, attr), jit ? jit->emitGetClsAttr(obj, attr) : NULL);
}
Value ASTInterpreter::visit_augBinOp(AST_AugBinOp* node) {
......@@ -650,15 +817,17 @@ Value ASTInterpreter::visit_augBinOp(AST_AugBinOp* node) {
Value left = visit_expr(node->left);
Value right = visit_expr(node->right);
return doBinOp(left.o, right.o, node->op_type, BinExpType::AugBinOp);
return doBinOp(left, right, node->op_type, BinExpType::AugBinOp);
}
Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
Value v;
if (node->opcode == AST_LangPrimitive::GET_ITER) {
assert(node->args.size() == 1);
v = getPystonIter(visit_expr(node->args[0]).o);
Value val = visit_expr(node->args[0]);
v = Value(getPystonIter(val.o), jit ? jit->emitGetPystonIter(val) : NULL);
} else if (node->opcode == AST_LangPrimitive::IMPORT_FROM) {
abortJITing();
assert(node->args.size() == 2);
assert(node->args[0]->type == AST_TYPE::Name);
assert(node->args[1]->type == AST_TYPE::Str);
......@@ -669,8 +838,9 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
const std::string& name = ast_str->str_data;
assert(name.size());
// TODO: shouldn't have to rebox here
v = importFrom(module.o, boxString(name));
v.o = importFrom(module.o, boxString(name));
} else if (node->opcode == AST_LangPrimitive::IMPORT_NAME) {
abortJITing();
assert(node->args.size() == 3);
assert(node->args[0]->type == AST_TYPE::Num);
assert(static_cast<AST_Num*>(node->args[0])->num_type == AST_Num::INT);
......@@ -681,8 +851,9 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
auto ast_str = ast_cast<AST_Str>(node->args[2]);
assert(ast_str->str_type == AST_Str::STR);
const std::string& module_name = ast_str->str_data;
v = import(level, froms.o, module_name);
v.o = import(level, froms.o, module_name);
} else if (node->opcode == AST_LangPrimitive::IMPORT_STAR) {
abortJITing();
assert(node->args.size() == 1);
assert(node->args[0]->type == AST_TYPE::Name);
......@@ -691,30 +862,28 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
Value module = visit_expr(node->args[0]);
v = importStar(module.o, globals);
v.o = importStar(module.o, globals);
} else if (node->opcode == AST_LangPrimitive::NONE) {
v = None;
v = getNone();
} else if (node->opcode == AST_LangPrimitive::LANDINGPAD) {
assert(last_exception.type);
Box* type = last_exception.type;
Box* value = last_exception.value ? last_exception.value : None;
Box* traceback = last_exception.traceback ? last_exception.traceback : None;
v = BoxedTuple::create({ type, value, traceback });
v = Value(BoxedTuple::create({ type, value, traceback }), jit ? jit->emitLandingpad() : NULL);
last_exception = ExcInfo(NULL, NULL, NULL);
} else if (node->opcode == AST_LangPrimitive::CHECK_EXC_MATCH) {
assert(node->args.size() == 2);
Value obj = visit_expr(node->args[0]);
Value cls = visit_expr(node->args[1]);
v = boxBool(exceptionMatches(obj.o, cls.o));
v = Value(boxBool(exceptionMatches(obj.o, cls.o)), jit ? jit->emitExceptionMatches(obj, cls) : NULL);
} else if (node->opcode == AST_LangPrimitive::LOCALS) {
assert(frame_info.boxedLocals != NULL);
v = frame_info.boxedLocals;
v = Value(frame_info.boxedLocals, jit ? jit->emitGetBoxedLocals() : NULL);
} else if (node->opcode == AST_LangPrimitive::NONZERO) {
assert(node->args.size() == 1);
Value obj = visit_expr(node->args[0]);
v = boxBool(nonzero(obj.o));
v = Value(boxBool(nonzero(obj.o)), jit ? jit->emitNonzero(obj) : NULL);
} else if (node->opcode == AST_LangPrimitive::SET_EXC_INFO) {
assert(node->args.size() == 3);
......@@ -725,25 +894,30 @@ Value ASTInterpreter::visit_langPrimitive(AST_LangPrimitive* node) {
Value traceback = visit_expr(node->args[2]);
assert(traceback.o);
if (jit)
jit->emitSetExcInfo(type, value, traceback);
getFrameInfo()->exc = ExcInfo(type.o, value.o, traceback.o);
v = None;
v = getNone();
} else if (node->opcode == AST_LangPrimitive::UNCACHE_EXC_INFO) {
assert(node->args.empty());
if (jit)
jit->emitUncacheExcInfo();
getFrameInfo()->exc = ExcInfo(NULL, NULL, NULL);
v = None;
v = getNone();
} else if (node->opcode == AST_LangPrimitive::HASNEXT) {
assert(node->args.size() == 1);
Value obj = visit_expr(node->args[0]);
v = boxBool(hasnext(obj.o));
v = Value(boxBool(hasnext(obj.o)), jit ? jit->emitHasnext(obj) : NULL);
} else
RELEASE_ASSERT(0, "unknown opcode %d", node->opcode);
return v;
}
Value ASTInterpreter::visit_yield(AST_Yield* node) {
Value value = node->value ? visit_expr(node->value) : None;
Value value = node->value ? visit_expr(node->value) : getNone();
assert(generator && generator->cls == generator_cls);
return yield(generator, value.o);
return Value(yield(generator, value.o), jit ? jit->emitYield(value) : NULL);
}
Value ASTInterpreter::visit_stmt(AST_stmt* node) {
......@@ -797,12 +971,19 @@ Value ASTInterpreter::visit_stmt(AST_stmt* node) {
}
Value ASTInterpreter::visit_return(AST_Return* node) {
Value s(node->value ? visit_expr(node->value) : None);
Value s = node->value ? visit_expr(node->value) : getNone();
if (jit) {
jit->emitReturn(s);
finishJITing();
}
next_block = 0;
return s;
}
Box* ASTInterpreter::createFunction(AST* node, AST_arguments* args, const std::vector<AST_stmt*>& body) {
abortJITing();
CLFunction* cl = wrapFunction(node, args, body, source_info);
std::vector<Box*, StlCompatAllocator<Box*>> defaults;
......@@ -852,6 +1033,7 @@ Box* ASTInterpreter::createFunction(AST* node, AST_arguments* args, const std::v
}
Value ASTInterpreter::visit_makeFunction(AST_MakeFunction* mkfn) {
abortJITing();
AST_FunctionDef* node = mkfn->function_def;
AST_arguments* args = node->args;
......@@ -864,10 +1046,11 @@ Value ASTInterpreter::visit_makeFunction(AST_MakeFunction* mkfn) {
for (int i = decorators.size() - 1; i >= 0; i--)
func = runtimeCall(decorators[i], ArgPassSpec(1), func, 0, 0, 0, 0);
return Value(func);
return Value(func, NULL);
}
Value ASTInterpreter::visit_makeClass(AST_MakeClass* mkclass) {
abortJITing();
AST_ClassDef* node = mkclass->class_def;
ScopeInfo* scope_info = source_info->scoping->getScopeInfoForNode(node);
assert(scope_info);
......@@ -902,22 +1085,37 @@ Value ASTInterpreter::visit_makeClass(AST_MakeClass* mkclass) {
for (int i = decorators.size() - 1; i >= 0; i--)
classobj = runtimeCall(decorators[i], ArgPassSpec(1), classobj, 0, 0, 0, 0);
return Value(classobj);
return Value(classobj, NULL);
}
Value ASTInterpreter::visit_raise(AST_Raise* node) {
if (node->arg0 == NULL) {
assert(!node->arg1);
assert(!node->arg2);
if (jit) {
jit->emitRaise0();
finishJITing();
}
raise0();
}
raise3(node->arg0 ? visit_expr(node->arg0).o : None, node->arg1 ? visit_expr(node->arg1).o : None,
node->arg2 ? visit_expr(node->arg2).o : None);
Value arg0 = node->arg0 ? visit_expr(node->arg0) : getNone();
Value arg1 = node->arg1 ? visit_expr(node->arg1) : getNone();
Value arg2 = node->arg2 ? visit_expr(node->arg2) : getNone();
if (jit) {
jit->emitRaise3(arg0, arg1, arg2);
finishJITing();
}
raise3(arg0.o, arg1.o, arg2.o);
return Value();
}
Value ASTInterpreter::visit_assert(AST_Assert* node) {
abortJITing();
#ifndef NDEBUG
// Currently we only generate "assert 0" statements
Value v = visit_expr(node->test);
......@@ -932,12 +1130,14 @@ Value ASTInterpreter::visit_assert(AST_Assert* node) {
}
Value ASTInterpreter::visit_global(AST_Global* node) {
abortJITing();
for (auto name : node->names)
sym_table.erase(name);
return Value();
}
Value ASTInterpreter::visit_delete(AST_Delete* node) {
abortJITing();
for (AST_expr* target_ : node->targets) {
switch (target_->type) {
case AST_TYPE::Subscript: {
......@@ -1003,49 +1203,39 @@ Value ASTInterpreter::visit_assign(AST_Assign* node) {
}
Value ASTInterpreter::visit_print(AST_Print* node) {
static BoxedString* write_str = static_cast<BoxedString*>(PyString_InternFromString("write"));
static BoxedString* newline_str = static_cast<BoxedString*>(PyString_InternFromString("\n"));
static BoxedString* space_str = static_cast<BoxedString*>(PyString_InternFromString(" "));
Box* dest = node->dest ? visit_expr(node->dest).o : getSysStdout();
int nvals = node->values.size();
assert(nvals <= 1 && "cfg should have lowered it to 0 or 1 values");
for (int i = 0; i < nvals; i++) {
Box* var = visit_expr(node->values[i]).o;
assert(node->values.size() <= 1 && "cfg should have lowered it to 0 or 1 values");
Value dest = node->dest ? visit_expr(node->dest) : Value();
Value var = node->values.size() ? visit_expr(node->values[0]) : Value();
// begin code for handling of softspace
bool new_softspace = (i < nvals - 1) || (!node->nl);
if (softspace(dest, new_softspace)) {
callattrInternal(dest, write_str, CLASS_OR_INST, 0, ArgPassSpec(1), space_str, 0, 0, 0, 0);
}
if (jit)
jit->emitPrint(dest, var, node->nl);
Box* str_or_unicode_var = (var->cls == unicode_cls) ? var : str(var);
callattrInternal(dest, write_str, CLASS_OR_INST, 0, ArgPassSpec(1), str_or_unicode_var, 0, 0, 0, 0);
}
if (node->dest)
printHelper(dest.o, var.o, node->nl);
else
printHelper(getSysStdout(), var.o, node->nl);
if (node->nl) {
callattrInternal(dest, write_str, CLASS_OR_INST, 0, ArgPassSpec(1), newline_str, 0, 0, 0, 0);
if (nvals == 0) {
softspace(dest, false);
}
}
return Value();
}
Value ASTInterpreter::visit_exec(AST_Exec* node) {
// TODO implement the locals and globals arguments
Box* code = visit_expr(node->body).o;
Box* globals = node->globals == NULL ? NULL : visit_expr(node->globals).o;
Box* locals = node->locals == NULL ? NULL : visit_expr(node->locals).o;
Value code = visit_expr(node->body);
Value globals = node->globals == NULL ? Value() : visit_expr(node->globals);
Value locals = node->locals == NULL ? Value() : visit_expr(node->locals);
exec(code, globals, locals, this->source_info->future_flags);
if (jit)
jit->emitExec(code, globals, locals, this->source_info->future_flags);
exec(code.o, globals.o, locals.o, this->source_info->future_flags);
return Value();
}
Value ASTInterpreter::visit_compare(AST_Compare* node) {
RELEASE_ASSERT(node->comparators.size() == 1, "not implemented");
return doBinOp(visit_expr(node->left).o, visit_expr(node->comparators[0]).o, node->ops[0], BinExpType::Compare);
Value left = visit_expr(node->left);
Value right = visit_expr(node->comparators[0]);
return doBinOp(left, right, node->ops[0], BinExpType::Compare);
}
Value ASTInterpreter::visit_expr(AST_expr* node) {
......@@ -1132,31 +1322,56 @@ Value ASTInterpreter::visit_call(AST_Call* node) {
}
std::vector<Box*, StlCompatAllocator<Box*>> args;
for (AST_expr* e : node->args)
args.push_back(visit_expr(e).o);
llvm::SmallVector<RewriterVar*, 8> args_vars;
for (AST_expr* e : node->args) {
Value v = visit_expr(e);
args.push_back(v.o);
args_vars.push_back(v);
}
std::vector<BoxedString*>* keyword_names = NULL;
if (node->keywords.size())
keyword_names = getKeywordNameStorage(node);
std::vector<BoxedString*> keywords;
for (AST_keyword* k : node->keywords) {
keywords.push_back(k->arg.getBox());
args.push_back(visit_expr(k->value).o);
Value v = visit_expr(k->value);
args.push_back(v.o);
args_vars.push_back(v);
}
if (node->starargs)
args.push_back(visit_expr(node->starargs).o);
if (node->starargs) {
Value v = visit_expr(node->starargs);
args.push_back(v.o);
args_vars.push_back(v);
}
if (node->kwargs)
args.push_back(visit_expr(node->kwargs).o);
if (node->kwargs) {
Value v = visit_expr(node->kwargs);
args.push_back(v.o);
args_vars.push_back(v);
}
ArgPassSpec argspec(node->args.size(), node->keywords.size(), node->starargs, node->kwargs);
if (is_callattr) {
CallattrFlags callattr_flags{.cls_only = callattr_clsonly, .null_on_nonexistent = false, .argspec = argspec };
return callattr(func.o, attr.getBox(), callattr_flags, args.size() > 0 ? args[0] : 0,
if (jit)
v.var = jit->emitCallattr(func, attr.getBox(), callattr_flags, args_vars, keyword_names);
v.o = callattr(func.o, attr.getBox(), callattr_flags, args.size() > 0 ? args[0] : 0,
args.size() > 1 ? args[1] : 0, args.size() > 2 ? args[2] : 0, args.size() > 3 ? &args[3] : 0,
&keywords);
keyword_names);
return v;
} else {
return runtimeCall(func.o, argspec, args.size() > 0 ? args[0] : 0, args.size() > 1 ? args[1] : 0,
args.size() > 2 ? args[2] : 0, args.size() > 3 ? &args[3] : 0, &keywords);
Value v;
if (jit)
v.var = jit->emitRuntimeCall(func, argspec, args_vars, keyword_names);
v.o = runtimeCall(func.o, argspec, args.size() > 0 ? args[0] : 0, args.size() > 1 ? args[1] : 0,
args.size() > 2 ? args[2] : 0, args.size() > 3 ? &args[3] : 0, keyword_names);
return v;
}
}
......@@ -1166,16 +1381,18 @@ Value ASTInterpreter::visit_expr(AST_Expr* node) {
}
Value ASTInterpreter::visit_num(AST_Num* node) {
if (node->num_type == AST_Num::INT)
return boxInt(node->n_int);
else if (node->num_type == AST_Num::FLOAT)
return boxFloat(node->n_float);
else if (node->num_type == AST_Num::LONG)
return createLong(node->n_long);
else if (node->num_type == AST_Num::COMPLEX)
return boxComplex(0.0, node->n_float);
Box* o = NULL;
if (node->num_type == AST_Num::INT) {
o = source_info->parent_module->getIntConstant(node->n_int);
} else if (node->num_type == AST_Num::FLOAT) {
o = source_info->parent_module->getFloatConstant(node->n_float);
} else if (node->num_type == AST_Num::LONG) {
o = source_info->parent_module->getLongConstant(node->n_long);
} else if (node->num_type == AST_Num::COMPLEX) {
o = source_info->parent_module->getPureImaginaryConstant(node->n_float);
} else
RELEASE_ASSERT(0, "not implemented");
return Value();
return Value(o, jit ? jit->imm(o) : NULL);
}
Value ASTInterpreter::visit_index(AST_Index* node) {
......@@ -1183,45 +1400,61 @@ Value ASTInterpreter::visit_index(AST_Index* node) {
}
Value ASTInterpreter::visit_repr(AST_Repr* node) {
return repr(visit_expr(node->value).o);
Value v = visit_expr(node->value);
return Value(repr(v.o), jit ? jit->emitRepr(v) : NULL);
}
Value ASTInterpreter::visit_lambda(AST_Lambda* node) {
abortJITing();
AST_Return* expr = new AST_Return();
expr->value = node->body;
std::vector<AST_stmt*> body = { expr };
return createFunction(node, node->args, body);
return Value(createFunction(node, node->args, body), NULL);
}
Value ASTInterpreter::visit_dict(AST_Dict* node) {
RELEASE_ASSERT(node->keys.size() == node->values.size(), "not implemented");
llvm::SmallVector<RewriterVar*, 8> keys;
llvm::SmallVector<RewriterVar*, 8> values;
BoxedDict* dict = new BoxedDict();
for (size_t i = 0; i < node->keys.size(); ++i) {
Box* v = visit_expr(node->values[i]).o;
Box* k = visit_expr(node->keys[i]).o;
dict->d[k] = v;
Value v = visit_expr(node->values[i]);
Value k = visit_expr(node->keys[i]);
dict->d[k.o] = v.o;
values.push_back(v);
keys.push_back(k);
}
return dict;
return Value(dict, jit ? jit->emitCreateDict(keys, values) : NULL);
}
Value ASTInterpreter::visit_set(AST_Set* node) {
llvm::SmallVector<RewriterVar*, 8> items;
BoxedSet::Set set;
for (AST_expr* e : node->elts)
set.insert(visit_expr(e).o);
for (AST_expr* e : node->elts) {
Value v = visit_expr(e);
set.insert(v.o);
items.push_back(v);
}
return new BoxedSet(std::move(set));
return Value(new BoxedSet(std::move(set)), jit ? jit->emitCreateSet(items) : NULL);
}
Value ASTInterpreter::visit_str(AST_Str* node) {
Box* o = NULL;
if (node->str_type == AST_Str::STR) {
return source_info->parent_module->getStringConstant(node->str_data);
o = source_info->parent_module->getStringConstant(node->str_data);
} else if (node->str_type == AST_Str::UNICODE) {
return decodeUTF8StringPtr(node->str_data);
o = source_info->parent_module->getUnicodeConstant(node->str_data);
} else {
RELEASE_ASSERT(0, "%d", node->str_type);
}
return Value(o, jit ? jit->imm(o) : NULL);
}
Value ASTInterpreter::visit_name(AST_Name* node) {
......@@ -1230,67 +1463,195 @@ Value ASTInterpreter::visit_name(AST_Name* node) {
}
switch (node->lookup_type) {
case ScopeInfo::VarScopeType::GLOBAL:
return getGlobal(globals, node->id.getBox());
case ScopeInfo::VarScopeType::DEREF: {
DerefInfo deref_info = scope_info->getDerefInfo(node->id);
assert(passed_closure);
BoxedClosure* closure = passed_closure;
for (int i = 0; i < deref_info.num_parents_from_passed_closure; i++) {
closure = closure->parent;
}
Box* val = closure->elts[deref_info.offset];
if (val == NULL) {
raiseExcHelper(NameError, "free variable '%s' referenced before assignment in enclosing scope",
node->id.c_str());
case ScopeInfo::VarScopeType::GLOBAL: {
Value v;
if (jit)
v.var = jit->emitGetGlobal(globals, node->id.getBox());
v.o = getGlobal(globals, node->id.getBox());
return v;
}
return val;
case ScopeInfo::VarScopeType::DEREF: {
return Value(ASTInterpreterJitInterface::derefHelper(this, node->id),
jit ? jit->emitDeref(node->id) : NULL);
}
case ScopeInfo::VarScopeType::FAST:
case ScopeInfo::VarScopeType::CLOSURE: {
SymMap::iterator it = sym_table.find(node->id);
if (it != sym_table.end())
return sym_table.getMapped(it->second);
Value v;
if (jit) {
bool is_live = false;
if (node->lookup_type == ScopeInfo::VarScopeType::FAST)
is_live = source_info->getLiveness()->isLiveAtEnd(node->id, current_block);
assertNameDefined(0, node->id.c_str(), UnboundLocalError, true);
return Value();
if (is_live)
v.var = jit->emitGetLocal(node->id);
else
v.var = jit->emitGetBlockLocal(node->id);
}
v.o = ASTInterpreterJitInterface::getLocalHelper(this, node->id);
return v;
}
case ScopeInfo::VarScopeType::NAME: {
return boxedLocalsGet(frame_info.boxedLocals, node->id.getBox(), globals);
Value v;
if (jit)
v.var = jit->emitGetBoxedLocal(node->id.getBox());
v.o = boxedLocalsGet(frame_info.boxedLocals, node->id.getBox(), globals);
return v;
}
default:
abort();
}
}
Value ASTInterpreter::visit_subscript(AST_Subscript* node) {
Value value = visit_expr(node->value);
Value slice = visit_expr(node->slice);
return getitem(value.o, slice.o);
return Value(getitem(value.o, slice.o), jit ? jit->emitGetItem(value, slice) : NULL);
}
Value ASTInterpreter::visit_list(AST_List* node) {
llvm::SmallVector<RewriterVar*, 8> items;
BoxedList* list = new BoxedList;
list->ensure(node->elts.size());
for (AST_expr* e : node->elts)
listAppendInternal(list, visit_expr(e).o);
return list;
for (AST_expr* e : node->elts) {
Value v = visit_expr(e);
items.push_back(v);
listAppendInternal(list, v.o);
}
return Value(list, jit ? jit->emitCreateList(items) : NULL);
}
Value ASTInterpreter::visit_tuple(AST_Tuple* node) {
llvm::SmallVector<RewriterVar*, 8> items;
BoxedTuple* rtn = BoxedTuple::create(node->elts.size());
int rtn_idx = 0;
for (AST_expr* e : node->elts)
rtn->elts[rtn_idx++] = visit_expr(e).o;
return rtn;
for (AST_expr* e : node->elts) {
Value v = visit_expr(e);
rtn->elts[rtn_idx++] = v.o;
items.push_back(v);
}
return Value(rtn, jit ? jit->emitCreateTuple(items) : NULL);
}
Value ASTInterpreter::visit_attribute(AST_Attribute* node) {
return pyston::getattr(visit_expr(node->value).o, node->attr.getBox());
Value v = visit_expr(node->value);
return Value(pyston::getattr(v.o, node->attr.getBox()), jit ? jit->emitGetAttr(v, node->attr.getBox()) : NULL);
}
}
int ASTInterpreterJitInterface::getCurrentBlockOffset() {
return offsetof(ASTInterpreter, current_block);
}
int ASTInterpreterJitInterface::getCurrentInstOffset() {
return offsetof(ASTInterpreter, current_inst);
}
Box* ASTInterpreterJitInterface::derefHelper(void* _interpreter, InternedString s) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
DerefInfo deref_info = interpreter->scope_info->getDerefInfo(s);
assert(interpreter->passed_closure);
BoxedClosure* closure = interpreter->passed_closure;
for (int i = 0; i < deref_info.num_parents_from_passed_closure; i++) {
closure = closure->parent;
}
Box* val = closure->elts[deref_info.offset];
if (val == NULL) {
raiseExcHelper(NameError, "free variable '%s' referenced before assignment in enclosing scope", s.c_str());
}
return val;
}
Box* ASTInterpreterJitInterface::doOSRHelper(void* _interpreter, AST_Jump* node) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
++interpreter->edgecount;
if (interpreter->edgecount >= OSR_THRESHOLD_BASELINE)
return interpreter->doOSR(node);
return NULL;
}
Box* ASTInterpreterJitInterface::getBoxedLocalHelper(void* _interpreter, BoxedString* s) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
return boxedLocalsGet(interpreter->frame_info.boxedLocals, s, interpreter->globals);
}
Box* ASTInterpreterJitInterface::getBoxedLocalsHelper(void* _interpreter) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
return interpreter->frame_info.boxedLocals;
}
Box* ASTInterpreterJitInterface::getLocalHelper(void* _interpreter, InternedString id) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
auto it = interpreter->sym_table.find(id);
if (it != interpreter->sym_table.end()) {
Box* v = interpreter->sym_table.getMapped(it->second);
assert(gc::isValidGCObject(v));
return v;
}
assertNameDefined(0, id.c_str(), UnboundLocalError, true);
return 0;
}
Box* ASTInterpreterJitInterface::landingpadHelper(void* _interpreter) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
auto& last_exception = interpreter->last_exception;
Box* type = last_exception.type;
Box* value = last_exception.value ? last_exception.value : None;
Box* traceback = last_exception.traceback ? last_exception.traceback : None;
Box* rtn = BoxedTuple::create({ type, value, traceback });
last_exception = ExcInfo(NULL, NULL, NULL);
return rtn;
}
Box* ASTInterpreterJitInterface::setExcInfoHelper(void* _interpreter, Box* type, Box* value, Box* traceback) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
interpreter->getFrameInfo()->exc = ExcInfo(type, value, traceback);
return None;
}
Box* ASTInterpreterJitInterface::uncacheExcInfoHelper(void* _interpreter) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
interpreter->getFrameInfo()->exc = ExcInfo(NULL, NULL, NULL);
return None;
}
Box* ASTInterpreterJitInterface::yieldHelper(void* _interpreter, Box* val) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
return yield(interpreter->generator, val);
}
void ASTInterpreterJitInterface::setItemNameHelper(void* _interpreter, Box* str, Box* val) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
assert(interpreter->frame_info.boxedLocals != NULL);
setitem(interpreter->frame_info.boxedLocals, str, val);
}
void ASTInterpreterJitInterface::setLocalClosureHelper(void* _interpreter, InternedString id, Box* v) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
assert(gc::isValidGCObject(v));
interpreter->sym_table[id] = v;
interpreter->created_closure->elts[interpreter->scope_info->getClosureOffset(id)] = v;
}
void ASTInterpreterJitInterface::setLocalHelper(void* _interpreter, InternedString id, Box* v) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
assert(gc::isValidGCObject(v));
interpreter->sym_table[id] = v;
}
const void* interpreter_instr_addr = (void*)&ASTInterpreter::executeInner;
Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* generator, Box* globals, Box* arg1,
......@@ -1299,7 +1660,9 @@ Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* ge
assert((!globals) == cf->clfunc->source->scoping->areGlobalsFromModule());
bool can_reopt = ENABLE_REOPT && !FORCE_INTERPRETER && (globals == NULL);
if (unlikely(can_reopt && cf->times_called > REOPT_THRESHOLD_INTERPRETER)) {
int num_blocks = cf->clfunc->source->cfg->blocks.size();
int threshold = num_blocks <= 20 ? (REOPT_THRESHOLD_BASELINE / 3) : REOPT_THRESHOLD_BASELINE;
if (unlikely(can_reopt && cf->times_called > threshold)) {
assert(!globals);
CompiledFunction* optimized = reoptCompiledFuncInternal(cf);
if (closure && generator)
......
......@@ -25,6 +25,7 @@ class GCVisitor;
class AST_expr;
class AST_stmt;
class AST_Jump;
class Box;
class BoxedClosure;
class BoxedDict;
......@@ -33,6 +34,43 @@ struct LineInfo;
extern const void* interpreter_instr_addr;
struct ASTInterpreterJitInterface {
static int getCurrentBlockOffset();
static int getCurrentInstOffset();
static Box* derefHelper(void* interp, InternedString s);
static Box* doOSRHelper(void* interp, AST_Jump* node);
static Box* getBoxedLocalHelper(void* interp, BoxedString* s);
static Box* getBoxedLocalsHelper(void* interp);
static Box* getLocalHelper(void* interp, InternedString id);
static Box* landingpadHelper(void* interp);
static Box* setExcInfoHelper(void* interp, Box* type, Box* value, Box* traceback);
static Box* uncacheExcInfoHelper(void* interp);
static Box* yieldHelper(void* interp, Box* val);
static void setItemNameHelper(void* interp, Box* str, Box* val);
static void setLocalClosureHelper(void* interp, InternedString id, Box* v);
static void setLocalHelper(void* interp, InternedString id, Box* v);
};
class RewriterVar;
struct Value {
union {
bool b;
int64_t n;
double d;
Box* o;
};
RewriterVar* var;
operator RewriterVar*() { return var; }
Value() : o(0), var(0) {}
Value(bool b, RewriterVar* var) : b(b), var(var) {}
Value(int64_t n, RewriterVar* var) : n(n), var(var) {}
Value(double d, RewriterVar* var) : d(d), var(var) {}
Value(Box* o, RewriterVar* var) : o(o), var(var) {}
};
void setupInterpreter();
Box* astInterpretFunction(CompiledFunction* f, int nargs, Box* closure, Box* generator, Box* globals, Box* arg1,
Box* arg2, Box* arg3, Box** args);
......
// Copyright (c) 2014-2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "codegen/baseline_jit.h"
#include <llvm/ADT/DenseMap.h>
#include <llvm/ADT/DenseSet.h>
#include "codegen/irgen/hooks.h"
#include "codegen/memmgr.h"
#include "core/cfg.h"
#include "runtime/objmodel.h"
#include "runtime/set.h"
#include "runtime/types.h"
namespace pyston {
static llvm::DenseSet<CFGBlock*> blocks_aborted;
JitCodeBlock::JitCodeBlock(llvm::StringRef name)
: frame_manager(false /* don't omit frame pointers */),
code(new uint8_t[code_size]),
entry_offset(0),
epilog_offset(0),
a(code.get(), code_size - epilog_size),
is_currently_writing(false),
asm_failed(false) {
static StatCounter num_jit_code_blocks("num_baselinejit_code_blocks");
num_jit_code_blocks.log();
static StatCounter num_jit_total_bytes("num_baselinejit_total_bytes");
num_jit_total_bytes.log(code_size);
// emit prolog
a.push(assembler::RBP);
a.mov(assembler::RSP, assembler::RBP);
static_assert(scratch_size % 16 == 0, "stack aligment code depends on this");
// subtract scratch size + 8bytes to align stack after the push.
a.sub(assembler::Immediate(scratch_size + 8), assembler::RSP);
a.push(assembler::RDI); // push interpreter pointer
a.jmp(assembler::Indirect(assembler::RSI, offsetof(CFGBlock, code))); // jump to block
entry_offset = a.bytesWritten();
// emit epilog
epilog_offset = code_size - epilog_size;
assembler::Assembler endAsm(code.get() + epilog_offset, epilog_size);
endAsm.leave();
endAsm.retq();
RELEASE_ASSERT(!endAsm.hasFailed(), "");
// generate eh frame...
frame_manager.writeAndRegister(code.get(), code_size);
g.func_addr_registry.registerFunction(("bjit: " + name).str(), code.get(), code_size, NULL);
}
std::unique_ptr<JitFragmentWriter> JitCodeBlock::newFragment(CFGBlock* block, int patch_jump_offset) {
if (is_currently_writing || blocks_aborted.count(block))
return std::unique_ptr<JitFragmentWriter>();
is_currently_writing = true;
StackInfo stack_info(scratch_size, 16);
std::unordered_set<int> live_outs;
void* fragment_start = a.curInstPointer() - patch_jump_offset;
long fragment_offset = a.bytesWritten() - patch_jump_offset;
long bytes_left = a.bytesLeft() + patch_jump_offset;
std::unique_ptr<ICInfo> ic_info(new ICInfo(fragment_start, nullptr, nullptr, stack_info, 1, bytes_left,
llvm::CallingConv::C, live_outs, assembler::RAX, 0));
std::unique_ptr<ICSlotRewrite> rewrite(new ICSlotRewrite(ic_info.get(), ""));
return std::unique_ptr<JitFragmentWriter>(new JitFragmentWriter(block, std::move(ic_info), std::move(rewrite),
fragment_offset, epilog_offset - fragment_offset,
patch_jump_offset, a.getStartAddr(), *this));
}
void JitCodeBlock::fragmentAbort(bool not_enough_space) {
asm_failed = not_enough_space;
is_currently_writing = false;
}
void JitCodeBlock::fragmentFinished(int bytes_written, int num_bytes_overlapping, void* next_fragment_start) {
assert(next_fragment_start == bytes_written + a.curInstPointer() - num_bytes_overlapping);
a.setCurInstPointer((uint8_t*)next_fragment_start);
asm_failed = false;
is_currently_writing = false;
}
JitFragmentWriter::JitFragmentWriter(CFGBlock* block, std::unique_ptr<ICInfo> ic_info,
std::unique_ptr<ICSlotRewrite> rewrite, int code_offset, int epilog_offset,
int num_bytes_overlapping, void* entry_code, JitCodeBlock& code_block)
: Rewriter(std::move(rewrite), 0, {}),
block(block),
code_offset(code_offset),
epilog_offset(epilog_offset),
num_bytes_overlapping(num_bytes_overlapping),
num_bytes_forward_jump(0),
entry_code(entry_code),
code_block(code_block),
interp(0),
ic_info(std::move(ic_info)) {
interp = createNewVar();
addLocationToVar(interp, Location(Location::Stack, 0));
interp->setAttr(ASTInterpreterJitInterface::getCurrentBlockOffset(), imm(block));
}
RewriterVar* JitFragmentWriter::imm(uint64_t val) {
return loadConst(val);
}
RewriterVar* JitFragmentWriter::imm(void* val) {
return loadConst((uint64_t)val);
}
RewriterVar* JitFragmentWriter::emitAugbinop(RewriterVar* lhs, RewriterVar* rhs, int op_type) {
#if ENABLE_BASELINEJIT_ICS
return call(false, (void*)augbinopICHelper, imm(new AugBinopIC), lhs, rhs, imm(op_type));
#else
return call(false, (void*)augbinop, lhs, rhs, imm(op_type));
#endif
}
RewriterVar* JitFragmentWriter::emitBinop(RewriterVar* lhs, RewriterVar* rhs, int op_type) {
#if ENABLE_BASELINEJIT_ICS
return call(false, (void*)binopICHelper, imm(new BinopIC), lhs, rhs, imm(op_type));
#else
return call(false, (void*)binop, lhs, rhs, imm(op_type));
#endif
}
RewriterVar* JitFragmentWriter::emitCallattr(RewriterVar* obj, BoxedString* attr, CallattrFlags flags,
const llvm::ArrayRef<RewriterVar*> args,
std::vector<BoxedString*>* keyword_names) {
// We could make this faster but for now: keep it simple, stupid...
RewriterVar* attr_var = imm(attr);
RewriterVar* flags_var = imm(flags.asInt());
RewriterVar* keyword_names_var = keyword_names ? imm(keyword_names) : nullptr;
RewriterVar* args_array = nullptr;
if (args.size())
args_array = allocArgs(args);
else
RELEASE_ASSERT(!keyword_names_var, "0 args but keyword names are set");
bool use_ic = false;
RewriterVar::SmallVector call_args;
call_args.push_back(obj);
call_args.push_back(attr_var);
call_args.push_back(flags_var);
#if ENABLE_BASELINEJIT_ICS
if (!keyword_names_var
&& flags.argspec.totalPassed() < 4) { // looks like runtime ICs with 7 or more args don't work right now..
use_ic = true;
call_args.push_back(imm(new CallattrIC));
}
#endif
if (args_array)
call_args.push_back(args_array);
if (keyword_names_var)
call_args.push_back(keyword_names_var);
#if ENABLE_BASELINEJIT_ICS
if (use_ic)
return call(false, (void*)callattrHelperIC, call_args);
#endif
return call(false, (void*)callattrHelper, call_args);
}
RewriterVar* JitFragmentWriter::emitCompare(RewriterVar* lhs, RewriterVar* rhs, int op_type) {
#if ENABLE_BASELINEJIT_ICS
return call(false, (void*)compareICHelper, imm(new CompareIC), lhs, rhs, imm(op_type));
#else
return call(false, (void*)compare, lhs, rhs, imm(op_type));
#endif
}
RewriterVar* JitFragmentWriter::emitCreateDict(const llvm::ArrayRef<RewriterVar*> keys,
const llvm::ArrayRef<RewriterVar*> values) {
assert(keys.size() == values.size());
if (keys.empty())
return call(false, (void*)createDict);
else
return call(false, (void*)createDictHelper, imm(keys.size()), allocArgs(keys), allocArgs(values));
}
RewriterVar* JitFragmentWriter::emitCreateList(const llvm::ArrayRef<RewriterVar*> values) {
auto num = values.size();
if (num == 0)
return call(false, (void*)createList);
else
return call(false, (void*)createListHelper, imm(num), allocArgs(values));
}
RewriterVar* JitFragmentWriter::emitCreateSet(const llvm::ArrayRef<RewriterVar*> values) {
return call(false, (void*)createSetHelper, imm(values.size()), allocArgs(values));
}
RewriterVar* JitFragmentWriter::emitCreateSlice(RewriterVar* start, RewriterVar* stop, RewriterVar* step) {
return call(false, (void*)createSlice, start, stop, step);
}
RewriterVar* JitFragmentWriter::emitCreateTuple(const llvm::ArrayRef<RewriterVar*> values) {
auto num = values.size();
if (num == 0)
return imm(EmptyTuple);
else if (num == 1)
return call(false, (void*)BoxedTuple::create1, values[0]);
else if (num == 2)
return call(false, (void*)BoxedTuple::create2, values[0], values[1]);
else if (num == 3)
return call(false, (void*)BoxedTuple::create3, values[0], values[1], values[2]);
else
return call(false, (void*)createTupleHelper, imm(num), allocArgs(values));
}
RewriterVar* JitFragmentWriter::emitDeref(InternedString s) {
return call(false, (void*)ASTInterpreterJitInterface::derefHelper, getInterp(),
#ifndef NDEBUG
imm(asUInt(s).first), imm(asUInt(s).second));
#else
imm(asUInt(s)));
#endif
}
RewriterVar* JitFragmentWriter::emitExceptionMatches(RewriterVar* v, RewriterVar* cls) {
return call(false, (void*)exceptionMatchesHelper, v, cls);
}
RewriterVar* JitFragmentWriter::emitGetAttr(RewriterVar* obj, BoxedString* s) {
#if ENABLE_BASELINEJIT_ICS
return call(false, (void*)getAttrICHelper, imm(new GetAttrIC), obj, imm(s));
#else
return call(false, (void*)getattr, obj, imm(s));
#endif
}
RewriterVar* JitFragmentWriter::emitGetBlockLocal(InternedString s) {
auto it = local_syms.find(s);
if (it == local_syms.end())
return emitGetLocal(s);
return it->second;
}
RewriterVar* JitFragmentWriter::emitGetBoxedLocal(BoxedString* s) {
return call(false, (void*)ASTInterpreterJitInterface::getBoxedLocalHelper, getInterp(), imm(s));
}
RewriterVar* JitFragmentWriter::emitGetBoxedLocals() {
return call(false, (void*)ASTInterpreterJitInterface::getBoxedLocalsHelper, getInterp());
}
RewriterVar* JitFragmentWriter::emitGetClsAttr(RewriterVar* obj, BoxedString* s) {
return call(false, (void*)getclsattr, obj, imm(s));
}
RewriterVar* JitFragmentWriter::emitGetGlobal(Box* global, BoxedString* s) {
#if ENABLE_BASELINEJIT_ICS
return call(false, (void*)getGlobalICHelper, imm(new GetGlobalIC), imm(global), imm(s));
#else
return call(false, (void*)getGlobal, imm(global), imm(s));
#endif
}
RewriterVar* JitFragmentWriter::emitGetItem(RewriterVar* value, RewriterVar* slice) {
#if ENABLE_BASELINEJIT_ICS
return call(false, (void*)getitemICHelper, imm(new GetItemIC), value, slice);
#else
return call(false, (void*)getitem, value, slice);
#endif
}
RewriterVar* JitFragmentWriter::emitGetLocal(InternedString s) {
return call(false, (void*)ASTInterpreterJitInterface::getLocalHelper, getInterp(),
#ifndef NDEBUG
imm(asUInt(s).first), imm(asUInt(s).second));
#else
imm(asUInt(s)));
#endif
}
RewriterVar* JitFragmentWriter::emitGetPystonIter(RewriterVar* v) {
return call(false, (void*)getPystonIter, v);
}
RewriterVar* JitFragmentWriter::emitHasnext(RewriterVar* v) {
return call(false, (void*)hasnextHelper, v);
}
RewriterVar* JitFragmentWriter::emitLandingpad() {
return call(false, (void*)ASTInterpreterJitInterface::landingpadHelper, getInterp());
}
RewriterVar* JitFragmentWriter::emitNonzero(RewriterVar* v) {
return call(false, (void*)nonzeroHelper, v);
}
RewriterVar* JitFragmentWriter::emitNotNonzero(RewriterVar* v) {
return call(false, (void*)notHelper, v);
}
RewriterVar* JitFragmentWriter::emitRepr(RewriterVar* v) {
return call(false, (void*)repr, v);
}
RewriterVar* JitFragmentWriter::emitRuntimeCall(RewriterVar* obj, ArgPassSpec argspec,
const llvm::ArrayRef<RewriterVar*> args,
std::vector<BoxedString*>* keyword_names) {
// We could make this faster but for now: keep it simple, stupid..
RewriterVar* argspec_var = imm(argspec.asInt());
RewriterVar* keyword_names_var = keyword_names ? imm(keyword_names) : nullptr;
RewriterVar* args_array = nullptr;
if (args.size()) {
args_array = allocArgs(args);
} else
RELEASE_ASSERT(!keyword_names_var, "0 args but keyword names are set");
bool use_ic = false;
RewriterVar::SmallVector call_args;
call_args.push_back(obj);
call_args.push_back(argspec_var);
#if ENABLE_BASELINEJIT_ICS
if (!keyword_names) { // looks like runtime ICs with 7 or more args don't work right now..
use_ic = true;
call_args.push_back(imm(new RuntimeCallIC));
}
#endif
if (args_array)
call_args.push_back(args_array);
if (keyword_names_var)
call_args.push_back(keyword_names_var);
#if ENABLE_BASELINEJIT_ICS
if (use_ic)
return call(false, (void*)runtimeCallHelperIC, call_args);
#endif
return call(false, (void*)runtimeCallHelper, call_args);
}
RewriterVar* JitFragmentWriter::emitUnaryop(RewriterVar* v, int op_type) {
#if ENABLE_BASELINEJIT_ICS
return call(false, (void*)unaryopICHelper, imm(new UnaryopIC), v, imm(op_type));
#else
return call(false, (void*)unaryop, v, imm(op_type));
#endif
}
RewriterVar* JitFragmentWriter::emitUnpackIntoArray(RewriterVar* v, uint64_t num) {
RewriterVar* array = call(false, (void*)unpackIntoArray, v, imm(num));
return array;
}
RewriterVar* JitFragmentWriter::emitYield(RewriterVar* v) {
return call(false, (void*)ASTInterpreterJitInterface::yieldHelper, getInterp(), v);
}
void JitFragmentWriter::emitExec(RewriterVar* code, RewriterVar* globals, RewriterVar* locals, FutureFlags flags) {
if (!globals)
globals = imm(0ul);
if (!locals)
locals = imm(0ul);
call(false, (void*)exec, code, globals, locals, imm(flags));
}
void JitFragmentWriter::emitJump(CFGBlock* b) {
RewriterVar* next = imm(b);
addAction([=]() { _emitJump(b, next, num_bytes_forward_jump); }, { next }, ActionType::NORMAL);
}
void JitFragmentWriter::emitOSRPoint(AST_Jump* node) {
RewriterVar* node_var = imm(node);
RewriterVar* result = createNewVar();
addAction([=]() { _emitOSRPoint(result, node_var); }, { result, node_var, getInterp() }, ActionType::NORMAL);
}
void JitFragmentWriter::emitPrint(RewriterVar* dest, RewriterVar* var, bool nl) {
if (!dest)
dest = call(false, (void*)getSysStdout);
if (!var)
var = imm(0ul);
call(false, (void*)printHelper, dest, var, imm(nl));
}
void JitFragmentWriter::emitRaise0() {
call(false, (void*)raise0);
}
void JitFragmentWriter::emitRaise3(RewriterVar* arg0, RewriterVar* arg1, RewriterVar* arg2) {
call(false, (void*)raise3, arg0, arg1, arg2);
}
void JitFragmentWriter::emitReturn(RewriterVar* v) {
addAction([=]() { _emitReturn(v); }, { v }, ActionType::NORMAL);
}
void JitFragmentWriter::emitSetAttr(RewriterVar* obj, BoxedString* s, RewriterVar* attr) {
#if ENABLE_BASELINEJIT_ICS
call(false, (void*)setAttrICHelper, imm(new SetAttrIC), obj, imm(s), attr);
#else
call(false, (void*)setattr, obj, imm(s), attr);
#endif
}
void JitFragmentWriter::emitSetBlockLocal(InternedString s, RewriterVar* v) {
local_syms[s] = v;
}
void JitFragmentWriter::emitSetCurrentInst(AST_stmt* node) {
getInterp()->setAttr(ASTInterpreterJitInterface::getCurrentInstOffset(), imm(node));
}
void JitFragmentWriter::emitSetExcInfo(RewriterVar* type, RewriterVar* value, RewriterVar* traceback) {
call(false, (void*)ASTInterpreterJitInterface::setExcInfoHelper, getInterp(), type, value, traceback);
}
void JitFragmentWriter::emitSetGlobal(Box* global, BoxedString* s, RewriterVar* v) {
#if ENABLE_BASELINEJIT_ICS
call(false, (void*)setGlobalICHelper, imm(new SetGlobalIC), imm(global), imm(s), v);
#else
call(false, (void*)setGlobal, imm(global), imm(s), v);
#endif
}
void JitFragmentWriter::emitSetItem(RewriterVar* target, RewriterVar* slice, RewriterVar* value) {
#if ENABLE_BASELINEJIT_ICS
call(false, (void*)setitemICHelper, imm(new SetItemIC), target, slice, value);
#else
call(false, (void*)setitem, target, slice, value);
#endif
}
void JitFragmentWriter::emitSetItemName(BoxedString* s, RewriterVar* v) {
call(false, (void*)ASTInterpreterJitInterface::setItemNameHelper, getInterp(), imm(s), v);
}
void JitFragmentWriter::emitSetLocal(InternedString s, bool set_closure, RewriterVar* v) {
void* func = set_closure ? (void*)ASTInterpreterJitInterface::setLocalClosureHelper
: (void*)ASTInterpreterJitInterface::setLocalHelper;
call(false, func, getInterp(),
#ifndef NDEBUG
imm(asUInt(s).first), imm(asUInt(s).second),
#else
imm(asUInt(s)),
#endif
v);
}
void JitFragmentWriter::emitSideExit(RewriterVar* v, Box* cmp_value, CFGBlock* next_block) {
RewriterVar* var = imm(cmp_value);
RewriterVar* next_block_var = imm(next_block);
addAction([=]() { _emitSideExit(v, var, next_block, next_block_var); }, { v, var, next_block_var },
ActionType::NORMAL);
}
void JitFragmentWriter::emitUncacheExcInfo() {
call(false, (void*)ASTInterpreterJitInterface::uncacheExcInfoHelper, getInterp());
}
void JitFragmentWriter::abortCompilation() {
blocks_aborted.insert(block);
code_block.fragmentAbort(false);
abort();
}
int JitFragmentWriter::finishCompilation() {
RELEASE_ASSERT(!assembler->hasFailed(), "");
commit();
if (failed) {
blocks_aborted.insert(block);
code_block.fragmentAbort(false);
return 0;
}
if (assembler->hasFailed()) {
code_block.fragmentAbort(true /* not_enough_space */);
return 0;
}
block->code = (void*)((uint64_t)entry_code + code_offset);
block->entry_code = (decltype(block->entry_code))entry_code;
void* next_fragment_start = (uint8_t*)block->code + assembler->bytesWritten();
code_block.fragmentFinished(assembler->bytesWritten(), num_bytes_overlapping, next_fragment_start);
return num_bytes_forward_jump;
}
bool JitFragmentWriter::finishAssembly(int continue_offset) {
return !assembler->hasFailed();
}
RewriterVar* JitFragmentWriter::allocArgs(const llvm::ArrayRef<RewriterVar*> args) {
auto num = args.size();
RewriterVar* array = allocate(num);
for (int i = 0; i < num; ++i)
array->setAttr(sizeof(void*) * i, args[i]);
return array;
}
#ifndef NDEBUG
std::pair<uint64_t, uint64_t> JitFragmentWriter::asUInt(InternedString s) {
static_assert(sizeof(InternedString) == sizeof(uint64_t) * 2, "");
union U {
U(InternedString is) : is(is) {}
InternedString is;
uint64_t u[2];
} u(s);
return std::make_pair(u.u[0], u.u[1]);
}
#else
uint64_t JitFragmentWriter::asUInt(InternedString s) {
static_assert(sizeof(InternedString) == sizeof(uint64_t), "");
union U {
U(InternedString is) : is(is) {}
InternedString is;
uint64_t u;
} u(s);
return u.u;
}
#endif
RewriterVar* JitFragmentWriter::getInterp() {
return interp;
}
Box* JitFragmentWriter::augbinopICHelper(AugBinopIC* ic, Box* lhs, Box* rhs, int op) {
return ic->call(lhs, rhs, op);
}
Box* JitFragmentWriter::binopICHelper(BinopIC* ic, Box* lhs, Box* rhs, int op) {
return ic->call(lhs, rhs, op);
}
#if ENABLE_BASELINEJIT_ICS
Box* JitFragmentWriter::callattrHelperIC(Box* obj, BoxedString* attr, CallattrFlags flags, CallattrIC* ic, Box** args) {
auto arg_tuple = getTupleFromArgsArray(&args[0], flags.argspec.totalPassed());
return ic->call(obj, attr, flags, std::get<0>(arg_tuple), std::get<1>(arg_tuple), std::get<2>(arg_tuple), NULL,
NULL);
}
#endif
Box* JitFragmentWriter::callattrHelper(Box* obj, BoxedString* attr, CallattrFlags flags, Box** args,
std::vector<BoxedString*>* keyword_names) {
auto arg_tuple = getTupleFromArgsArray(&args[0], flags.argspec.totalPassed());
Box* r = callattr(obj, attr, flags, std::get<0>(arg_tuple), std::get<1>(arg_tuple), std::get<2>(arg_tuple),
std::get<3>(arg_tuple), keyword_names);
assert(gc::isValidGCObject(r));
return r;
}
Box* JitFragmentWriter::compareICHelper(CompareIC* ic, Box* lhs, Box* rhs, int op) {
return ic->call(lhs, rhs, op);
}
Box* JitFragmentWriter::createDictHelper(uint64_t num, Box** keys, Box** values) {
BoxedDict* dict = (BoxedDict*)createDict();
for (uint64_t i = 0; i < num; ++i) {
assert(gc::isValidGCObject(keys[i]));
assert(gc::isValidGCObject(values[i]));
dict->d[keys[i]] = values[i];
}
return dict;
}
Box* JitFragmentWriter::createListHelper(uint64_t num, Box** data) {
BoxedList* list = (BoxedList*)createList();
list->ensure(num);
for (uint64_t i = 0; i < num; ++i) {
assert(gc::isValidGCObject(data[i]));
listAppendInternal(list, data[i]);
}
return list;
}
Box* JitFragmentWriter::createSetHelper(uint64_t num, Box** data) {
BoxedSet* set = (BoxedSet*)createSet();
for (int i = 0; i < num; ++i)
set->s.insert(data[i]);
return set;
}
Box* JitFragmentWriter::createTupleHelper(uint64_t num, Box** data) {
return BoxedTuple::create(num, data);
}
Box* JitFragmentWriter::exceptionMatchesHelper(Box* obj, Box* cls) {
return boxBool(exceptionMatches(obj, cls));
}
Box* JitFragmentWriter::getAttrICHelper(GetAttrIC* ic, Box* o, BoxedString* attr) {
return ic->call(o, attr);
}
Box* JitFragmentWriter::getGlobalICHelper(GetGlobalIC* ic, Box* o, BoxedString* s) {
return ic->call(o, s);
}
Box* JitFragmentWriter::getitemICHelper(GetItemIC* ic, Box* o, Box* attr) {
return ic->call(o, attr);
}
Box* JitFragmentWriter::hasnextHelper(Box* b) {
return boxBool(pyston::hasnext(b));
}
Box* JitFragmentWriter::nonzeroHelper(Box* b) {
return boxBool(b->nonzeroIC());
}
Box* JitFragmentWriter::notHelper(Box* b) {
return boxBool(!b->nonzeroIC());
}
#if ENABLE_BASELINEJIT_ICS
Box* JitFragmentWriter::runtimeCallHelperIC(Box* obj, ArgPassSpec argspec, RuntimeCallIC* ic, Box** args) {
auto arg_tuple = getTupleFromArgsArray(&args[0], argspec.totalPassed());
return ic->call(obj, argspec, std::get<0>(arg_tuple), std::get<1>(arg_tuple), std::get<2>(arg_tuple),
std::get<3>(arg_tuple));
}
#endif
Box* JitFragmentWriter::runtimeCallHelper(Box* obj, ArgPassSpec argspec, Box** args,
std::vector<BoxedString*>* keyword_names) {
auto arg_tuple = getTupleFromArgsArray(&args[0], argspec.totalPassed());
return runtimeCall(obj, argspec, std::get<0>(arg_tuple), std::get<1>(arg_tuple), std::get<2>(arg_tuple),
std::get<3>(arg_tuple), keyword_names);
}
Box* JitFragmentWriter::setAttrICHelper(SetAttrIC* ic, Box* o, BoxedString* attr, Box* value) {
return ic->call(o, attr, value);
}
Box* JitFragmentWriter::setGlobalICHelper(SetGlobalIC* ic, Box* o, BoxedString* s, Box* v) {
return ic->call(o, s, v);
}
Box* JitFragmentWriter::setitemICHelper(SetItemIC* ic, Box* o, Box* attr, Box* value) {
return ic->call(o, attr, value);
}
Box* JitFragmentWriter::unaryopICHelper(UnaryopIC* ic, Box* obj, int op) {
return ic->call(obj, op);
}
void JitFragmentWriter::_emitJump(CFGBlock* b, RewriterVar* block_next, int& size_of_indirect_jump) {
size_of_indirect_jump = 0;
if (b->code) {
int64_t offset = (uint64_t)b->code - ((uint64_t)entry_code + code_offset);
if (isLargeConstant(offset)) {
assembler->mov(assembler::Immediate(b->code), assembler::R11);
assembler->jmpq(assembler::R11);
} else
assembler->jmp(assembler::JumpDestination::fromStart(offset));
} else {
int num_bytes = assembler->bytesWritten();
block_next->getInReg(assembler::RAX, true);
assembler->mov(assembler::Indirect(assembler::RAX, 8), assembler::RSI);
assembler->test(assembler::RSI, assembler::RSI);
assembler->je(assembler::JumpDestination::fromStart(epilog_offset));
assembler->jmp(assembler::Indirect(assembler::RAX, offsetof(CFGBlock, code)));
size_of_indirect_jump = assembler->bytesWritten() - num_bytes;
}
block_next->bumpUse();
}
void JitFragmentWriter::_emitOSRPoint(RewriterVar* result, RewriterVar* node_var) {
RewriterVar::SmallVector args;
args.push_back(getInterp());
args.push_back(node_var);
_call(result, false, (void*)ASTInterpreterJitInterface::doOSRHelper, args, RewriterVar::SmallVector());
auto result_reg = result->getInReg(assembler::RDX);
result->bumpUse();
assembler->test(result_reg, result_reg);
{
assembler::ForwardJump je(*assembler, assembler::COND_EQUAL);
assembler->mov(assembler::Immediate(0ul), assembler::RAX); // TODO: use xor
assembler->jmp(assembler::JumpDestination::fromStart(epilog_offset));
}
assertConsistent();
}
void JitFragmentWriter::_emitReturn(RewriterVar* return_val) {
return_val->getInReg(assembler::RDX, true);
assembler->mov(assembler::Immediate(0ul), assembler::RAX); // TODO: use xor
assembler->jmp(assembler::JumpDestination::fromStart(epilog_offset));
return_val->bumpUse();
}
void JitFragmentWriter::_emitSideExit(RewriterVar* var, RewriterVar* val_constant, CFGBlock* next_block,
RewriterVar* next_block_var) {
assert(val_constant->is_constant);
assert(next_block_var->is_constant);
uint64_t val = val_constant->constant_value;
assembler::Register var_reg = var->getInReg();
if (isLargeConstant(val)) {
assembler::Register reg = val_constant->getInReg(Location::any(), true, /* otherThan */ var_reg);
assembler->cmp(var_reg, reg);
} else {
assembler->cmp(var_reg, assembler::Immediate(val));
}
{
assembler::ForwardJump jne(*assembler, assembler::COND_EQUAL);
int bytes = 0;
_emitJump(next_block, next_block_var, bytes);
if (bytes) {
// TODO: We generated an indirect jump.
// If we later on JIT the dest block we could patch this code to a direct jump to the dest.
}
}
var->bumpUse();
val_constant->bumpUse();
assertConsistent();
}
}
// Copyright (c) 2014-2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PYSTON_CODEGEN_BASELINEJIT_H
#define PYSTON_CODEGEN_BASELINEJIT_H
#include <llvm/ADT/ArrayRef.h>
#include "asm_writing/rewriter.h"
#include "codegen/ast_interpreter.h"
#include "gc/heap.h"
#include "runtime/ics.h"
namespace pyston {
#define ENABLE_BASELINEJIT_ICS 1
class AST_stmt;
class Box;
class BoxedDict;
class BoxedList;
class BoxedTuple;
class SetGlobalIC;
class GetGlobalIC;
class SetAttrIC;
class GetAttrIC;
class SetItemIC;
class GetItemIC;
class CompareIC;
class AugBinopIC;
class UnaryopIC;
class RuntimeCallIC;
class JitFragmentWriter;
// This JIT tier is designed as Pystons entry level JIT tier (executed after a only a few dozens runs of a basic block)
// which emits code very quick. It does not do any type specializations but can use inline caches.
// It operates on a basic block at a time (=CFGBLock*) and supports very fast switching between the
// interpreter and the JITed code on every block start/end.
//
// To archive this it's tightly integrated with the AST Interpreter and always operates on an ASTInterpreter instance.
// The process works like this:
// - in the ASTInterpreter main loop we will check on every basic block start if we have already machine code for the
// basic block
// - if we have, we will directly call it (='entry_code' pointer inside the CFGBlock)
// - if we don't have but determined that the block is hot (loop backedge threshold reached or function got called
// often enough) and and we haven't yet tried to JIT it we will start with the JITing process:
// - create/reuse a JitCodeBlock for the function
// - create a new JitFragmentWriter for the basic block to JIT
// - interpret the basic block and in addition call into corresponding emit* functions of the JitFragmentWriter on
// every AST node encountered.
// - if a node is encountered which is not supported, abort JITing of the block and blacklist this block
// - if we reached the control flow changing node of the basic block (e.g. a branch, return or jump node) we finish
// JITing the block.
//
// A JITed block is allowed to jump directly to another JITed basic block or if the block is not yet JITed (or we are
// unable to JIT it) we return from the function and the interpreter will immediatelly continue interpreting the next
// block.
// JitCodeBlock manages a fixed size memory block which stores JITed code.
// It can contain a variable number of blocks generated by JitFragmentWriter instances.
// Currently a JitFragment always contains the code of a single CFGBlock*.
// A JitFragment can get called from the Interpreter by calling 'entry_code' which will jump to the fragment start or
// it can get executed by a jump from another fragment.
// At every fragment end we can jump to another fragment, fallback to the Interpreter or exit.
// This means we are not allowed to assume that a register contains a specific value between JitFragments.
// This also means that we are allowed to store a Python variable which only lives in the current CFGBLock* inside a
// register or stack slot but we aren't if it outlives the block - we have to store it in the interpreter instance.
//
// To execute a specific CFGBlock one has to call:
// CFGBlock* block;
// block->entry_code(ast_interpreter_instance, block)
//
// Signature of a JitCodeBlock:
// std::pair<CFGBlock*, Box*>(*entry_code)(ASTInterpreter* interp, CFGBlock* block)
// args:
// interp: instance to the ASTInterpreter
// block: block to execute
//
// return value:
// first: next block to execute in the interpreter
// if 0, we executed a return node and should return second
// second: return value only used if first == 0
//
// Basic layout of generated code block is:
// entry_code:
// push %rbp ; setup frame pointer
// mov %rsp,%rbp
// sub $0x108,%rsp ; setup scratch, 0x108 = scratch_size + 8 (=stack alignment)
// push %rdi ; save the pointer to ASTInterpreter instance
// jmpq *0x8(%rsi) ; jump to block->code
// possible values: first_JitFragment, second_JitFragment,...
//
// first_JitFragment:
// ...
// ; Side exit (e.g. if <val> <block1> else <block2>
// movabs $0x1270014108,%rcx ; rcx = True
// cmp %rax,%rcx ; rax == True
// jne end_side_exit
// mov %rax,0x10(%rsp) ;
// movabs $0x215bb60,%rax ; rax = CFGBlock* to call next (rax is also the 1. return reg)
// mov 0x8(%rax),%rsi ; load CFGBlock->code
// test %rsi,%rsi ; CFGBlock->code == 0
// je epilog ; exit to interpreter if code == 0
// jmpq *0x8(%rax) ; jump to new jit fragment (e.g second_JitFragment)
// end_side_exit:
// ....
// second_JitFragment:
// ...
//
// nth_JitFragment:
// ... ; direct jump previous JITed block
// jmp first_JitFragment
//
// epilog: ; code which jumps to epilog has to make sure that
// ; rax contains the next block to execute
// ; or 0 if we are finished but then rdx must contain the Box* value to return
// leave
// ret
//
class JitCodeBlock {
private:
static constexpr int scratch_size = 256;
static constexpr int code_size = 4096 * 2;
static constexpr int epilog_size = 2; // size of [leave, ret] in bytes
EHFrameManager frame_manager;
std::unique_ptr<uint8_t[]> code;
int entry_offset;
int epilog_offset;
assembler::Assembler a;
bool is_currently_writing;
bool asm_failed;
public:
JitCodeBlock(llvm::StringRef name);
std::unique_ptr<JitFragmentWriter> newFragment(CFGBlock* block, int patch_jump_offset = 0);
bool shouldCreateNewBlock() const { return asm_failed || a.bytesLeft() < 128; }
void fragmentAbort(bool not_enough_space);
void fragmentFinished(int bytes_witten, int num_bytes_overlapping, void* next_fragment_start);
};
class JitFragmentWriter : public Rewriter {
private:
CFGBlock* block;
int code_offset; // offset inside the JitCodeBlock to the start of this block
int epilog_offset; // offset inside the JitCodeBlock to the epilog
int num_bytes_overlapping; // num of bytes this block overlaps with the prev. used to patch unessary forward jumps
int num_bytes_forward_jump; // number of bytes emited for the last forward jump to the next block. This is used to
// patch unessary forward jumps when the next fragment is emited (it becomes
// num_bytes_overlapping)
void* entry_code; // JitCodeBlock start address. Mmust have an offset of 0 into the code block
JitCodeBlock& code_block;
RewriterVar* interp;
llvm::DenseMap<InternedString, RewriterVar*> local_syms;
std::unique_ptr<ICInfo> ic_info;
public:
JitFragmentWriter(CFGBlock* block, std::unique_ptr<ICInfo> ic_info, std::unique_ptr<ICSlotRewrite> rewrite,
int code_offset, int epilog_offset, int num_bytes_overlapping, void* entry_code,
JitCodeBlock& code_block);
RewriterVar* imm(uint64_t val);
RewriterVar* imm(void* val);
RewriterVar* emitAugbinop(RewriterVar* lhs, RewriterVar* rhs, int op_type);
RewriterVar* emitBinop(RewriterVar* lhs, RewriterVar* rhs, int op_type);
RewriterVar* emitCallattr(RewriterVar* obj, BoxedString* attr, CallattrFlags flags,
const llvm::ArrayRef<RewriterVar*> args, std::vector<BoxedString*>* keyword_names);
RewriterVar* emitCompare(RewriterVar* lhs, RewriterVar* rhs, int op_type);
RewriterVar* emitCreateDict(const llvm::ArrayRef<RewriterVar*> keys, const llvm::ArrayRef<RewriterVar*> values);
RewriterVar* emitCreateList(const llvm::ArrayRef<RewriterVar*> values);
RewriterVar* emitCreateSet(const llvm::ArrayRef<RewriterVar*> values);
RewriterVar* emitCreateSlice(RewriterVar* start, RewriterVar* stop, RewriterVar* step);
RewriterVar* emitCreateTuple(const llvm::ArrayRef<RewriterVar*> values);
RewriterVar* emitDeref(InternedString s);
RewriterVar* emitExceptionMatches(RewriterVar* v, RewriterVar* cls);
RewriterVar* emitGetAttr(RewriterVar* obj, BoxedString* s);
RewriterVar* emitGetBlockLocal(InternedString s);
RewriterVar* emitGetBoxedLocal(BoxedString* s);
RewriterVar* emitGetBoxedLocals();
RewriterVar* emitGetClsAttr(RewriterVar* obj, BoxedString* s);
RewriterVar* emitGetGlobal(Box* global, BoxedString* s);
RewriterVar* emitGetItem(RewriterVar* value, RewriterVar* slice);
RewriterVar* emitGetLocal(InternedString s);
RewriterVar* emitGetPystonIter(RewriterVar* v);
RewriterVar* emitHasnext(RewriterVar* v);
RewriterVar* emitLandingpad();
RewriterVar* emitNonzero(RewriterVar* v);
RewriterVar* emitNotNonzero(RewriterVar* v);
RewriterVar* emitRepr(RewriterVar* v);
RewriterVar* emitRuntimeCall(RewriterVar* obj, ArgPassSpec argspec, const llvm::ArrayRef<RewriterVar*> args,
std::vector<BoxedString*>* keyword_names);
RewriterVar* emitUnaryop(RewriterVar* v, int op_type);
RewriterVar* emitUnpackIntoArray(RewriterVar* v, uint64_t num);
RewriterVar* emitYield(RewriterVar* v);
void emitExec(RewriterVar* code, RewriterVar* globals, RewriterVar* locals, FutureFlags flags);
void emitJump(CFGBlock* b);
void emitOSRPoint(AST_Jump* node);
void emitPrint(RewriterVar* dest, RewriterVar* var, bool nl);
void emitRaise0();
void emitRaise3(RewriterVar* arg0, RewriterVar* arg1, RewriterVar* arg2);
void emitReturn(RewriterVar* v);
void emitSetAttr(RewriterVar* obj, BoxedString* s, RewriterVar* attr);
void emitSetBlockLocal(InternedString s, RewriterVar* v);
void emitSetCurrentInst(AST_stmt* node);
void emitSetExcInfo(RewriterVar* type, RewriterVar* value, RewriterVar* traceback);
void emitSetGlobal(Box* global, BoxedString* s, RewriterVar* v);
void emitSetItemName(BoxedString* s, RewriterVar* v);
void emitSetItem(RewriterVar* target, RewriterVar* slice, RewriterVar* value);
void emitSetLocal(InternedString s, bool set_closure, RewriterVar* v);
void emitSideExit(RewriterVar* v, Box* cmp_value, CFGBlock* next_block);
void emitUncacheExcInfo();
void abortCompilation();
int finishCompilation();
bool finishAssembly(int continue_offset) override;
private:
RewriterVar* allocArgs(const llvm::ArrayRef<RewriterVar*> args);
#ifndef NDEBUG
std::pair<uint64_t, uint64_t> asUInt(InternedString s);
#else
uint64_t asUInt(InternedString s);
#endif
RewriterVar* getInterp();
static Box* augbinopICHelper(AugBinopIC* ic, Box* lhs, Box* rhs, int op);
static Box* binopICHelper(BinopIC* ic, Box* lhs, Box* rhs, int op);
static Box* callattrHelper(Box* obj, BoxedString* attr, CallattrFlags flags, Box** args,
std::vector<BoxedString*>* keyword_names);
static Box* compareICHelper(CompareIC* ic, Box* lhs, Box* rhs, int op);
static Box* createDictHelper(uint64_t num, Box** keys, Box** values);
static Box* createListHelper(uint64_t num, Box** data);
static Box* createSetHelper(uint64_t num, Box** data);
static Box* createTupleHelper(uint64_t num, Box** data);
static Box* exceptionMatchesHelper(Box* obj, Box* cls);
static Box* getAttrICHelper(GetAttrIC* ic, Box* o, BoxedString* attr);
static Box* getGlobalICHelper(GetGlobalIC* ic, Box* o, BoxedString* s);
static Box* getitemICHelper(GetItemIC* ic, Box* o, Box* attr);
static Box* hasnextHelper(Box* b);
static Box* nonzeroHelper(Box* b);
static Box* notHelper(Box* b);
static Box* runtimeCallHelper(Box* obj, ArgPassSpec argspec, Box** args, std::vector<BoxedString*>* keyword_names);
static Box* setAttrICHelper(SetAttrIC* ic, Box* o, BoxedString* attr, Box* value);
static Box* setGlobalICHelper(SetGlobalIC* ic, Box* o, BoxedString* s, Box* v);
static Box* setitemICHelper(SetItemIC* ic, Box* o, Box* attr, Box* value);
static Box* unaryopICHelper(UnaryopIC* ic, Box* obj, int op);
#if ENABLE_BASELINEJIT_ICS
static Box* callattrHelperIC(Box* obj, BoxedString* attr, CallattrFlags flags, CallattrIC* ic, Box** args);
static Box* runtimeCallHelperIC(Box* obj, ArgPassSpec argspec, RuntimeCallIC* ic, Box** args);
#endif
void _emitJump(CFGBlock* b, RewriterVar* block_next, int& size_of_indirect_jump);
void _emitOSRPoint(RewriterVar* result, RewriterVar* node_var);
void _emitReturn(RewriterVar* v);
void _emitSideExit(RewriterVar* var, RewriterVar* val_constant, CFGBlock* next_block, RewriterVar* false_path);
};
}
#endif
......@@ -25,6 +25,7 @@
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/FileSystem.h"
#include "analysis/function_analysis.h"
#include "analysis/scoping_analysis.h"
#include "codegen/compvars.h"
#include "core/ast.h"
......@@ -62,6 +63,10 @@ SourceInfo::SourceInfo(BoxedModule* m, ScopingAnalysis* scoping, FutureFlags fut
}
}
SourceInfo::~SourceInfo() {
// TODO: release memory..
}
void FunctionAddressRegistry::registerFunction(const std::string& name, void* addr, int length,
llvm::Function* llvm_func) {
assert(addr);
......
......@@ -544,9 +544,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
emitter->getBuilder()->CreateStore(new_call_count, call_count_ptr);
int reopt_threshold;
if (effort == EffortLevel::MINIMAL)
reopt_threshold = REOPT_THRESHOLD_BASELINE;
else if (effort == EffortLevel::MODERATE)
if (effort == EffortLevel::MODERATE)
reopt_threshold = REOPT_THRESHOLD_T2;
else
RELEASE_ASSERT(0, "Unknown effort: %d", (int)effort);
......@@ -1059,15 +1057,15 @@ CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const O
computeBlockSetClosure(blocks);
}
std::unique_ptr<LivenessAnalysis> liveness = computeLivenessInfo(source->cfg);
LivenessAnalysis* liveness = source->getLiveness();
std::unique_ptr<PhiAnalysis> phis;
if (entry_descriptor)
phis = computeRequiredPhis(entry_descriptor, liveness.get(), source->getScopeInfo());
phis = computeRequiredPhis(entry_descriptor, liveness, source->getScopeInfo());
else
phis = computeRequiredPhis(*param_names, source->cfg, liveness.get(), source->getScopeInfo());
phis = computeRequiredPhis(*param_names, source->cfg, liveness, source->getScopeInfo());
IRGenState irstate(cf, source, std::move(liveness), std::move(phis), param_names, getGCBuilder(), dbg_funcinfo);
IRGenState irstate(cf, source, std::move(phis), param_names, getGCBuilder(), dbg_funcinfo);
emitBBs(&irstate, types, entry_descriptor, blocks);
......
......@@ -21,6 +21,7 @@
#include "analysis/scoping_analysis.h"
#include "asm_writing/icinfo.h"
#include "codegen/ast_interpreter.h"
#include "codegen/baseline_jit.h"
#include "codegen/codegen.h"
#include "codegen/compvars.h"
#include "codegen/irgen.h"
......@@ -120,6 +121,12 @@ ScopeInfo* SourceInfo::getScopeInfo() {
return scoping->getScopeInfoForNode(ast);
}
LivenessAnalysis* SourceInfo::getLiveness() {
if (!liveness_info)
liveness_info = computeLivenessInfo(cfg);
return liveness_info.get();
}
EffortLevel initialEffort() {
if (FORCE_INTERPRETER)
return EffortLevel::INTERPRETED;
......@@ -127,7 +134,7 @@ EffortLevel initialEffort() {
return EffortLevel::MAXIMAL;
if (ENABLE_INTERPRETER)
return EffortLevel::INTERPRETED;
return EffortLevel::MINIMAL;
return EffortLevel::MODERATE;
}
static void compileIR(CompiledFunction* cf, EffortLevel effort) {
......@@ -270,13 +277,6 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
num_compiles.log();
break;
}
case EffortLevel::MINIMAL: {
static StatCounter us_compiling("us_compiling_1_minimal");
us_compiling.log(us);
static StatCounter num_compiles("num_compiles_1_minimal");
num_compiles.log();
break;
}
case EffortLevel::MODERATE: {
static StatCounter us_compiling("us_compiling_2_moderate");
us_compiling.log(us);
......@@ -742,6 +742,21 @@ void CompiledFunction::speculationFailed() {
}
}
CompiledFunction::CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, bool is_interpreted, void* code,
EffortLevel effort, const OSREntryDescriptor* entry_descriptor)
: clfunc(NULL),
func(func),
spec(spec),
entry_descriptor(entry_descriptor),
is_interpreted(is_interpreted),
code(code),
effort(effort),
times_called(0),
times_speculation_failed(0),
location_map(nullptr) {
assert((spec != NULL) + (entry_descriptor != NULL) == 1);
}
ConcreteCompilerType* CompiledFunction::getReturnType() {
if (spec)
return spec->rtn_type;
......@@ -803,7 +818,7 @@ CompiledFunction* compilePartialFuncInternal(OSRExit* exit) {
assert(exit->parent_cf->clfunc);
CompiledFunction*& new_cf = exit->parent_cf->clfunc->osr_versions[exit->entry];
if (new_cf == NULL) {
EffortLevel new_effort = exit->parent_cf->effort == EffortLevel::INTERPRETED ? EffortLevel::MINIMAL
EffortLevel new_effort = exit->parent_cf->effort == EffortLevel::INTERPRETED ? EffortLevel::MODERATE
: EffortLevel::MAXIMAL;
CompiledFunction* compiled = compileFunction(exit->parent_cf->clfunc, NULL, new_effort, exit->entry);
assert(compiled == new_cf);
......@@ -830,8 +845,6 @@ extern "C" CompiledFunction* reoptCompiledFuncInternal(CompiledFunction* cf) {
EffortLevel new_effort;
if (cf->effort == EffortLevel::INTERPRETED)
new_effort = EffortLevel::MINIMAL;
else if (cf->effort == EffortLevel::MINIMAL)
new_effort = EffortLevel::MODERATE;
else if (cf->effort == EffortLevel::MODERATE)
new_effort = EffortLevel::MAXIMAL;
......
......@@ -42,12 +42,10 @@ extern "C" void dumpLLVM(llvm::Value* v) {
v->dump();
}
IRGenState::IRGenState(CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<LivenessAnalysis> liveness,
std::unique_ptr<PhiAnalysis> phis, ParamNames* param_names, GCBuilder* gc,
llvm::MDNode* func_dbg_info)
IRGenState::IRGenState(CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<PhiAnalysis> phis,
ParamNames* param_names, GCBuilder* gc, llvm::MDNode* func_dbg_info)
: cf(cf),
source_info(source_info),
liveness(std::move(liveness)),
phis(std::move(phis)),
param_names(param_names),
gc(gc),
......@@ -426,13 +424,22 @@ IREmitter* createIREmitter(IRGenState* irstate, llvm::BasicBlock*& curblock, IRG
}
static std::unordered_map<AST_expr*, std::vector<BoxedString*>*> made_keyword_storage;
static std::vector<BoxedString*>* getKeywordNameStorage(AST_Call* node) {
std::vector<BoxedString*>* getKeywordNameStorage(AST_Call* node) {
auto it = made_keyword_storage.find(node);
if (it != made_keyword_storage.end())
return it->second;
auto rtn = new std::vector<BoxedString*>();
made_keyword_storage.insert(it, std::make_pair(node, rtn));
// Only add the keywords to the array the first time, since
// the later times we will hit the cache which will have the
// keyword names already populated:
if (!rtn->size()) {
for (auto kw : node->keywords)
rtn->push_back(kw->arg.getBox());
}
return rtn;
}
......@@ -833,22 +840,10 @@ private:
}
std::vector<CompilerVariable*> args;
std::vector<BoxedString*>* keyword_names;
if (node->keywords.size()) {
std::vector<BoxedString*>* keyword_names = NULL;
if (node->keywords.size())
keyword_names = getKeywordNameStorage(node);
// Only add the keywords to the array the first time, since
// the later times we will hit the cache which will have the
// keyword names already populated:
if (!keyword_names->size()) {
for (auto kw : node->keywords) {
keyword_names->push_back(kw->arg.getBox());
}
}
} else {
keyword_names = NULL;
}
for (int i = 0; i < node->args.size(); i++) {
CompilerVariable* a = evalExpr(node->args[i], unw_info);
args.push_back(a);
......@@ -1887,6 +1882,7 @@ private:
static BoxedString* space_str = static_cast<BoxedString*>(PyString_InternFromString(" "));
// TODO: why are we inline-generating all this code instead of just emitting a call to some runtime function?
// (=printHelper())
int nvals = node->values.size();
for (int i = 0; i < nvals; i++) {
CompilerVariable* var = evalExpr(node->values[i], unw_info);
......@@ -2027,9 +2023,7 @@ private:
auto effort = irstate->getEffortLevel();
int osr_threshold;
if (effort == EffortLevel::MINIMAL)
osr_threshold = OSR_THRESHOLD_BASELINE;
else if (effort == EffortLevel::MODERATE)
if (effort == EffortLevel::MODERATE)
osr_threshold = OSR_THRESHOLD_T2;
else
RELEASE_ASSERT(0, "Unknown effort: %d", (int)effort);
......
......@@ -56,7 +56,6 @@ class IRGenState {
private:
CompiledFunction* cf;
SourceInfo* source_info;
std::unique_ptr<LivenessAnalysis> liveness;
std::unique_ptr<PhiAnalysis> phis;
ParamNames* param_names;
GCBuilder* gc;
......@@ -70,8 +69,8 @@ private:
public:
IRGenState(CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<LivenessAnalysis> liveness,
std::unique_ptr<PhiAnalysis> phis, ParamNames* param_names, GCBuilder* gc, llvm::MDNode* func_dbg_info);
IRGenState(CompiledFunction* cf, SourceInfo* source_info, std::unique_ptr<PhiAnalysis> phis,
ParamNames* param_names, GCBuilder* gc, llvm::MDNode* func_dbg_info);
~IRGenState();
CompiledFunction* getCurFunction() { return cf; }
......@@ -90,7 +89,7 @@ public:
SourceInfo* getSourceInfo() { return source_info; }
LivenessAnalysis* getLiveness() { return liveness.get(); }
LivenessAnalysis* getLiveness() { return source_info->getLiveness(); }
PhiAnalysis* getPhis() { return phis.get(); }
ScopeInfo* getScopeInfo();
......@@ -133,11 +132,13 @@ public:
};
class IREmitter;
class AST_Call;
IREmitter* createIREmitter(IRGenState* irstate, llvm::BasicBlock*& curblock, IRGenerator* irgenerator = NULL);
IRGenerator* createIRGenerator(IRGenState* irstate, std::unordered_map<CFGBlock*, llvm::BasicBlock*>& entry_blocks,
CFGBlock* myblock, TypeAnalysis* types);
CLFunction* wrapFunction(AST* node, AST_arguments* args, const std::vector<AST_stmt*>& body, SourceInfo* source);
std::vector<BoxedString*>* getKeywordNameStorage(AST_Call* node);
}
#endif
......@@ -36,6 +36,7 @@
namespace pyston {
class AST_stmt;
class Box;
class CFG;
class CFGBlock {
......@@ -43,6 +44,12 @@ private:
CFG* cfg;
public:
// Baseline JIT helper fields:
// contains address to the start of the code of this basic block
void* code;
// contains the address of the entry function
std::pair<CFGBlock*, Box*>(*entry_code)(void* interpeter, CFGBlock* block);
std::vector<AST_stmt*> body;
std::vector<CFGBlock*> predecessors, successors;
int idx; // index in the CFG
......@@ -50,7 +57,7 @@ public:
typedef std::vector<AST_stmt*>::iterator iterator;
CFGBlock(CFG* cfg, int idx) : cfg(cfg), idx(idx), info(NULL) {}
CFGBlock(CFG* cfg, int idx) : cfg(cfg), code(NULL), entry_code(NULL), idx(idx), info(NULL) {}
void connectTo(CFGBlock* successor, bool allow_backedge = false);
void unconnectFrom(CFGBlock* successor);
......
......@@ -39,15 +39,16 @@ bool DUMPJIT = false;
bool TRAP = false;
bool USE_STRIPPED_STDLIB = true; // always true
bool ENABLE_INTERPRETER = true;
bool ENABLE_BASELINEJIT = true;
bool ENABLE_PYPA_PARSER = true;
bool USE_REGALLOC_BASIC = true;
bool PAUSE_AT_ABORT = false;
bool ENABLE_TRACEBACKS = true;
int OSR_THRESHOLD_INTERPRETER = 500;
int REOPT_THRESHOLD_INTERPRETER = 200;
int OSR_THRESHOLD_BASELINE = 10000;
int REOPT_THRESHOLD_BASELINE = 250;
int OSR_THRESHOLD_INTERPRETER = 25;
int REOPT_THRESHOLD_INTERPRETER = 25;
int OSR_THRESHOLD_BASELINE = 2500;
int REOPT_THRESHOLD_BASELINE = 1000;
int OSR_THRESHOLD_T2 = 10000;
int REOPT_THRESHOLD_T2 = 10000;
int SPECULATION_THRESHOLD = 100;
......
......@@ -38,8 +38,8 @@ extern int SPECULATION_THRESHOLD;
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_PYPA_PARSER, USE_REGALLOC_BASIC, PAUSE_AT_ABORT, ENABLE_TRACEBACKS,
ASSEMBLY_LOGGING;
CONTINUE_AFTER_FATAL, ENABLE_INTERPRETER, ENABLE_BASELINEJIT, ENABLE_PYPA_PARSER, USE_REGALLOC_BASIC,
PAUSE_AT_ABORT, ENABLE_TRACEBACKS, ASSEMBLY_LOGGING;
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,
......
......@@ -65,7 +65,6 @@ using gc::GCVisitor;
enum class EffortLevel {
INTERPRETED = 0,
MINIMAL = 1,
MODERATE = 2,
MAXIMAL = 3,
};
......@@ -221,6 +220,7 @@ class BoxedClosure;
class BoxedGenerator;
class ICInfo;
class LocationMap;
class JitCodeBlock;
struct CompiledFunction {
private:
......@@ -249,21 +249,10 @@ public:
LocationMap* location_map; // only meaningful if this is a compiled frame
std::vector<ICInfo*> ics;
std::vector<std::unique_ptr<JitCodeBlock>> code_blocks;
CompiledFunction(llvm::Function* func, FunctionSpecialization* spec, bool is_interpreted, void* code,
EffortLevel effort, const OSREntryDescriptor* entry_descriptor)
: clfunc(NULL),
func(func),
spec(spec),
entry_descriptor(entry_descriptor),
is_interpreted(is_interpreted),
code(code),
effort(effort),
times_called(0),
times_speculation_failed(0),
location_map(nullptr) {
assert((spec != NULL) + (entry_descriptor != NULL) == 1);
}
EffortLevel effort, const OSREntryDescriptor* entry_descriptor);
ConcreteCompilerType* getReturnType();
......@@ -282,6 +271,7 @@ typedef int FutureFlags;
class BoxedModule;
class ScopeInfo;
class InternedStringPool;
class LivenessAnalysis;
class SourceInfo {
public:
BoxedModule* parent_module;
......@@ -295,6 +285,7 @@ public:
InternedStringPool& getInternedStrings();
ScopeInfo* getScopeInfo();
LivenessAnalysis* getLiveness();
// TODO we're currently copying the body of the AST into here, since lambdas don't really have a statement-based
// body and we have to create one. Ideally, we'd be able to avoid the space duplication for non-lambdas.
......@@ -307,6 +298,10 @@ public:
SourceInfo(BoxedModule* m, ScopingAnalysis* scoping, FutureFlags future_flags, AST* ast,
std::vector<AST_stmt*> body, std::string fn);
~SourceInfo();
private:
std::unique_ptr<LivenessAnalysis> liveness_info;
};
typedef std::vector<CompiledFunction*> FunctionList;
......
......@@ -226,6 +226,28 @@ extern "C" bool softspace(Box* b, bool newval) {
return r;
}
extern "C" void printHelper(Box* dest, Box* var, bool nl) {
static BoxedString* write_str = static_cast<BoxedString*>(PyString_InternFromString("write"));
static BoxedString* newline_str = static_cast<BoxedString*>(PyString_InternFromString("\n"));
static BoxedString* space_str = static_cast<BoxedString*>(PyString_InternFromString(" "));
if (var) {
// begin code for handling of softspace
bool new_softspace = !nl;
if (softspace(dest, new_softspace))
callattrInternal(dest, write_str, CLASS_OR_INST, 0, ArgPassSpec(1), space_str, 0, 0, 0, 0);
Box* str_or_unicode_var = (var->cls == unicode_cls) ? var : str(var);
callattrInternal(dest, write_str, CLASS_OR_INST, 0, ArgPassSpec(1), str_or_unicode_var, 0, 0, 0, 0);
}
if (nl) {
callattrInternal(dest, write_str, CLASS_OR_INST, 0, ArgPassSpec(1), newline_str, 0, 0, 0, 0);
if (!var)
softspace(dest, false);
}
}
extern "C" void my_assert(bool b) {
assert(b);
}
......
......@@ -48,6 +48,7 @@ BoxedModule* getCurrentModule();
// TODO sort this
extern "C" bool softspace(Box* b, bool newval);
extern "C" void printHelper(Box* dest, Box* var, bool nl);
extern "C" void my_assert(bool b);
extern "C" Box* getattr(Box* obj, BoxedString* attr);
extern "C" Box* getattrMaybeNonstring(Box* obj, Box* attr);
......
......@@ -578,6 +578,23 @@ public:
rtn->elts[2] = elt2;
return rtn;
}
static BoxedTuple* create4(Box* elt0, Box* elt1, Box* elt2, Box* elt3) {
BoxedTuple* rtn = new (4) BoxedTuple(4);
rtn->elts[0] = elt0;
rtn->elts[1] = elt1;
rtn->elts[2] = elt2;
rtn->elts[3] = elt3;
return rtn;
}
static BoxedTuple* create5(Box* elt0, Box* elt1, Box* elt2, Box* elt3, Box* elt4) {
BoxedTuple* rtn = new (5) BoxedTuple(5);
rtn->elts[0] = elt0;
rtn->elts[1] = elt1;
rtn->elts[2] = elt2;
rtn->elts[3] = elt3;
rtn->elts[4] = elt4;
return rtn;
}
static BoxedTuple* create(std::initializer_list<Box*> members) { return new (members.size()) BoxedTuple(members); }
static BoxedTuple* create(int64_t size, BoxedClass* cls) {
......
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