Commit 218d8cd5 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge pull request #228 from kmod/interp2

New AST interpreter
parents bb3a19e1 062009c7
......@@ -24,3 +24,14 @@ the JIT'd code.
There's a gprof-based profile, but that doesn't support any JIT'd code. It can be quite handy for profiling the Pyston
codegen + LLVM cost.
We also have a few internal timers that log important sections of code. You can see their totals by passing '-s' to
the executable (ARGS=-s to a Make recipe).
Some common ones are:
- us_compiling: total amount of time (in microseconds) spent in 'codegen', from
the time we decide we want to run some Python code to the point that we can
start executing it
- us_compiling_irgen: total time creating the LLVM IR. includes some analysis time
- us_compiling_jitting: subset of us_compiling which is spent in LLVM
compilation (time from giving LLVM its IR to when we get machine code back)
......@@ -459,7 +459,13 @@ bool PhiAnalysis::isPotentiallyUndefinedAfter(const std::string& name, CFGBlock*
if (block->successors.size() != 1)
return false;
for (CFGBlock* pred : block->successors[0]->predecessors) {
return isPotentiallyUndefinedAt(name, block->successors[0]);
}
bool PhiAnalysis::isPotentiallyUndefinedAt(const std::string& name, CFGBlock* block) {
assert(!startswith(name, "!"));
for (CFGBlock* pred : block->predecessors) {
DefinednessAnalysis::DefinitionLevel dlevel = definedness.isDefinedAtEnd(name, pred);
if (dlevel != DefinednessAnalysis::Defined)
return true;
......
......@@ -84,8 +84,9 @@ class PhiAnalysis {
public:
typedef std::unordered_set<std::string> RequiredSet;
private:
DefinednessAnalysis definedness;
private:
LivenessAnalysis* liveness;
std::unordered_map<CFGBlock*, const RequiredSet> required_phis;
......@@ -97,6 +98,7 @@ public:
const RequiredSet& getAllRequiredAfter(CFGBlock* block);
const RequiredSet& getAllRequiredFor(CFGBlock* block);
bool isPotentiallyUndefinedAfter(const std::string& name, CFGBlock* block);
bool isPotentiallyUndefinedAt(const std::string& name, CFGBlock* block);
};
LivenessAnalysis* computeLivenessInfo(CFG*);
......
......@@ -70,8 +70,8 @@ public:
// assert(name[0] != '#' && "should test this");
return true;
}
bool refersToClosure(const std::string name) override { return false; }
bool saveInClosure(const std::string name) override { return false; }
bool refersToClosure(const std::string& name) override { return false; }
bool saveInClosure(const std::string& name) override { return false; }
const std::unordered_set<std::string>& getClassDefLocalNames() override { RELEASE_ASSERT(0, ""); }
};
......@@ -141,13 +141,13 @@ public:
return true;
return usage->written.count(name) == 0 && usage->got_from_closure.count(name) == 0;
}
bool refersToClosure(const std::string name) override {
bool refersToClosure(const std::string& name) override {
// HAX
if (isCompilerCreatedName(name))
return false;
return usage->got_from_closure.count(name) != 0;
}
bool saveInClosure(const std::string name) override {
bool saveInClosure(const std::string& name) override {
// HAX
if (isCompilerCreatedName(name))
return false;
......
......@@ -36,8 +36,8 @@ public:
virtual void setTakesGenerator(bool b = true) { isGeneratorValue = b; }
virtual bool refersToGlobal(const std::string& name) = 0;
virtual bool refersToClosure(const std::string name) = 0;
virtual bool saveInClosure(const std::string name) = 0;
virtual bool refersToClosure(const std::string& name) = 0;
virtual bool saveInClosure(const std::string& name) = 0;
// Get the names set within a classdef that should be forwarded on to
// the metaclass constructor.
......
// Copyright (c) 2014 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/ast_interpreter.h"
#include <llvm/ADT/StringMap.h>
#include <unordered_map>
#include "analysis/function_analysis.h"
#include "analysis/scoping_analysis.h"
#include "codegen/codegen.h"
#include "codegen/compvars.h"
#include "codegen/irgen.h"
#include "codegen/irgen/hooks.h"
#include "codegen/irgen/irgenerator.h"
#include "codegen/irgen/util.h"
#include "codegen/osrentry.h"
#include "core/ast.h"
#include "core/cfg.h"
#include "core/common.h"
#include "core/stats.h"
#include "core/thread_utils.h"
#include "core/util.h"
#include "runtime/generator.h"
#include "runtime/import.h"
#include "runtime/inline/boxing.h"
#include "runtime/long.h"
#include "runtime/objmodel.h"
#include "runtime/set.h"
#include "runtime/types.h"
namespace pyston {
#define OSR_THRESHOLD 100
#define REOPT_THRESHOLD 100
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) {}
};
class ASTInterpreter {
public:
typedef llvm::StringMap<Box*> SymMap;
ASTInterpreter(CompiledFunction* compiled_function, void* frame_addr);
~ASTInterpreter();
void initArguments(int nargs, BoxedClosure* closure, BoxedGenerator* generator, Box* arg1, Box* arg2, Box* arg3,
Box** args);
Value execute(CFGBlock* block = 0);
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);
void doStore(AST_expr* node, Value value);
void doStore(const std::string& name, Value value);
void eraseDeadSymbols();
Value visit_assert(AST_Assert* node);
Value visit_assign(AST_Assign* node);
Value visit_binop(AST_BinOp* node);
Value visit_call(AST_Call* node);
Value visit_classDef(AST_ClassDef* node);
Value visit_compare(AST_Compare* node);
Value visit_delete(AST_Delete* node);
Value visit_functionDef(AST_FunctionDef* node);
Value visit_global(AST_Global* node);
Value visit_module(AST_Module* node);
Value visit_print(AST_Print* node);
Value visit_raise(AST_Raise* node);
Value visit_return(AST_Return* node);
Value visit_stmt(AST_stmt* node);
Value visit_unaryop(AST_UnaryOp* node);
Value visit_attribute(AST_Attribute* node);
Value visit_dict(AST_Dict* node);
Value visit_expr(AST_expr* node);
Value visit_expr(AST_Expr* node);
Value visit_index(AST_Index* node);
Value visit_lambda(AST_Lambda* node);
Value visit_list(AST_List* node);
Value visit_name(AST_Name* node);
Value visit_num(AST_Num* node);
Value visit_repr(AST_Repr* node);
Value visit_set(AST_Set* node);
Value visit_slice(AST_Slice* node);
Value visit_str(AST_Str* node);
Value visit_subscript(AST_Subscript* node);
Value visit_tuple(AST_Tuple* node);
Value visit_yield(AST_Yield* node);
// pseudo
Value visit_augBinOp(AST_AugBinOp* node);
Value visit_branch(AST_Branch* node);
Value visit_clsAttribute(AST_ClsAttribute* node);
Value visit_invoke(AST_Invoke* node);
Value visit_jump(AST_Jump* node);
Value visit_langPrimitive(AST_LangPrimitive* node);
void* frame_addr;
CompiledFunction* compiled_func;
SourceInfo* source_info;
ScopeInfo* scope_info;
SymMap sym_table;
CFGBlock* next_block, *current_block;
AST* current_inst;
Box* last_exception;
BoxedClosure* passed_closure, *created_closure;
BoxedGenerator* generator;
unsigned edgecount;
public:
LineInfo* getCurrentLineInfo();
BoxedModule* getParentModule() { return source_info->parent_module; }
const SymMap& getSymbolTable() { return sym_table; }
};
static_assert(THREADING_USE_GIL, "have to make the interpreter map thread safe!");
static std::unordered_map<void*, ASTInterpreter*> s_interpreterMap;
Box* astInterpretFunction(CompiledFunction* cf, int nargs, Box* closure, Box* generator, Box* arg1, Box* arg2,
Box* arg3, Box** args) {
if (unlikely(cf->times_called > REOPT_THRESHOLD)) {
CompiledFunction* optimized = reoptCompiledFuncInternal(cf);
if (closure && generator)
return optimized->closure_generator_call((BoxedClosure*)closure, (BoxedGenerator*)generator, arg1, arg2,
arg3, args);
else if (closure)
return optimized->closure_call((BoxedClosure*)closure, arg1, arg2, arg3, args);
else if (generator)
return optimized->generator_call((BoxedGenerator*)generator, arg1, arg2, arg3, args);
return optimized->call(arg1, arg2, arg3, args);
}
++cf->times_called;
void* frame_addr = __builtin_frame_address(0);
ASTInterpreter interpreter(cf, frame_addr);
interpreter.initArguments(nargs, (BoxedClosure*)closure, (BoxedGenerator*)generator, arg1, arg2, arg3, args);
Value v = interpreter.execute();
return v.o ? v.o : None;
}
const LineInfo* getLineInfoForInterpretedFrame(void* frame_ptr) {
ASTInterpreter* interpreter = s_interpreterMap[frame_ptr];
assert(interpreter);
return interpreter->getCurrentLineInfo();
}
LineInfo* ASTInterpreter::getCurrentLineInfo() {
LineInfo* line_info = new LineInfo(current_inst->lineno, current_inst->col_offset, source_info->parent_module->fn,
source_info->getName());
return line_info;
}
BoxedModule* getModuleForInterpretedFrame(void* frame_ptr) {
ASTInterpreter* interpreter = s_interpreterMap[frame_ptr];
assert(interpreter);
return interpreter->getParentModule();
}
BoxedDict* localsForInterpretedFrame(void* frame_ptr, bool only_user_visible) {
ASTInterpreter* interpreter = s_interpreterMap[frame_ptr];
assert(interpreter);
BoxedDict* rtn = new BoxedDict();
for (auto&& l : interpreter->getSymbolTable()) {
if (only_user_visible && (l.getKey()[0] == '!' || l.getKey()[0] == '#'))
continue;
rtn->d[new BoxedString(l.getKey())] = l.getValue();
}
return rtn;
}
void gatherInterpreterRoots(GCVisitor* visitor) {
for (const auto& p : s_interpreterMap) {
for (const auto& p2 : p.second->getSymbolTable()) {
visitor->visitPotential(p2.second);
}
}
}
ASTInterpreter::ASTInterpreter(CompiledFunction* compiled_function, void* frame_addr)
: frame_addr(frame_addr), compiled_func(compiled_function), source_info(compiled_function->clfunc->source),
scope_info(0), next_block(0), current_block(0), current_inst(0), last_exception(0), passed_closure(0),
created_closure(0), generator(0), edgecount(0) {
s_interpreterMap[frame_addr] = this;
CLFunction* f = compiled_function->clfunc;
if (!source_info->cfg)
source_info->cfg = computeCFG(f->source, f->source->body);
scope_info = source_info->getScopeInfo();
}
ASTInterpreter::~ASTInterpreter() {
assert(s_interpreterMap[frame_addr] == this);
s_interpreterMap.erase(frame_addr);
}
void ASTInterpreter::initArguments(int nargs, BoxedClosure* _closure, BoxedGenerator* _generator, Box* arg1, Box* arg2,
Box* arg3, Box** args) {
passed_closure = _closure;
generator = _generator;
if (scope_info->createsClosure())
created_closure = createClosure(passed_closure);
std::vector<Box*> argsArray{ arg1, arg2, arg3 };
for (int i = 3; i < nargs; ++i)
argsArray.push_back(args[i - 3]);
int i = 0;
if (source_info->arg_names.args) {
for (AST_expr* e : *source_info->arg_names.args) {
RELEASE_ASSERT(e->type == AST_TYPE::Name, "not implemented");
AST_Name* name = (AST_Name*)e;
doStore(name->id, argsArray[i++]);
}
}
if (source_info->arg_names.vararg && !source_info->arg_names.vararg->empty()) {
doStore(*source_info->arg_names.vararg, argsArray[i++]);
}
if (source_info->arg_names.kwarg && !source_info->arg_names.kwarg->empty()) {
doStore(*source_info->arg_names.kwarg, argsArray[i++]);
}
}
Value ASTInterpreter::execute(CFGBlock* block) {
if (!block)
block = source_info->cfg->getStartingBlock();
Value v;
next_block = block;
while (next_block) {
current_block = next_block;
next_block = 0;
for (AST_stmt* s : current_block->body) {
current_inst = s;
v = visit_stmt(s);
}
}
return v;
}
void ASTInterpreter::eraseDeadSymbols() {
if (source_info->liveness == NULL)
source_info->liveness = computeLivenessInfo(source_info->cfg);
if (source_info->phis == NULL)
source_info->phis
= computeRequiredPhis(source_info->arg_names, source_info->cfg, source_info->liveness, scope_info);
std::vector<std::string> dead_symbols;
for (auto&& it : sym_table) {
if (!source_info->liveness->isLiveAtEnd(it.getKey(), current_block)) {
dead_symbols.push_back(it.getKey());
} else if (source_info->phis->isRequiredAfter(it.getKey(), current_block)) {
assert(!scope_info->refersToGlobal(it.getKey()));
} else {
}
}
for (auto&& dead : dead_symbols)
sym_table.erase(dead);
}
Value ASTInterpreter::doBinOp(Box* left, Box* right, int op, BinExpType exp_type) {
if (op == AST_TYPE::Div && (source_info->parent_module->future_flags & FF_DIVISION)) {
op = AST_TYPE::TrueDiv;
}
switch (exp_type) {
case BinExpType::AugBinOp:
return augbinop(left, right, op);
case BinExpType::BinOp:
return binop(left, right, op);
case BinExpType::Compare:
return compare(left, right, op);
default:
RELEASE_ASSERT(0, "not implemented");
}
return Value();
}
void ASTInterpreter::doStore(const std::string& name, Value value) {
if (scope_info->refersToGlobal(name)) {
setattr(source_info->parent_module, name.c_str(), value.o);
} else {
sym_table[name] = value.o;
if (scope_info->saveInClosure(name))
setattr(created_closure, name.c_str(), value.o);
}
}
void ASTInterpreter::doStore(AST_expr* node, Value value) {
if (node->type == AST_TYPE::Name) {
AST_Name* name = (AST_Name*)node;
doStore(name->id, value);
} else if (node->type == AST_TYPE::Attribute) {
AST_Attribute* attr = (AST_Attribute*)node;
setattr(visit_expr(attr->value).o, attr->attr.c_str(), value.o);
} else if (node->type == AST_TYPE::Tuple) {
AST_Tuple* tuple = (AST_Tuple*)node;
Box** array = unpackIntoArray(value.o, tuple->elts.size());
unsigned i = 0;
for (AST_expr* e : tuple->elts)
doStore(e, array[i++]);
} else if (node->type == AST_TYPE::List) {
AST_List* list = (AST_List*)node;
Box** array = unpackIntoArray(value.o, list->elts.size());
unsigned i = 0;
for (AST_expr* e : list->elts)
doStore(e, array[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);
setitem(target.o, slice.o, value.o);
} else {
RELEASE_ASSERT(0, "not implemented");
}
}
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));
else
return unaryop(operand.o, node->op_type);
}
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);
}
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 ASTInterpreter::visit_branch(AST_Branch* node) {
if (nonzero(visit_expr(node->test).o))
next_block = node->iftrue;
else
next_block = node->iffalse;
return Value();
}
Value ASTInterpreter::visit_jump(AST_Jump* node) {
if (ENABLE_OSR && node->target->idx < current_block->idx && compiled_func) {
++edgecount;
if (edgecount > OSR_THRESHOLD) {
eraseDeadSymbols();
OSRExit exit(compiled_func, OSREntryDescriptor::create(compiled_func, node));
std::map<std::string, Box*> sorted_symbol_table;
auto phis = compiled_func->clfunc->source->phis;
for (auto& name : phis->definedness.getDefinedNamesAtEnd(current_block)) {
auto it = sym_table.find(name);
if (!compiled_func->clfunc->source->liveness->isLiveAtEnd(name, current_block))
continue;
if (phis->isPotentiallyUndefinedAfter(name, current_block)) {
bool is_defined = it != sym_table.end();
sorted_symbol_table[getIsDefinedName(name)] = (Box*)is_defined;
sorted_symbol_table[name] = is_defined ? it->getValue() : NULL;
} else {
ASSERT(it != sym_table.end(), "%s", name.c_str());
sorted_symbol_table[it->getKey()] = it->getValue();
}
}
if (generator)
sorted_symbol_table[PASSED_GENERATOR_NAME] = generator;
if (passed_closure)
sorted_symbol_table[PASSED_CLOSURE_NAME] = passed_closure;
if (created_closure)
sorted_symbol_table[CREATED_CLOSURE_NAME] = created_closure;
std::vector<Box*> arg_array;
for (auto& it : sorted_symbol_table) {
if (isIsDefinedName(it.first))
exit.entry->args[it.first] = BOOL;
else if (it.first == PASSED_GENERATOR_NAME)
exit.entry->args[it.first] = GENERATOR;
else if (it.first == PASSED_CLOSURE_NAME || it.first == CREATED_CLOSURE_NAME)
exit.entry->args[it.first] = CLOSURE;
else {
assert(it.first[0] != '!');
exit.entry->args[it.first] = UNKNOWN;
}
arg_array.push_back(it.second);
}
CompiledFunction* partial_func = compilePartialFuncInternal(&exit);
Box* arg1 = arg_array.size() >= 1 ? arg_array[0] : 0;
Box* arg2 = arg_array.size() >= 2 ? arg_array[1] : 0;
Box* arg3 = arg_array.size() >= 3 ? arg_array[2] : 0;
Box** args = arg_array.size() >= 4 ? &arg_array[3] : 0;
return partial_func->call(arg1, arg2, arg3, args);
}
}
next_block = node->target;
return Value();
}
Value ASTInterpreter::visit_invoke(AST_Invoke* node) {
Value v;
try {
v = visit_stmt(node->stmt);
next_block = node->normal_dest;
} catch (Box* b) {
next_block = node->exc_dest;
last_exception = b;
}
return v;
}
Value ASTInterpreter::visit_clsAttribute(AST_ClsAttribute* node) {
return getattr(visit_expr(node->value).o, node->attr.c_str());
}
Value ASTInterpreter::visit_augBinOp(AST_AugBinOp* node) {
assert(node->op_type != AST_TYPE::Is && node->op_type != AST_TYPE::IsNot && "not tested yet");
Value left = visit_expr(node->left);
Value right = visit_expr(node->right);
return doBinOp(left.o, right.o, 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 = getiter(visit_expr(node->args[0]).o);
} else if (node->opcode == AST_LangPrimitive::IMPORT_FROM) {
assert(node->args.size() == 2);
assert(node->args[0]->type == AST_TYPE::Name);
assert(node->args[1]->type == AST_TYPE::Str);
Value module = visit_expr(node->args[0]);
const std::string& name = ast_cast<AST_Str>(node->args[1])->s;
assert(name.size());
v = importFrom(module.o, &name);
} else if (node->opcode == AST_LangPrimitive::IMPORT_NAME) {
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);
assert(node->args[2]->type == AST_TYPE::Str);
int level = static_cast<AST_Num*>(node->args[0])->n_int;
Value froms = visit_expr(node->args[1]);
const std::string& module_name = static_cast<AST_Str*>(node->args[2])->s;
v = import(level, froms.o, &module_name);
} else if (node->opcode == AST_LangPrimitive::IMPORT_STAR) {
assert(node->args.size() == 1);
assert(node->args[0]->type == AST_TYPE::Name);
RELEASE_ASSERT(source_info->ast->type == AST_TYPE::Module, "import * not supported in functions");
Value module = visit_expr(node->args[0]);
v = importStar(module.o, source_info->parent_module);
} else if (node->opcode == AST_LangPrimitive::NONE) {
v = None;
} else if (node->opcode == AST_LangPrimitive::LANDINGPAD) {
v = last_exception;
last_exception = nullptr;
} else if (node->opcode == AST_LangPrimitive::ISINSTANCE) {
assert(node->args.size() == 3);
Value obj = visit_expr(node->args[0]);
Value cls = visit_expr(node->args[1]);
Value flags = visit_expr(node->args[2]);
v = boxBool(isinstance(obj.o, cls.o, unboxInt(flags.o)));
} else if (node->opcode == AST_LangPrimitive::LOCALS) {
BoxedDict* dict = new BoxedDict;
for (auto& p : sym_table) {
llvm::StringRef s = p.first();
if (s[0] == '!' || s[0] == '#')
continue;
dict->d[new BoxedString(s.str())] = p.second;
}
v = dict;
} else
RELEASE_ASSERT(0, "not implemented");
return v;
}
Value ASTInterpreter::visit_yield(AST_Yield* node) {
Value value = node->value ? visit_expr(node->value) : None;
assert(generator && generator->cls == generator_cls);
return yield(generator, value.o);
}
Value __attribute__((flatten)) ASTInterpreter::visit_stmt(AST_stmt* node) {
switch (node->type) {
case AST_TYPE::Assert:
return visit_assert((AST_Assert*)node);
case AST_TYPE::Assign:
return visit_assign((AST_Assign*)node);
case AST_TYPE::ClassDef:
return visit_classDef((AST_ClassDef*)node);
case AST_TYPE::Delete:
return visit_delete((AST_Delete*)node);
case AST_TYPE::Expr:
return visit_expr((AST_Expr*)node);
case AST_TYPE::FunctionDef:
return visit_functionDef((AST_FunctionDef*)node);
case AST_TYPE::Pass:
return Value(); // nothing todo
case AST_TYPE::Print:
return visit_print((AST_Print*)node);
case AST_TYPE::Raise:
return visit_raise((AST_Raise*)node);
case AST_TYPE::Return:
return visit_return((AST_Return*)node);
case AST_TYPE::Global:
return visit_global((AST_Global*)node);
// pseudo
case AST_TYPE::Branch:
return visit_branch((AST_Branch*)node);
case AST_TYPE::Jump:
return visit_jump((AST_Jump*)node);
case AST_TYPE::Invoke:
return visit_invoke((AST_Invoke*)node);
default:
RELEASE_ASSERT(0, "not implemented");
};
return Value();
}
Value ASTInterpreter::visit_return(AST_Return* node) {
Value s(node->value ? visit_expr(node->value) : None);
next_block = 0;
return s;
}
Box* ASTInterpreter::createFunction(AST* node, AST_arguments* args, const std::vector<AST_stmt*>& body) {
CLFunction* cl = wrapFunction(node, args, body, source_info);
std::vector<Box*> defaults;
for (AST_expr* d : args->defaults)
defaults.push_back(visit_expr(d).o);
defaults.push_back(0);
// FIXME: Using initializer_list is pretty annoying since you're not supposed to create them:
union {
struct {
Box** ptr;
size_t s;
} d;
std::initializer_list<Box*> il = {};
} u;
u.d.ptr = &defaults[0];
u.d.s = defaults.size() - 1;
ScopeInfo* scope_info_node = source_info->scoping->getScopeInfoForNode(node);
bool is_generator = scope_info_node->takesGenerator();
BoxedClosure* closure = 0;
if (scope_info_node->takesClosure()) {
if (scope_info->createsClosure()) {
closure = created_closure;
} else {
assert(scope_info->passesThroughClosure());
closure = passed_closure;
}
assert(closure);
}
return boxCLFunction(cl, closure, is_generator, u.il);
}
Value ASTInterpreter::visit_functionDef(AST_FunctionDef* node) {
AST_arguments* args = node->args;
std::vector<Box*> decorators;
for (AST_expr* d : node->decorator_list)
decorators.push_back(visit_expr(d).o);
Box* func = createFunction(node, args, node->body);
for (int i = decorators.size() - 1; i >= 0; i--)
func = runtimeCall(decorators[i], ArgPassSpec(1), func, 0, 0, 0, 0);
doStore(node->name, func);
return Value();
}
Value ASTInterpreter::visit_classDef(AST_ClassDef* node) {
ScopeInfo* scope_info = source_info->scoping->getScopeInfoForNode(node);
assert(scope_info);
BoxedTuple::GCVector bases;
for (AST_expr* b : node->bases)
bases.push_back(visit_expr(b).o);
BoxedTuple* basesTuple = new BoxedTuple(std::move(bases));
std::vector<Box*> decorators;
for (AST_expr* d : node->decorator_list)
decorators.push_back(visit_expr(d).o);
BoxedClosure* closure = scope_info->takesClosure() ? created_closure : 0;
CLFunction* cl = wrapFunction(node, nullptr, node->body, source_info);
Box* attrDict = runtimeCall(boxCLFunction(cl, closure, false, {}), ArgPassSpec(0), 0, 0, 0, 0, 0);
Box* classobj = createUserClass(&node->name, basesTuple, attrDict);
for (int i = decorators.size() - 1; i >= 0; i--)
classobj = runtimeCall(decorators[i], ArgPassSpec(1), classobj, 0, 0, 0, 0);
doStore(node->name, classobj);
return Value();
}
Value ASTInterpreter::visit_raise(AST_Raise* node) {
if (node->arg0 == NULL) {
assert(!node->arg1);
assert(!node->arg2);
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);
return Value();
}
Value ASTInterpreter::visit_assert(AST_Assert* node) {
if (!nonzero(visit_expr(node->test).o))
assertFail(source_info->parent_module, node->msg ? visit_expr(node->msg).o : 0);
return Value();
}
Value ASTInterpreter::visit_global(AST_Global* node) {
for (std::string& name : node->names)
sym_table.erase(name);
return Value();
}
Value ASTInterpreter::visit_delete(AST_Delete* node) {
for (AST_expr* target_ : node->targets) {
switch (target_->type) {
case AST_TYPE::Subscript: {
AST_Subscript* sub = (AST_Subscript*)target_;
Value value = visit_expr(sub->value);
Value slice = visit_expr(sub->slice);
delitem(value.o, slice.o);
break;
}
case AST_TYPE::Attribute: {
AST_Attribute* attr = (AST_Attribute*)target_;
delattr(visit_expr(attr->value).o, attr->attr.c_str());
break;
}
case AST_TYPE::Name: {
AST_Name* target = (AST_Name*)target_;
if (scope_info->refersToGlobal(target->id)) {
// Can't use delattr since the errors are different:
delGlobal(source_info->parent_module, &target->id);
continue;
}
assert(!scope_info->refersToClosure(target->id));
assert(!scope_info->saveInClosure(
target->id)); // SyntaxError: can not delete variable 'x' referenced in nested scope
// A del of a missing name generates different error messages in a function scope vs a classdef scope
bool local_error_msg = (source_info->ast->type != AST_TYPE::ClassDef);
if (sym_table.count(target->id) == 0) {
assertNameDefined(0, target->id.c_str(), NameError, local_error_msg);
return Value();
}
sym_table.erase(target->id);
break;
}
default:
ASSERT(0, "Unsupported del target: %d", target_->type);
abort();
}
}
return Value();
}
Value ASTInterpreter::visit_assign(AST_Assign* node) {
Value v = visit_expr(node->value);
for (AST_expr* e : node->targets)
doStore(e, v);
return Value();
}
Value ASTInterpreter::visit_print(AST_Print* node) {
static const std::string write_str("write");
static const std::string newline_str("\n");
static const std::string space_str(" ");
Box* dest = node->dest ? visit_expr(node->dest).o : getSysStdout();
int nvals = node->values.size();
for (int i = 0; i < nvals; i++) {
Box* var = visit_expr(node->values[i]).o;
// 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), boxString(space_str), 0, 0, 0, 0);
}
callattrInternal(dest, &write_str, CLASS_OR_INST, 0, ArgPassSpec(1), str(var), 0, 0, 0, 0);
}
if (node->nl) {
callattrInternal(dest, &write_str, CLASS_OR_INST, 0, ArgPassSpec(1), boxString(newline_str), 0, 0, 0, 0);
if (nvals == 0) {
softspace(dest, false);
}
}
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 __attribute__((flatten)) ASTInterpreter::visit_expr(AST_expr* node) {
switch (node->type) {
case AST_TYPE::Attribute:
return visit_attribute((AST_Attribute*)node);
case AST_TYPE::BinOp:
return visit_binop((AST_BinOp*)node);
case AST_TYPE::Call:
return visit_call((AST_Call*)node);
case AST_TYPE::Compare:
return visit_compare((AST_Compare*)node);
case AST_TYPE::Dict:
return visit_dict((AST_Dict*)node);
case AST_TYPE::Index:
return visit_index((AST_Index*)node);
case AST_TYPE::Lambda:
return visit_lambda((AST_Lambda*)node);
case AST_TYPE::List:
return visit_list((AST_List*)node);
case AST_TYPE::Name:
return visit_name((AST_Name*)node);
case AST_TYPE::Num:
return visit_num((AST_Num*)node);
case AST_TYPE::Repr:
return visit_repr((AST_Repr*)node);
case AST_TYPE::Set:
return visit_set((AST_Set*)node);
case AST_TYPE::Slice:
return visit_slice((AST_Slice*)node);
case AST_TYPE::Str:
return visit_str((AST_Str*)node);
case AST_TYPE::Subscript:
return visit_subscript((AST_Subscript*)node);
case AST_TYPE::Tuple:
return visit_tuple((AST_Tuple*)node);
case AST_TYPE::UnaryOp:
return visit_unaryop((AST_UnaryOp*)node);
case AST_TYPE::Yield:
return visit_yield((AST_Yield*)node);
// pseudo
case AST_TYPE::AugBinOp:
return visit_augBinOp((AST_AugBinOp*)node);
case AST_TYPE::ClsAttribute:
return visit_clsAttribute((AST_ClsAttribute*)node);
case AST_TYPE::LangPrimitive:
return visit_langPrimitive((AST_LangPrimitive*)node);
default:
RELEASE_ASSERT(0, "");
};
return Value();
}
Value ASTInterpreter::visit_call(AST_Call* node) {
Value v;
Value func;
std::string* attr = nullptr;
bool is_callattr = false;
bool callattr_clsonly = false;
if (node->func->type == AST_TYPE::Attribute) {
is_callattr = true;
callattr_clsonly = false;
AST_Attribute* attr_ast = ast_cast<AST_Attribute>(node->func);
func = visit_expr(attr_ast->value);
attr = &attr_ast->attr;
} else if (node->func->type == AST_TYPE::ClsAttribute) {
is_callattr = true;
callattr_clsonly = true;
AST_ClsAttribute* attr_ast = ast_cast<AST_ClsAttribute>(node->func);
func = visit_expr(attr_ast->value);
attr = &attr_ast->attr;
} else
func = visit_expr(node->func);
std::vector<Box*> args;
for (AST_expr* e : node->args)
args.push_back(visit_expr(e).o);
std::vector<const std::string*> keywords;
for (AST_keyword* k : node->keywords) {
keywords.push_back(&k->arg);
args.push_back(visit_expr(k->value).o);
}
if (node->starargs)
args.push_back(visit_expr(node->starargs).o);
if (node->kwargs)
args.push_back(visit_expr(node->kwargs).o);
ArgPassSpec argspec(node->args.size(), node->keywords.size(), node->starargs, node->kwargs);
if (is_callattr) {
return callattr(func.o, attr, CallattrFlags({.cls_only = callattr_clsonly, .null_on_nonexistent = false }),
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);
} 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 ASTInterpreter::visit_expr(AST_Expr* node) {
return visit_expr(node->value);
}
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);
RELEASE_ASSERT(0, "not implemented");
return Value();
}
Value ASTInterpreter::visit_index(AST_Index* node) {
return visit_expr(node->value);
}
Value ASTInterpreter::visit_repr(AST_Repr* node) {
return repr(visit_expr(node->value).o);
}
Value ASTInterpreter::visit_lambda(AST_Lambda* node) {
AST_Return* expr = new AST_Return();
expr->value = node->body;
std::vector<AST_stmt*> body = { expr };
return createFunction(node, node->args, body);
}
Value ASTInterpreter::visit_dict(AST_Dict* node) {
RELEASE_ASSERT(node->keys.size() == node->values.size(), "not implemented");
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;
}
return dict;
}
Value ASTInterpreter::visit_set(AST_Set* node) {
BoxedSet::Set set;
for (AST_expr* e : node->elts)
set.insert(visit_expr(e).o);
return new BoxedSet(std::move(set), set_cls);
}
Value ASTInterpreter::visit_str(AST_Str* node) {
return boxString(node->s);
}
Value ASTInterpreter::visit_name(AST_Name* node) {
if (scope_info->refersToGlobal(node->id))
return getGlobal(source_info->parent_module, &node->id);
else if (scope_info->refersToClosure(node->id)) {
return getattr(passed_closure, node->id.c_str());
} else {
SymMap::iterator it = sym_table.find(node->id);
if (it != sym_table.end()) {
Box* value = it->second;
if (!value)
assertNameDefined(value, node->id.c_str(), UnboundLocalError, true);
return value;
}
// classdefs have different scoping rules than functions:
if (source_info->ast->type == AST_TYPE::ClassDef)
return getGlobal(source_info->parent_module, &node->id);
assertNameDefined(0, node->id.c_str(), UnboundLocalError, true);
return Value();
}
}
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);
}
Value ASTInterpreter::visit_list(AST_List* node) {
BoxedList* list = new BoxedList;
list->ensure(node->elts.size());
for (AST_expr* e : node->elts)
listAppendInternal(list, visit_expr(e).o);
return list;
}
Value ASTInterpreter::visit_tuple(AST_Tuple* node) {
BoxedTuple::GCVector elts;
for (AST_expr* e : node->elts)
elts.push_back(visit_expr(e).o);
return new BoxedTuple(std::move(elts));
}
Value ASTInterpreter::visit_attribute(AST_Attribute* node) {
return getattr(visit_expr(node->value).o, node->attr.c_str());
}
}
// Copyright (c) 2014 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_ASTINTERPRETER_H
#define PYSTON_CODEGEN_ASTINTERPRETER_H
namespace pyston {
namespace gc {
class GCVisitor;
}
class Box;
class BoxedDict;
class BoxedModule;
struct CompiledFunction;
struct LineInfo;
Box* astInterpretFunction(CompiledFunction* f, int nargs, Box* closure, Box* generator, Box* arg1, Box* arg2, Box* arg3,
Box** args);
const LineInfo* getLineInfoForInterpretedFrame(void* frame_ptr);
BoxedModule* getModuleForInterpretedFrame(void* frame_ptr);
void gatherInterpreterRoots(gc::GCVisitor* visitor);
BoxedDict* localsForInterpretedFrame(void* frame_ptr, bool only_user_visible);
}
#endif
......@@ -279,7 +279,7 @@ computeBlockTraversalOrder(const BlockSet& full_blocks, const BlockSet& partial_
}
static ConcreteCompilerType* getTypeAtBlockStart(TypeAnalysis* types, const std::string& name, CFGBlock* block) {
if (startswith(name, "!is_defined"))
if (isIsDefinedName(name))
return BOOL;
else if (name == PASSED_GENERATOR_NAME)
return GENERATOR;
......@@ -329,9 +329,8 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
// llvm::BranchInst::Create(llvm_entry_blocks[entry_descriptor->backedge->target->idx], entry_block);
llvm::BasicBlock* osr_entry_block_end = osr_entry_block;
llvm::BasicBlock* osr_unbox_block_end = osr_unbox_block;
std::unique_ptr<IREmitter> entry_emitter(createIREmitter(irstate, osr_entry_block_end));
std::unique_ptr<IREmitter> unbox_emitter(createIREmitter(irstate, osr_unbox_block_end));
std::unique_ptr<IREmitter> unbox_emitter(createIREmitter(irstate, osr_unbox_block));
CFGBlock* target_block = entry_descriptor->backedge->target;
......@@ -409,8 +408,29 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
}
ASSERT(speculated_class, "%s", phi_type->debugName().c_str());
ASSERT(entry_descriptor->args.count("!is_defined_" + p.first) == 0,
"This class-check-creating behavior will segfault if the argument wasn't actually defined!");
assert(p.first[0] != '!');
std::string is_defined_name = getIsDefinedName(p.first);
llvm::BasicBlock* defined_join = nullptr, * defined_prev = nullptr, * defined_check = nullptr;
if (entry_descriptor->args.count(is_defined_name)) {
// relying on the fact that we are iterating over the names in order
// and the fake names precede the real names:
assert(osr_syms->count(is_defined_name));
ConcreteCompilerVariable* is_defined_var = (*osr_syms)[is_defined_name];
assert(is_defined_var->getType() == BOOL);
llvm::Value* is_defined_i1 = i1FromBool(*entry_emitter, is_defined_var);
defined_check = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
defined_join = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
llvm::BranchInst* br
= entry_emitter->getBuilder()->CreateCondBr(is_defined_i1, defined_check, defined_join);
defined_prev = osr_entry_block_end;
osr_entry_block_end = defined_check;
entry_emitter->getBuilder()->SetInsertPoint(defined_check);
}
llvm::Value* type_check = ConcreteCompilerVariable(p.second, from_arg, true)
.makeClassCheck(*entry_emitter, speculated_class);
......@@ -421,6 +441,18 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
}
// entry_emitter->getBuilder()->CreateCall(g.funcs.my_assert, type_check);
if (defined_join) {
entry_emitter->getBuilder()->CreateBr(defined_join);
osr_entry_block_end = defined_join;
entry_emitter->getBuilder()->SetInsertPoint(defined_join);
auto guard_phi = entry_emitter->getBuilder()->CreatePHI(g.i1, 2);
guard_phi->addIncoming(getConstantInt(0, g.i1), defined_prev);
guard_phi->addIncoming(guard_val, defined_check);
guard_val = guard_phi;
}
if (speculated_class == int_cls) {
v = unbox_emitter->getBuilder()->CreateCall(g.funcs.unboxInt, from_arg);
(new ConcreteCompilerVariable(BOXED_INT, from_arg, true))->decvref(*unbox_emitter);
......@@ -621,25 +653,32 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
into_hax.insert(b2);
}
const PhiAnalysis::RequiredSet& names = source->phis->getAllRequiredFor(block);
std::unordered_set<std::string> names;
for (const auto& s : source->phis->getAllRequiredFor(block)) {
names.insert(s);
if (source->phis->isPotentiallyUndefinedAfter(s, block->predecessors[0])) {
names.insert(getIsDefinedName(s));
}
}
if (source->getScopeInfo()->createsClosure())
names.insert(CREATED_CLOSURE_NAME);
if (source->getScopeInfo()->takesClosure())
names.insert(PASSED_CLOSURE_NAME);
if (source->getScopeInfo()->takesGenerator())
names.insert(PASSED_GENERATOR_NAME);
for (const auto& s : names) {
// printf("adding guessed phi for %s\n", s.c_str());
ConcreteCompilerType* type = types->getTypeAtBlockStart(s, block);
ConcreteCompilerType* type = getTypeAtBlockStart(types, s, block);
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(type->llvmType(), block->predecessors.size(), s);
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(type, phi, true);
generator->giveLocalSymbol(s, var);
(*phis)[s] = std::make_pair(type, phi);
if (source->phis->isPotentiallyUndefinedAfter(s, block->predecessors[0])) {
std::string is_defined_name = "!is_defined_" + s;
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(BOOL->llvmType(), block->predecessors.size(),
is_defined_name);
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(BOOL, phi, true);
generator->giveLocalSymbol(is_defined_name, var);
(*phis)[is_defined_name] = std::make_pair(BOOL, phi);
}
}
} else {
assert(pred);
......@@ -746,7 +785,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
if (full_blocks.count(b2) == 0 && partial_blocks.count(b2) == 0)
continue;
// printf("%d %d %ld %ld\n", i, b2->idx, phi_ending_symbol_tables[b2]->size(), phis->size());
// printf("%d %d %ld %ld\n", b->idx, b2->idx, phi_ending_symbol_tables[b2]->size(), phis->size());
compareKeyset(phi_ending_symbol_tables[b2], phis);
assert(phi_ending_symbol_tables[b2]->size() == phis->size());
}
......@@ -762,13 +801,18 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
llvm::BasicBlock* off_ramp = llvm::BasicBlock::Create(g.context, "deopt_ramp", irstate->getLLVMFunction());
offramps.push_back(off_ramp);
llvm::BasicBlock* off_ramp_end = off_ramp;
IREmitter* emitter = createIREmitter(irstate, off_ramp_end);
IREmitter* emitter = createIREmitter(irstate, offramps[offramps.size() - 1]);
emitters.push_back(emitter);
block_guards[i]->branch->setSuccessor(1, off_ramp);
}
// Can't always add the phi incoming value right away, since we may have to create more
// basic blocks as part of type coercion.
// Intsead, just make a record of the phi node, value, and the location of the from-BB,
// which we won't read until after all new BBs have been added.
std::vector<std::tuple<llvm::PHINode*, llvm::Value*, llvm::BasicBlock*&> > phi_args;
for (PHITable::iterator it = phis->begin(); it != phis->end(); it++) {
llvm::PHINode* llvm_phi = it->second.second;
for (int j = 0; j < b->predecessors.size(); j++) {
......@@ -796,12 +840,33 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
llvm_phi->addIncoming(v->getValue(), osr_unbox_block);
}
std::string is_defined_name = getIsDefinedName(it->first);
for (int i = 0; i < block_guards.size(); i++) {
GuardList::BlockEntryGuard* guard = block_guards[i];
IREmitter* emitter = emitters[i];
ASSERT(phis->count("!is_defined_" + it->first) == 0,
"This class-check-creating behavior will segfault if the argument wasn't actually defined!");
auto is_defined_it = guard->symbol_table.find(is_defined_name);
ConcreteCompilerVariable* is_defined_var = nullptr;
if (is_defined_it != guard->symbol_table.end()) {
auto var = is_defined_it->second;
assert(var->getType() == BOOL);
is_defined_var = static_cast<ConcreteCompilerVariable*>(var);
}
llvm::BasicBlock* defined_prev = nullptr, * defined_convert = nullptr, * defined_join = nullptr;
if (is_defined_var) {
defined_prev = offramps[i];
defined_convert = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
defined_join = llvm::BasicBlock::Create(g.context, "", irstate->getLLVMFunction());
llvm::Value* is_defined_val = i1FromBool(*emitter, is_defined_var);
emitter->getBuilder()->CreateCondBr(is_defined_val, defined_convert, defined_join);
offramps[i] = defined_convert;
emitter->getBuilder()->SetInsertPoint(defined_convert);
}
CompilerVariable* unconverted = guard->symbol_table[it->first];
ConcreteCompilerVariable* v;
......@@ -854,13 +919,31 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
ASSERT(it->second.first == v->getType(), "");
assert(it->second.first->llvmType() == v->getValue()->getType());
llvm_phi->addIncoming(v->getValue(), offramps[i]);
llvm::Value* val = v->getValue();
if (defined_prev) {
emitter->getBuilder()->CreateBr(defined_join);
auto prev = offramps[i];
offramps[i] = defined_join;
emitter->getBuilder()->SetInsertPoint(defined_join);
auto phi = emitter->getBuilder()->CreatePHI(it->second.first->llvmType(), 2);
phi->addIncoming(llvm::UndefValue::get(phi->getType()), defined_prev);
phi->addIncoming(val, prev);
val = phi;
}
phi_args.emplace_back(llvm_phi, val, offramps[i]);
// TODO not sure if this is right:
unconverted->decvref(*emitter);
delete v;
}
}
for (auto t : phi_args) {
std::get<0>(t)->addIncoming(std::get<1>(t), std::get<2>(t));
}
for (int i = 0; i < block_guards.size(); i++) {
emitters[i]->getBuilder()->CreateBr(llvm_entry_blocks[b]);
......@@ -986,7 +1069,9 @@ static std::string getUniqueFunctionName(std::string nameprefix, EffortLevel::Ef
os << nameprefix;
os << "_e" << effort;
if (entry) {
os << "_osr" << entry->backedge->target->idx << "_from_" << entry->cf->func->getName().data();
os << "_osr" << entry->backedge->target->idx;
if (entry->cf->func)
os << "_from_" << entry->cf->func->getName().data();
}
os << '_' << num_functions;
num_functions++;
......
......@@ -79,6 +79,13 @@ public:
ExcInfo exc_info) = 0;
};
extern const std::string CREATED_CLOSURE_NAME;
extern const std::string PASSED_CLOSURE_NAME;
extern const std::string PASSED_GENERATOR_NAME;
std::string getIsDefinedName(const std::string& name);
bool isIsDefinedName(const std::string& name);
CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_descriptor,
EffortLevel::EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix);
......
......@@ -20,6 +20,7 @@
#include "analysis/function_analysis.h"
#include "analysis/scoping_analysis.h"
#include "asm_writing/icinfo.h"
#include "codegen/ast_interpreter.h"
#include "codegen/codegen.h"
#include "codegen/compvars.h"
#include "codegen/irgen.h"
......@@ -137,7 +138,6 @@ static std::unordered_map<std::string, CompiledFunction*> machine_name_to_cf;
CompiledFunction* cfForMachineFunctionName(const std::string& machine_name) {
assert(machine_name.size());
auto r = machine_name_to_cf[machine_name];
ASSERT(r, "%s", machine_name.c_str());
return r;
}
......@@ -185,17 +185,29 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
// Do the analysis now if we had deferred it earlier:
if (source->cfg == NULL) {
assert(source->ast);
source->cfg = computeCFG(source, source->body);
source->liveness = computeLivenessInfo(source->cfg);
source->phis = computeRequiredPhis(source->arg_names, source->cfg, source->liveness, source->getScopeInfo());
}
CompiledFunction* cf = doCompile(source, entry, effort, spec, name);
if (effort != EffortLevel::INTERPRETED) {
if (source->liveness == NULL)
source->liveness = computeLivenessInfo(source->cfg);
if (source->phis == NULL)
source->phis
= computeRequiredPhis(source->arg_names, source->cfg, source->liveness, source->getScopeInfo());
}
registerMachineName(cf->func->getName(), cf);
compileIR(cf, effort);
CompiledFunction* cf = 0;
if (effort == EffortLevel::INTERPRETED) {
cf = new CompiledFunction(0, spec, true, NULL, NULL, effort, 0);
} else {
cf = doCompile(source, entry, effort, spec, name);
registerMachineName(cf->func->getName(), cf);
compileIR(cf, effort);
}
f->addVersion(cf);
assert(f->versions.size());
......@@ -252,10 +264,6 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
ScopingAnalysis* scoping = runScopingAnalysis(m);
SourceInfo* si = new SourceInfo(bm, scoping, m, m->body);
si->cfg = computeCFG(si, m->body);
si->liveness = computeLivenessInfo(si->cfg);
si->phis = computeRequiredPhis(si->arg_names, si->cfg, si->liveness, si->getScopeInfo());
CLFunction* cl_f = new CLFunction(0, 0, false, false, si);
EffortLevel::EffortLevel effort = initialEffort();
......@@ -265,7 +273,7 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
}
if (cf->is_interpreted)
interpretFunction(cf->func, 0, NULL, NULL, NULL, NULL, NULL, NULL);
astInterpretFunction(cf, 0, NULL, NULL, NULL, NULL, NULL, NULL);
else
((void (*)())cf->code)();
}
......@@ -311,7 +319,7 @@ static CompiledFunction* _doReopt(CompiledFunction* cf, EffortLevel::EffortLevel
}
static StatCounter stat_osrexits("OSR exits");
void* compilePartialFunc(OSRExit* exit) {
CompiledFunction* compilePartialFuncInternal(OSRExit* exit) {
LOCK_REGION(codegen_rwlock.asWrite());
assert(exit);
......@@ -334,11 +342,16 @@ void* compilePartialFunc(OSRExit* exit) {
assert(compiled = new_cf);
}
return new_cf->code;
return new_cf;
}
void* compilePartialFunc(OSRExit* exit) {
return compilePartialFuncInternal(exit)->code;
}
static StatCounter stat_reopt("reopts");
extern "C" char* reoptCompiledFunc(CompiledFunction* cf) {
extern "C" CompiledFunction* reoptCompiledFuncInternal(CompiledFunction* cf) {
if (VERBOSITY("irgen") >= 1)
printf("In reoptCompiledFunc, %p, %ld\n", cf, cf->times_called);
stat_reopt.log();
......@@ -347,7 +360,12 @@ extern "C" char* reoptCompiledFunc(CompiledFunction* cf) {
assert(cf->clfunc->versions.size());
CompiledFunction* new_cf = _doReopt(cf, (EffortLevel::EffortLevel(cf->effort + 1)));
assert(!new_cf->is_interpreted);
return (char*)new_cf->code;
return new_cf;
}
extern "C" char* reoptCompiledFunc(CompiledFunction* cf) {
return (char*)reoptCompiledFuncInternal(cf)->code;
}
CLFunction* createRTFunction(int num_args, int num_defaults, bool takes_varargs, bool takes_kwargs) {
......
......@@ -23,7 +23,9 @@ struct CompiledFunction;
class CLFunction;
class OSRExit;
CompiledFunction* compilePartialFuncInternal(OSRExit* exit);
void* compilePartialFunc(OSRExit*);
extern "C" CompiledFunction* reoptCompiledFuncInternal(CompiledFunction*);
extern "C" char* reoptCompiledFunc(CompiledFunction*);
class AST_Module;
......
......@@ -256,6 +256,14 @@ const std::string CREATED_CLOSURE_NAME = "!created_closure";
const std::string PASSED_CLOSURE_NAME = "!passed_closure";
const std::string PASSED_GENERATOR_NAME = "!passed_generator";
std::string getIsDefinedName(const std::string& name) {
return "!is_defined_" + name;
}
bool isIsDefinedName(const std::string& name) {
return startswith(name, "!is_defined_");
}
class IRGeneratorImpl : public IRGenerator {
private:
IRGenState* irstate;
......@@ -411,8 +419,8 @@ private:
if (p.first[0] == '!' || p.first[0] == '#')
continue;
ConcreteCompilerVariable* is_defined_var = static_cast<ConcreteCompilerVariable*>(
_getFake(_getFakeName("is_defined", p.first.c_str()), true));
ConcreteCompilerVariable* is_defined_var
= static_cast<ConcreteCompilerVariable*>(_getFake(getIsDefinedName(p.first), true));
static const std::string setitem_str("__setitem__");
if (!is_defined_var) {
......@@ -822,7 +830,7 @@ private:
return undefVariable();
}
std::string defined_name = _getFakeName("is_defined", node->id.c_str());
std::string defined_name = getIsDefinedName(node->id);
ConcreteCompilerVariable* is_defined_var
= static_cast<ConcreteCompilerVariable*>(_getFake(defined_name, true));
......@@ -1272,12 +1280,6 @@ private:
return rtn;
}
static std::string _getFakeName(const char* prefix, const char* token) {
char buf[40];
snprintf(buf, 40, "!%s_%s", prefix, token);
return std::string(buf);
}
void _setFake(std::string name, CompilerVariable* val) {
assert(name[0] == '!');
CompilerVariable*& cur = symbol_table[name];
......@@ -1326,8 +1328,8 @@ private:
val->incvref();
// Clear out the is_defined name since it is now definitely defined:
assert(!startswith(name, "!is_defined"));
std::string defined_name = _getFakeName("is_defined", name.c_str());
assert(!isIsDefinedName(name));
std::string defined_name = getIsDefinedName(name);
_popFake(defined_name, true);
if (scope_info->saveInClosure(name)) {
......@@ -1496,7 +1498,7 @@ private:
decorators.push_back(evalExpr(d, exc_info));
}
CLFunction* cl = _wrapFunction(node, nullptr, node->body);
CLFunction* cl = wrapFunction(node, nullptr, node->body, irstate->getSourceInfo());
// TODO duplication with _createFunction:
CompilerVariable* created_closure = NULL;
......@@ -1614,7 +1616,7 @@ private:
return;
}
std::string defined_name = _getFakeName("is_defined", target->id.c_str());
std::string defined_name = getIsDefinedName(target->id);
ConcreteCompilerVariable* is_defined_var = static_cast<ConcreteCompilerVariable*>(_getFake(defined_name, true));
if (is_defined_var) {
......@@ -1628,27 +1630,9 @@ private:
symbol_table.erase(target->id);
}
CLFunction* _wrapFunction(AST* node, AST_arguments* args, const std::vector<AST_stmt*>& body) {
// Different compilations of the parent scope of a functiondef should lead
// to the same CLFunction* being used:
static std::unordered_map<AST*, CLFunction*> made;
CLFunction*& cl = made[node];
if (cl == NULL) {
SourceInfo* source = irstate->getSourceInfo();
SourceInfo* si = new SourceInfo(source->parent_module, source->scoping, node, body);
if (args)
cl = new CLFunction(args->args.size(), args->defaults.size(), args->vararg.size(), args->kwarg.size(),
si);
else
cl = new CLFunction(0, 0, 0, 0, si);
}
return cl;
}
CompilerVariable* _createFunction(AST* node, ExcInfo exc_info, AST_arguments* args,
const std::vector<AST_stmt*>& body) {
CLFunction* cl = this->_wrapFunction(node, args, body);
CLFunction* cl = wrapFunction(node, args, body, irstate->getSourceInfo());
std::vector<ConcreteCompilerVariable*> defaults;
for (auto d : args->defaults) {
......@@ -2159,7 +2143,7 @@ private:
}
bool allowableFakeEndingSymbol(const std::string& name) {
return startswith(name, "!is_defined") || name == PASSED_CLOSURE_NAME || name == CREATED_CLOSURE_NAME
return isIsDefinedName(name) || name == PASSED_CLOSURE_NAME || name == CREATED_CLOSURE_NAME
|| name == PASSED_GENERATOR_NAME;
}
......@@ -2177,7 +2161,7 @@ private:
continue;
}
// ASSERT(it->first[0] != '!' || startswith(it->first, "!is_defined"), "left a fake variable in the real
// ASSERT(it->first[0] != '!' || isIsDefinedName(it->first), "left a fake variable in the real
// symbol table? '%s'", it->first.c_str());
if (!source->liveness->isLiveAtEnd(it->first, myblock)) {
......@@ -2218,7 +2202,7 @@ private:
assert(!scope_info->refersToGlobal(*it));
CompilerVariable*& cur = symbol_table[*it];
std::string defined_name = _getFakeName("is_defined", it->c_str());
std::string defined_name = getIsDefinedName(*it);
if (cur != NULL) {
// printf("defined on this path; ");
......@@ -2309,7 +2293,7 @@ public:
assert(it->second->getVrefs() == 1);
// this conversion should have already happened... should refactor this.
ConcreteCompilerType* ending_type;
if (startswith(it->first, "!is_defined")) {
if (isIsDefinedName(it->first)) {
assert(it->second->getType() == BOOL);
ending_type = BOOL;
} else if (it->first == PASSED_CLOSURE_NAME) {
......@@ -2456,4 +2440,20 @@ IRGenerator* createIRGenerator(IRGenState* irstate, std::unordered_map<CFGBlock*
const GuardList& in_guards, bool is_partial) {
return new IRGeneratorImpl(irstate, entry_blocks, myblock, types, out_guards, in_guards, is_partial);
}
CLFunction* wrapFunction(AST* node, AST_arguments* args, const std::vector<AST_stmt*>& body, SourceInfo* source) {
// Different compilations of the parent scope of a functiondef should lead
// to the same CLFunction* being used:
static std::unordered_map<AST*, CLFunction*> made;
CLFunction*& cl = made[node];
if (cl == NULL) {
SourceInfo* si = new SourceInfo(source->parent_module, source->scoping, node, body);
if (args)
cl = new CLFunction(args->args.size(), args->defaults.size(), args->vararg.size(), args->kwarg.size(), si);
else
cl = new CLFunction(0, 0, 0, 0, si);
}
return cl;
}
}
......@@ -46,6 +46,7 @@ extern const std::string CREATED_CLOSURE_NAME;
extern const std::string PASSED_CLOSURE_NAME;
extern const std::string PASSED_GENERATOR_NAME;
// Class that holds state of the current IR generation, that might not be local
// to the specific phase or pass we're in.
// TODO this probably shouldn't be here
......@@ -205,6 +206,8 @@ IREmitter* createIREmitter(IRGenState* irstate, llvm::BasicBlock*& curblock, IRG
IRGenerator* createIRGenerator(IRGenState* irstate, std::unordered_map<CFGBlock*, llvm::BasicBlock*>& entry_blocks,
CFGBlock* myblock, TypeAnalysis* types, GuardList& out_guards,
const GuardList& in_guards, bool is_partial);
CLFunction* wrapFunction(AST* node, AST_arguments* args, const std::vector<AST_stmt*>& body, SourceInfo* source);
}
#endif
......@@ -219,8 +219,8 @@ static void set(SymMap& symbols, const llvm::BasicBlock::iterator& it, Val v) {
static std::unordered_map<void*, llvm::Instruction*> cur_instruction_map;
typedef std::vector<const SymMap*> root_stack_t;
threading::PerThreadSet<root_stack_t> root_stack_set;
static threading::PerThreadSet<root_stack_t> root_stack_set;
/*
void gatherInterpreterRoots(GCVisitor* visitor) {
root_stack_set.forEachValue(std::function<void(root_stack_t*, GCVisitor*)>([](root_stack_t* v, GCVisitor* visitor) {
for (const SymMap* sym_map : *v) {
......@@ -231,7 +231,8 @@ void gatherInterpreterRoots(GCVisitor* visitor) {
}),
visitor);
}
*/
#if 0
BoxedDict* localsForInterpretedFrame(void* frame_ptr, bool only_user_visible) {
llvm::Instruction* inst = cur_instruction_map[frame_ptr];
assert(inst);
......@@ -285,6 +286,7 @@ BoxedDict* localsForInterpretedFrame(void* frame_ptr, bool only_user_visible) {
return rtn;
}
#endif
class UnregisterHelper {
private:
......@@ -302,6 +304,7 @@ public:
};
static std::unordered_map<llvm::Instruction*, LineInfo*> line_infos;
/*
const LineInfo* getLineInfoForInterpretedFrame(void* frame_ptr) {
llvm::Instruction* cur_instruction = cur_instruction_map[frame_ptr];
assert(cur_instruction);
......@@ -319,7 +322,7 @@ const LineInfo* getLineInfoForInterpretedFrame(void* frame_ptr) {
return it->second;
}
}
*/
void dumpLLVM(llvm::Value* v) {
v->dump();
}
......
......@@ -29,12 +29,14 @@ class Box;
class BoxedDict;
struct LineInfo;
#if 0
Box* interpretFunction(llvm::Function* f, int nargs, Box* closure, Box* generator, Box* arg1, Box* arg2, Box* arg3,
Box** args);
void gatherInterpreterRoots(gc::GCVisitor* visitor);
const LineInfo* getLineInfoForInterpretedFrame(void* frame_ptr);
BoxedDict* localsForInterpretedFrame(void* frame_ptr, bool only_user_visible);
#endif
}
#endif
......@@ -23,6 +23,7 @@
#include "llvm/ExecutionEngine/ObjectImage.h"
#include "llvm/IR/DebugInfo.h"
#include "codegen/ast_interpreter.h"
#include "codegen/codegen.h"
#include "codegen/compvars.h"
#include "codegen/irgen/hooks.h"
......@@ -307,7 +308,7 @@ public:
int code = unw_get_proc_info(&this->cursor, &pip);
RELEASE_ASSERT(code == 0, "%d", code);
if (pip.start_ip == (intptr_t)interpretFunction) {
if (pip.start_ip == (intptr_t)astInterpretFunction) {
unw_word_t bp;
unw_get_reg(&this->cursor, UNW_TDEP_BP, &bp);
......@@ -401,12 +402,19 @@ CompiledFunction* getTopCompiledFunction() {
assert(last_entry->func.size());
CompiledFunction* cf = cfForMachineFunctionName(last_entry->func);
assert(cf);
return cf;
}
BoxedModule* getCurrentModule() {
return getTopCompiledFunction()->clfunc->source->parent_module;
CompiledFunction* compiledFunction = getTopCompiledFunction();
if (compiledFunction)
return compiledFunction->clfunc->source->parent_module;
else {
std::unique_ptr<PythonFrameIterator> frame = getTopPythonFrame();
auto& id = frame->getId();
assert(id.type == PythonFrameId::INTERPRETED);
return getModuleForInterpretedFrame((void*)id.bp);
}
}
BoxedDict* getLocals(bool only_user_visible) {
......
......@@ -288,7 +288,6 @@ public:
void addVersion(CompiledFunction* compiled) {
assert(compiled);
assert((source == NULL) == (compiled->func == NULL));
assert(compiled->spec);
assert(compiled->spec->arg_types.size() == num_args + (takes_varargs ? 1 : 0) + (takes_kwargs ? 1 : 0));
assert(compiled->clfunc == NULL);
......
......@@ -21,8 +21,8 @@
#include <setjmp.h>
#include <vector>
#include "codegen/ast_interpreter.h"
#include "codegen/codegen.h"
#include "codegen/llvm_interpreter.h"
#include "core/common.h"
#include "core/threading.h"
#include "gc/collector.h"
......
......@@ -23,6 +23,7 @@
#include "asm_writing/icinfo.h"
#include "asm_writing/rewriter.h"
#include "codegen/ast_interpreter.h"
#include "codegen/codegen.h"
#include "codegen/compvars.h"
#include "codegen/irgen/hooks.h"
......@@ -2486,7 +2487,7 @@ Box* callCLFunc(CLFunction* f, CallRewriteArgs* rewrite_args, int num_output_arg
assert(chosen_cf->is_interpreted == (chosen_cf->code == NULL));
if (chosen_cf->is_interpreted) {
return interpretFunction(chosen_cf->func, num_output_args, closure, generator, oarg1, oarg2, oarg3, oargs);
return astInterpretFunction(chosen_cf, num_output_args, closure, generator, oarg1, oarg2, oarg3, oargs);
}
if (rewrite_args) {
......
......@@ -31,9 +31,11 @@ extern "C" Box* createSet();
class BoxedSet : public Box {
public:
std::unordered_set<Box*, PyHasher, PyEq, StlCompatAllocator<Box*> > s;
typedef std::unordered_set<Box*, PyHasher, PyEq, StlCompatAllocator<Box*> > Set;
Set s;
BoxedSet(BoxedClass* cls) __attribute__((visibility("default"))) : Box(cls) {}
BoxedSet(Set&& s, BoxedClass* cls) __attribute__((visibility("default"))) : Box(cls), s(s) {}
};
}
......
# expected: fail
def f((a,b)):
print a,b
f(range(2))
f((1, 2))
def gen():
while True:
for i in xrange(100):
range(100) * 1000 # allocate memory to force GC collections
b = yield 0
if b:
break
done = []
def thread_run(g):
for i in xrange(5):
g.next()
try:
g.send(1)
assert 0
except StopIteration:
pass
done.append(0)
g = gen()
for i in xrange(5):
g.next()
from thread import start_new_thread
t = start_new_thread(thread_run, (g,))
g = ""
def f():
pass
f()
f()
import time
while not done:
time.sleep(0.1)
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