Commit 0ec4a8fa authored by Kevin Modzelewski's avatar Kevin Modzelewski

Merge "intern codegen strings" change into keyword-params

The InternedString change conflicted with Travis's keyword-params change,
since the arg names were switched to InternedStrings.

I decided to not intern the param names any more, and instead represent them
as llvm::StringRef.  Another option would be to intern the builtin ones, even
though there's no independent benefit to doing so, since that would let us use
InternedStrings more generally.  Probably the "best" but most complicated would
be to have the StringRef version and then an InternedString version as well
that was constructed when needed.
parents 5de99b04 325dbfeb
......@@ -16,8 +16,8 @@ install:
- git config --global user.email "you@example.com"
- git config --global user.name "Your Name"
- mkdir ~/pyston-build && cd ~/pyston-build
- make -C $TRAVIS_BUILD_DIR llvm_up
- cmake -GNinja $TRAVIS_BUILD_DIR
- ninja llvm_up
- ninja libunwind ext_cpython
script:
......
......@@ -969,6 +969,7 @@ test_cpp_ll:
$(CLANGPP_EXE) $(TEST_DIR)/test.cpp -o test.ll -c -O3 -emit-llvm -S -std=c++11 -g
less test.ll
rm test.ll
.PHONY: bench_exceptions
bench_exceptions:
$(CLANGPP_EXE) $(TEST_DIR)/bench_exceptions.cpp -o bench_exceptions -O3 -std=c++11
zsh -c 'ulimit -v $(MAX_MEM_KB); ulimit -d $(MAX_MEM_KB); time ./bench_exceptions'
......
......@@ -123,7 +123,8 @@ PyAPI_DATA(PyTypeObject*) dictvalues_cls;
#define PyDictValues_Type (*dictvalues_cls)
// Pyston changes: these aren't direct macros any more [they potentially could be though]
PyAPI_FUNC(bool) PyDict_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(bool) _PyDict_Check(PyObject*) PYSTON_NOEXCEPT;
#define PyDict_Check(op) _PyDict_Check((PyObject*)(op))
#if 0
#define PyDict_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS)
......
......@@ -37,6 +37,9 @@ typedef struct {
#endif
typedef struct _PyFileObject PyFileObject;
// Pyston change: use this to access the fp instead of ->f_fp
PyAPI_FUNC(void) PyFile_SetFP(PyObject*, FILE*) PYSTON_NOEXCEPT;
// Pyston change: this is no longer a static object
PyAPI_DATA(PyTypeObject*) file_cls;
#define PyFile_Type (*file_cls)
......
......@@ -49,7 +49,8 @@ PyAPI_DATA(PyTypeObject*) list_cls;
#define PyList_Type (*list_cls)
// Pyston changes: these aren't direct macros any more [they potentially could be though]
PyAPI_FUNC(bool) PyList_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(bool) _PyList_Check(PyObject*) PYSTON_NOEXCEPT;
#define PyList_Check(op) _PyList_Check((PyObject*)(op))
#if 0
#define PyList_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LIST_SUBCLASS)
......@@ -71,9 +72,9 @@ PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *) PYSTON_NOEXCEP
/* Macro, trading safety for speed */
// Pyston changes: these aren't direct macros any more [they potentially could be though]
#define PyList_GET_ITEM(op, i) PyList_GetItem(op, i)
#define PyList_SET_ITEM(op, i, v) PyList_SetItem(op, i, v)
#define PyList_GET_SIZE(op) PyList_Size(op)
#define PyList_GET_ITEM(op, i) PyList_GetItem((PyObject*)(op), (i))
#define PyList_SET_ITEM(op, i, v) PyList_SetItem((PyObject*)(op), (i), (v))
#define PyList_GET_SIZE(op) PyList_Size((PyObject*)(op))
//#define PyList_GET_ITEM(op, i) (((PyListObject *)(op))->ob_item[i])
//#define PyList_SET_ITEM(op, i, v) (((PyListObject *)(op))->ob_item[i] = (v))
//#define PyList_GET_SIZE(op) Py_SIZE(op)
......
......@@ -21,7 +21,8 @@ PyAPI_DATA(PyTypeObject*) long_cls;
#define PyLong_Type (*long_cls)
// Pyston changes: these aren't direct macros any more [they potentially could be though]
PyAPI_FUNC(bool) PyLong_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(bool) _PyLong_Check(PyObject*) PYSTON_NOEXCEPT;
#define PyLong_Check(op) _PyLong_Check((PyObject*)(op))
#if 0
#define PyLong_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS)
......
......@@ -499,7 +499,8 @@ PyAPI_DATA(PyTypeObject*) type_cls;
//PyAPI_DATA(PyTypeObject) PySuper_Type; /* built-in 'super' */
// Pyston changes: these aren't direct macros any more [they potentially could be though]
PyAPI_FUNC(bool) PyType_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(bool) _PyType_Check(PyObject*) PYSTON_NOEXCEPT;
#define PyType_Check(op) _PyType_Check((PyObject*)(op))
#if 0
#define PyType_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TYPE_SUBCLASS)
......
......@@ -39,7 +39,8 @@ PyAPI_DATA(PyTypeObject*) ellipsis_cls;
// Pyston changes: these aren't direct macros any more [they potentially could be though]
//#define PySlice_Check(op) (Py_TYPE(op) == &PySlice_Type)
PyAPI_FUNC(bool) PySlice_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(bool) _PySlice_Check(PyObject*) PYSTON_NOEXCEPT;
#define PySlice_Check(op) _PySlice_Check((PyObject*)(op))
PyAPI_FUNC(PyObject *) PySlice_New(PyObject* start, PyObject* stop,
PyObject* step) PYSTON_NOEXCEPT;
......
......@@ -42,7 +42,8 @@ PyAPI_DATA(PyTypeObject*) tuple_cls;
#define PyTuple_Type (*tuple_cls)
// Pyston changes: these aren't direct macros any more [they potentially could be though]
PyAPI_FUNC(bool) PyTuple_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(bool) _PyTuple_Check(PyObject*) PYSTON_NOEXCEPT;
#define PyTuple_Check(op) _PyTuple_Check((PyObject*)(op))
#if 0
#define PyTuple_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TUPLE_SUBCLASS)
......
......@@ -435,7 +435,8 @@ PyAPI_DATA(PyTypeObject*) unicode_cls;
//PyAPI_DATA(PyTypeObject) PyUnicode_Type;
// Pyston changes: these aren't direct macros any more [they potentially could be though]
PyAPI_FUNC(bool) PyUnicode_Check(PyObject*) PYSTON_NOEXCEPT;
PyAPI_FUNC(bool) _PyUnicode_Check(PyObject*) PYSTON_NOEXCEPT;
#define PyUnicode_Check(op) _PyUnicode_Check((PyObject*)(op))
#if 0
#define PyUnicode_Check(op) \
PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_UNICODE_SUBCLASS)
......
......@@ -6916,7 +6916,7 @@ posix_fdopen(PyObject *self, PyObject *args)
/* We now know we will succeed, so initialize the file object. */
// Pyston change:
Py_FatalError("Pyston TODO: implement this");
PyFile_SetFP(f, fp);
//((PyFileObject *)f)->f_fp = fp;
PyFile_SetBufSize(f, bufsize);
......
......@@ -25,7 +25,7 @@ namespace pyston {
template <typename T> class BBAnalyzer {
public:
typedef std::unordered_map<std::string, T> Map;
typedef std::unordered_map<InternedString, T> Map;
typedef std::unordered_map<CFGBlock*, Map> AllMap;
virtual ~BBAnalyzer() {}
......
This diff is collapsed.
......@@ -19,6 +19,7 @@
#include <unordered_map>
#include <unordered_set>
#include "core/stringpool.h"
#include "core/types.h"
namespace pyston {
......@@ -39,17 +40,7 @@ private:
typedef std::unordered_map<CFGBlock*, std::unique_ptr<LivenessBBVisitor>> LivenessCacheMap;
LivenessCacheMap liveness_cache;
std::unordered_map<int, std::unordered_map<CFGBlock*, bool>> result_cache;
// Map strings to unique indices. For a given CFG, the set of strings should be fairly small
// (a constant fraction max of the CFG itself), so just store all of them. The theory is that
// for any particular name, we will do many lookups on it in different hash tables, and by
// converting to a string only once, the extra hashtable lookup will be profitable since it
// can make all the rest faster (int hashes vs string hashes).
//
// Haven't validated this, though.
std::unordered_map<std::string, int> string_index_map;
int getStringIndex(const std::string& s);
std::unordered_map<InternedString, std::unordered_map<CFGBlock*, bool>> result_cache;
public:
LivenessAnalysis(CFG* cfg);
......@@ -57,7 +48,7 @@ public:
// we don't keep track of node->parent_block relationships, so you have to pass both:
bool isKill(AST_Name* node, CFGBlock* parent_block);
bool isLiveAtEnd(const std::string& name, CFGBlock* block);
bool isLiveAtEnd(InternedString name, CFGBlock* block);
};
class DefinednessAnalysis {
......@@ -67,22 +58,22 @@ public:
PotentiallyDefined,
Defined,
};
typedef std::unordered_set<std::string> RequiredSet;
typedef std::unordered_set<InternedString> RequiredSet;
private:
std::unordered_map<CFGBlock*, std::unordered_map<std::string, DefinitionLevel>> results;
std::unordered_map<CFGBlock*, std::unordered_map<InternedString, DefinitionLevel>> results;
std::unordered_map<CFGBlock*, const RequiredSet> defined_at_end;
ScopeInfo* scope_info;
public:
DefinednessAnalysis(const ParamNames& param_names, CFG* cfg, ScopeInfo* scope_info);
DefinitionLevel isDefinedAtEnd(const std::string& name, CFGBlock* block);
DefinitionLevel isDefinedAtEnd(InternedString name, CFGBlock* block);
const RequiredSet& getDefinedNamesAtEnd(CFGBlock* block);
};
class PhiAnalysis {
public:
typedef std::unordered_set<std::string> RequiredSet;
typedef std::unordered_set<InternedString> RequiredSet;
DefinednessAnalysis definedness;
......@@ -93,12 +84,12 @@ private:
public:
PhiAnalysis(const ParamNames&, CFG* cfg, LivenessAnalysis* liveness, ScopeInfo* scope_info);
bool isRequired(const std::string& name, CFGBlock* block);
bool isRequiredAfter(const std::string& name, CFGBlock* block);
bool isRequired(InternedString name, CFGBlock* block);
bool isRequiredAfter(InternedString name, CFGBlock* block);
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);
bool isPotentiallyUndefinedAfter(InternedString name, CFGBlock* block);
bool isPotentiallyUndefinedAt(InternedString name, CFGBlock* block);
};
LivenessAnalysis* computeLivenessInfo(CFG*);
......
This diff is collapsed.
......@@ -16,6 +16,7 @@
#define PYSTON_ANALYSIS_SCOPINGANALYSIS_H
#include "core/common.h"
#include "core/stringpool.h"
namespace pyston {
......@@ -32,16 +33,12 @@ public:
virtual bool takesClosure() = 0;
virtual bool passesThroughClosure() = 0;
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 refersToGlobal(InternedString name) = 0;
virtual bool refersToClosure(InternedString name) = 0;
virtual bool saveInClosure(InternedString name) = 0;
// Get the names set within a classdef that should be forwarded on to
// the metaclass constructor.
// An error to call this on a non-classdef node.
virtual const std::unordered_set<std::string>& getClassDefLocalNames() = 0;
virtual std::string mangleName(const std::string& id) = 0;
virtual InternedString mangleName(InternedString id) = 0;
virtual InternedString internString(llvm::StringRef) = 0;
};
class ScopingAnalysis {
......@@ -52,6 +49,7 @@ public:
private:
std::unordered_map<AST*, ScopeInfo*> scopes;
AST_Module* parent_module;
InternedStringPool& interned_strings;
std::unordered_map<AST*, AST*> scope_replacements;
......@@ -70,6 +68,8 @@ public:
ScopingAnalysis(AST_Module* m);
ScopeInfo* getScopeInfoForNode(AST* node);
InternedStringPool& getInternedStrings();
};
ScopingAnalysis* runScopingAnalysis(AST_Module* m);
......
......@@ -36,17 +36,17 @@ namespace pyston {
class NullTypeAnalysis : public TypeAnalysis {
public:
ConcreteCompilerType* getTypeAtBlockStart(const std::string& name, CFGBlock* block) override;
ConcreteCompilerType* getTypeAtBlockEnd(const std::string& name, CFGBlock* block) override;
ConcreteCompilerType* getTypeAtBlockStart(InternedString name, CFGBlock* block) override;
ConcreteCompilerType* getTypeAtBlockEnd(InternedString name, CFGBlock* block) override;
BoxedClass* speculatedExprClass(AST_expr*) override { return NULL; }
};
ConcreteCompilerType* NullTypeAnalysis::getTypeAtBlockStart(const std::string& name, CFGBlock* block) {
ConcreteCompilerType* NullTypeAnalysis::getTypeAtBlockStart(InternedString name, CFGBlock* block) {
return UNKNOWN;
}
ConcreteCompilerType* NullTypeAnalysis::getTypeAtBlockEnd(const std::string& name, CFGBlock* block) {
ConcreteCompilerType* NullTypeAnalysis::getTypeAtBlockEnd(InternedString name, CFGBlock* block) {
assert(block->successors.size() > 0);
return getTypeAtBlockStart(name, block->successors[0]);
}
......@@ -68,7 +68,7 @@ static BoxedClass* simpleCallSpeculation(AST_Call* node, CompilerType* rtn_type,
return NULL;
}
if (node->func->type == AST_TYPE::Name && ast_cast<AST_Name>(node->func)->id == "xrange")
if (node->func->type == AST_TYPE::Name && ast_cast<AST_Name>(node->func)->id.str() == "xrange")
return xrange_cls;
// if (node->func->type == AST_TYPE::Attribute && ast_cast<AST_Attribute>(node->func)->attr == "dot")
......@@ -77,7 +77,7 @@ static BoxedClass* simpleCallSpeculation(AST_Call* node, CompilerType* rtn_type,
return NULL;
}
typedef std::unordered_map<std::string, CompilerType*> TypeMap;
typedef std::unordered_map<InternedString, CompilerType*> TypeMap;
typedef std::unordered_map<CFGBlock*, TypeMap> AllTypeMap;
typedef std::unordered_map<AST_expr*, CompilerType*> ExprTypeMap;
typedef std::unordered_map<AST_expr*, BoxedClass*> TypeSpeculations;
......@@ -140,7 +140,7 @@ private:
return rtn;
}
void _doSet(std::string target, CompilerType* t) {
void _doSet(InternedString target, CompilerType* t) {
if (t)
sym_table[target] = t;
}
......@@ -170,7 +170,7 @@ private:
void* visit_attribute(AST_Attribute* node) override {
CompilerType* t = getType(node->value);
CompilerType* rtn = t->getattrType(&node->attr, false);
CompilerType* rtn = t->getattrType(&node->attr.str(), false);
// if (speculation != TypeAnalysis::NONE && (node->attr == "x" || node->attr == "y" || node->attr == "z")) {
// rtn = processSpeculation(float_cls, node, rtn);
......@@ -192,7 +192,7 @@ private:
void* visit_clsattribute(AST_ClsAttribute* node) override {
CompilerType* t = getType(node->value);
CompilerType* rtn = t->getattrType(&node->attr, true);
CompilerType* rtn = t->getattrType(&node->attr.str(), true);
if (VERBOSITY() >= 2 && rtn == UNDEF) {
printf("Think %s.%s is undefined, at %d:%d\n", t->debugName().c_str(), node->attr.c_str(), node->lineno,
node->col_offset);
......@@ -288,9 +288,9 @@ private:
arg_types.push_back(getType(node->args[i]));
}
std::vector<std::pair<const std::string&, CompilerType*>> kw_types;
std::vector<std::pair<InternedString, CompilerType*>> kw_types;
for (AST_keyword* kw : node->keywords) {
kw_types.push_back(std::make_pair<const std::string&, CompilerType*>(kw->arg, getType(kw->value)));
kw_types.push_back(std::make_pair(kw->arg, getType(kw->value)));
}
CompilerType* starargs = node->starargs ? getType(node->starargs) : NULL;
......@@ -393,7 +393,7 @@ private:
void* visit_name(AST_Name* node) override {
if (scope_info->refersToGlobal(node->id)) {
if (node->id == "xrange") {
if (node->id.str() == "xrange") {
// printf("TODO guard here and return the classobj\n");
// return typeOfClassobj(xrange_cls);
}
......@@ -543,11 +543,11 @@ private:
// not part of the visitor api:
void _visit_alias(AST_alias* node) {
const std::string* name = &node->name;
if (node->asname.size())
name = &node->asname;
InternedString name = node->name;
if (node->asname.str().size())
name = node->asname;
_doSet(*name, UNKNOWN);
_doSet(name, UNKNOWN);
}
void visit_import(AST_Import* node) override {
......@@ -616,11 +616,11 @@ private:
speculation(speculation) {}
public:
ConcreteCompilerType* getTypeAtBlockEnd(const std::string& name, CFGBlock* block) override {
ConcreteCompilerType* getTypeAtBlockEnd(InternedString name, CFGBlock* block) override {
assert(block->successors.size() > 0);
return getTypeAtBlockStart(name, block->successors[0]);
}
ConcreteCompilerType* getTypeAtBlockStart(const std::string& name, CFGBlock* block) override {
ConcreteCompilerType* getTypeAtBlockStart(InternedString name, CFGBlock* block) override {
CompilerType* base = starting_types[block][name];
ASSERT(base != NULL, "%s %d", name.c_str(), block->idx);
......@@ -685,16 +685,16 @@ public:
int i = 0;
for (; i < arg_names.args.size(); i++) {
initial_types[arg_names.args[i]] = unboxedType(arg_types[i]);
initial_types[scope_info->internString(arg_names.args[i])] = unboxedType(arg_types[i]);
}
if (arg_names.vararg.size()) {
initial_types[arg_names.vararg] = unboxedType(arg_types[i]);
initial_types[scope_info->internString(arg_names.vararg)] = unboxedType(arg_types[i]);
i++;
}
if (arg_names.kwarg.size()) {
initial_types[arg_names.kwarg] = unboxedType(arg_types[i]);
initial_types[scope_info->internString(arg_names.kwarg)] = unboxedType(arg_types[i]);
i++;
}
......
......@@ -18,6 +18,7 @@
#include <unordered_map>
#include <vector>
#include "core/stringpool.h"
#include "core/types.h"
namespace pyston {
......@@ -36,12 +37,11 @@ public:
virtual ~TypeAnalysis() {}
virtual ConcreteCompilerType* getTypeAtBlockStart(const std::string& name, CFGBlock* block) = 0;
virtual ConcreteCompilerType* getTypeAtBlockEnd(const std::string& name, CFGBlock* block) = 0;
virtual ConcreteCompilerType* getTypeAtBlockStart(InternedString name, CFGBlock* block) = 0;
virtual ConcreteCompilerType* getTypeAtBlockEnd(InternedString name, CFGBlock* block) = 0;
virtual BoxedClass* speculatedExprClass(AST_expr*) = 0;
};
// TypeAnalysis* analyze(CFG *cfg, std::unordered_map<std::string, ConcreteCompilerType*> arg_types);
TypeAnalysis* doTypeAnalysis(CFG* cfg, const ParamNames& param_names,
const std::vector<ConcreteCompilerType*>& arg_types, EffortLevel::EffortLevel effort,
TypeAnalysis::SpeculationLevel speculation, ScopeInfo* scope_info);
......
This diff is collapsed.
......@@ -21,6 +21,7 @@ namespace gc {
class GCVisitor;
}
class AST_expr;
class AST_stmt;
class Box;
class BoxedDict;
......@@ -31,7 +32,8 @@ extern const void* interpreter_instr_addr;
Box* astInterpretFunction(CompiledFunction* f, int nargs, Box* closure, Box* generator, Box* arg1, Box* arg2, Box* arg3,
Box** args);
Box* astInterpretFrom(CompiledFunction* cf, AST_stmt* start_at, BoxedDict* locals);
Box* astInterpretFrom(CompiledFunction* cf, AST_expr* after_expr, AST_stmt* enclosing_stmt, Box* expr_val,
BoxedDict* locals);
AST_stmt* getCurrentStatementForInterpretedFrame(void* frame_ptr);
CompiledFunction* getCFForInterpretedFrame(void* frame_ptr);
......
......@@ -1068,6 +1068,13 @@ public:
return boolFromI1(emitter, cmp);
}
CompilerVariable* getitem(IREmitter& emitter, const OpInfo& info, VAR* var, CompilerVariable* slice) override {
ConcreteCompilerVariable* converted = var->makeConverted(emitter, BOXED_FLOAT);
CompilerVariable* rtn = converted->getitem(emitter, info, slice);
converted->decvref(emitter);
return rtn;
}
CompilerVariable* binexp(IREmitter& emitter, const OpInfo& info, VAR* var, CompilerVariable* rhs,
AST_TYPE::AST_TYPE op_type, BinExpType exp_type) override {
if (rhs->getType() != INT && rhs->getType() != FLOAT) {
......@@ -1366,21 +1373,21 @@ public:
const std::string* attr, bool clsonly, ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names,
bool raise_on_missing = true) {
bool* no_attribute = NULL) {
if (!canStaticallyResolveGetattrs())
return NULL;
Box* rtattr = cls->getattr(*attr);
if (rtattr == NULL) {
if (raise_on_missing) {
if (no_attribute) {
*no_attribute = true;
} else {
llvm::CallSite call = emitter.createCall2(info.unw_info, g.funcs.raiseAttributeErrorStr,
getStringConstantPtr(*getNameOfClass(cls) + "\0"),
getStringConstantPtr(*attr + '\0'));
call.setDoesNotReturn();
return undefVariable();
} else {
return NULL;
}
return undefVariable();
}
if (rtattr->cls != function_cls)
......@@ -1459,6 +1466,7 @@ public:
ConcreteCompilerVariable* rtn = _call(emitter, info, linked_function, cf->code, other_args, argspec, new_args,
keyword_names, cf->spec->rtn_type);
assert(rtn->getType() == cf->spec->rtn_type);
assert(rtn->getType() != UNDEF);
// We should provide unboxed versions of these rather than boxing then unboxing:
// TODO is it more efficient to unbox here, or should we leave it boxed?
......@@ -1509,8 +1517,21 @@ public:
const std::string& left_side_name = getOpName(op_type);
ConcreteCompilerVariable* called_constant = tryCallattrConstant(
emitter, info, var, &left_side_name, true, ArgPassSpec(1, 0, 0, 0), { converted_rhs }, NULL, false);
bool no_attribute = false;
ConcreteCompilerVariable* called_constant
= tryCallattrConstant(emitter, info, var, &left_side_name, true, ArgPassSpec(1, 0, 0, 0),
{ converted_rhs }, NULL, &no_attribute);
if (no_attribute) {
assert(called_constant->getType() == UNDEF);
// Kind of hacky, but just call into getitem like normal. except...
auto r = UNKNOWN->binexp(emitter, info, var, converted_rhs, op_type, exp_type);
r->decvref(emitter);
// ... return the undef value, since that matches what the type analyzer thought we would do.
return called_constant;
}
if (called_constant) {
converted_rhs->decvref(emitter);
return called_constant;
......@@ -1525,8 +1546,20 @@ public:
CompilerVariable* getitem(IREmitter& emitter, const OpInfo& info, VAR* var, CompilerVariable* slice) override {
static const std::string attr("__getitem__");
ConcreteCompilerVariable* called_constant
= tryCallattrConstant(emitter, info, var, &attr, true, ArgPassSpec(1, 0, 0, 0), { slice }, NULL, false);
bool no_attribute = false;
ConcreteCompilerVariable* called_constant = tryCallattrConstant(
emitter, info, var, &attr, true, ArgPassSpec(1, 0, 0, 0), { slice }, NULL, &no_attribute);
if (no_attribute) {
assert(called_constant->getType() == UNDEF);
// Kind of hacky, but just call into getitem like normal. except...
auto r = UNKNOWN->getitem(emitter, info, var, slice);
r->decvref(emitter);
// ... return the undef value, since that matches what the type analyzer thought we would do.
return called_constant;
}
if (called_constant)
return called_constant;
......
......@@ -272,14 +272,14 @@ computeBlockTraversalOrder(const BlockSet& full_blocks, const BlockSet& partial_
return rtn;
}
static ConcreteCompilerType* getTypeAtBlockStart(TypeAnalysis* types, const std::string& name, CFGBlock* block) {
if (isIsDefinedName(name))
static ConcreteCompilerType* getTypeAtBlockStart(TypeAnalysis* types, InternedString name, CFGBlock* block) {
if (isIsDefinedName(name.str()))
return BOOL;
else if (name == PASSED_GENERATOR_NAME)
else if (name.str() == PASSED_GENERATOR_NAME)
return GENERATOR;
else if (name == PASSED_CLOSURE_NAME)
else if (name.str() == PASSED_CLOSURE_NAME)
return CLOSURE;
else if (name == CREATED_CLOSURE_NAME)
else if (name.str() == CREATED_CLOSURE_NAME)
return CLOSURE;
else
return types->getTypeAtBlockStart(name, block);
......@@ -450,9 +450,10 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
}
ASSERT(speculated_class, "%s", phi_type->debugName().c_str());
assert(p.first[0] != '!');
assert(p.first.str()[0] != '!');
std::string is_defined_name = getIsDefinedName(p.first);
// TODO cache this
InternedString is_defined_name = getIsDefinedName(p.first, source->getInternedStrings());
llvm::Value* prev_guard_val = NULL;
ConcreteCompilerVariable* is_defined_var = NULL;
......@@ -507,7 +508,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
}
if (VERBOSITY("irgen"))
v->setName("prev_" + p.first);
v->setName("prev_" + p.first.str());
(*osr_syms)[p.first] = new ConcreteCompilerVariable(phi_type, v, true);
}
......@@ -543,7 +544,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
std::unordered_map<CFGBlock*, SymbolTable*> ending_symbol_tables;
std::unordered_map<CFGBlock*, ConcreteSymbolTable*> phi_ending_symbol_tables;
typedef std::unordered_map<std::string, std::pair<ConcreteCompilerType*, llvm::PHINode*>> PHITable;
typedef std::unordered_map<InternedString, std::pair<ConcreteCompilerType*, llvm::PHINode*>> PHITable;
std::unordered_map<CFGBlock*, PHITable*> created_phis;
CFGBlock* initial_block = NULL;
......@@ -680,7 +681,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
// analyzed_type->debugName().c_str());
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(analyzed_type->llvmType(),
block->predecessors.size() + 1, p.first);
block->predecessors.size() + 1, p.first.str());
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(analyzed_type, phi, true);
generator->giveLocalSymbol(p.first, var);
(*phis)[p.first] = std::make_pair(analyzed_type, phi);
......@@ -696,27 +697,28 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
}
std::unordered_set<std::string> names;
std::unordered_set<InternedString> names;
for (const auto& s : source->phis->getAllRequiredFor(block)) {
names.insert(s);
if (source->phis->isPotentiallyUndefinedAfter(s, block->predecessors[0])) {
names.insert(getIsDefinedName(s));
names.insert(getIsDefinedName(s, source->getInternedStrings()));
}
}
if (source->getScopeInfo()->createsClosure())
names.insert(CREATED_CLOSURE_NAME);
names.insert(source->getInternedStrings().get(CREATED_CLOSURE_NAME));
if (source->getScopeInfo()->takesClosure())
names.insert(PASSED_CLOSURE_NAME);
names.insert(source->getInternedStrings().get(PASSED_CLOSURE_NAME));
if (source->is_generator)
names.insert(PASSED_GENERATOR_NAME);
names.insert(source->getInternedStrings().get(PASSED_GENERATOR_NAME));
for (const auto& s : names) {
// printf("adding guessed phi for %s\n", s.c_str());
ConcreteCompilerType* type = getTypeAtBlockStart(types, s, block);
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(type->llvmType(), block->predecessors.size(), s);
llvm::PHINode* phi
= emitter->getBuilder()->CreatePHI(type->llvmType(), block->predecessors.size(), s.str());
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(type, phi, true);
generator->giveLocalSymbol(s, var);
......@@ -776,7 +778,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
for (ConcreteSymbolTable::iterator it = pred_st->begin(); it != pred_st->end(); it++) {
// printf("adding phi for %s\n", it->first.c_str());
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(it->second->getType()->llvmType(),
block->predecessors.size(), it->first);
block->predecessors.size(), it->first.str());
// emitter->getBuilder()->CreateCall(g.funcs.dump, phi);
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(it->second->getType(), phi, true);
generator->giveLocalSymbol(it->first, var);
......@@ -882,7 +884,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
llvm_phi->addIncoming(v->getValue(), osr_unbox_block_end);
}
std::string is_defined_name = getIsDefinedName(it->first);
InternedString is_defined_name = getIsDefinedName(it->first, source->getInternedStrings());
for (int i = 0; i < block_guards.size(); i++) {
GuardList::BlockEntryGuard* guard = block_guards[i];
......
......@@ -91,7 +91,7 @@ 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);
InternedString getIsDefinedName(InternedString name, InternedStringPool& interned_strings);
bool isIsDefinedName(const std::string& name);
CompiledFunction* doCompile(SourceInfo* source, ParamNames* param_names, const OSREntryDescriptor* entry_descriptor,
......
......@@ -69,7 +69,7 @@ void raiseFutureImportErrorNotBeginning(const char* file, AST* node) {
class BadFutureImportVisitor : public NoopASTVisitor {
public:
virtual bool visit_importfrom(AST_ImportFrom* node) {
if (node->module == "__future__") {
if (node->module.str() == "__future__") {
raiseFutureImportErrorNotBeginning(file, node);
}
return true;
......@@ -103,14 +103,14 @@ FutureFlags getFutureFlags(AST_Module* m, const char* file) {
for (int i = 0; i < m->body.size(); i++) {
AST_stmt* stmt = m->body[i];
if (stmt->type == AST_TYPE::ImportFrom && static_cast<AST_ImportFrom*>(stmt)->module == "__future__") {
if (stmt->type == AST_TYPE::ImportFrom && static_cast<AST_ImportFrom*>(stmt)->module.str() == "__future__") {
if (future_import_allowed) {
// We have a `from __future__` import statement, and we are
// still at the top of the file, so just set the appropriate
// future flag for each imported option.
for (AST_alias* alias : static_cast<AST_ImportFrom*>(stmt)->names) {
const std::string& option_name = alias->name;
const std::string& option_name = alias->name.str();
auto iter = future_options.find(option_name);
if (iter == future_options.end()) {
// If it's not one of the available options, throw an error.
......
......@@ -52,40 +52,44 @@ ParamNames::ParamNames(AST* ast) : takes_param_names(true) {
for (int i = 0; i < arguments->args.size(); i++) {
AST_expr* arg = arguments->args[i];
if (arg->type == AST_TYPE::Name) {
args.push_back(ast_cast<AST_Name>(arg)->id);
args.push_back(ast_cast<AST_Name>(arg)->id.str());
} else {
args.push_back("." + std::to_string(i + 1));
}
}
vararg = arguments->vararg;
kwarg = arguments->kwarg;
vararg = arguments->vararg.str();
kwarg = arguments->kwarg.str();
} else {
RELEASE_ASSERT(0, "%d", ast->type);
}
}
ParamNames::ParamNames(const std::vector<std::string>& args, const std::string& vararg, const std::string& kwarg)
ParamNames::ParamNames(const std::vector<llvm::StringRef>& args, llvm::StringRef vararg, llvm::StringRef kwarg)
: takes_param_names(true) {
this->args = args;
this->vararg = vararg;
this->kwarg = kwarg;
}
std::string SourceInfo::mangleName(const std::string& id) {
InternedString SourceInfo::mangleName(InternedString id) {
assert(ast);
if (ast->type == AST_TYPE::Module)
return id;
return getScopeInfo()->mangleName(id);
}
InternedStringPool& SourceInfo::getInternedStrings() {
return scoping->getInternedStrings();
}
const std::string SourceInfo::getName() {
assert(ast);
switch (ast->type) {
case AST_TYPE::ClassDef:
return ast_cast<AST_ClassDef>(ast)->name;
return ast_cast<AST_ClassDef>(ast)->name.str();
case AST_TYPE::FunctionDef:
return ast_cast<AST_FunctionDef>(ast)->name;
return ast_cast<AST_FunctionDef>(ast)->name.str();
case AST_TYPE::Lambda:
return "<lambda>";
case AST_TYPE::Module:
......@@ -290,6 +294,42 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
((void (*)())cf->code)();
}
// If a function version keeps failing its speculations, kill it (remove it
// from the list of valid function versions). The next time we go to call
// the function, we will have to pick a different version, potentially recompiling.
//
// TODO we should have logic like this at the CLFunc level that detects that we keep
// on creating functions with failing speculations, and then stop speculating.
void CompiledFunction::speculationFailed() {
LOCK_REGION(codegen_rwlock.asWrite());
this->times_speculation_failed++;
if (this->times_speculation_failed >= 4) {
// printf("Killing %p because it failed too many speculations\n", this);
CLFunction* cl = this->clfunc;
assert(cl);
bool found = false;
for (int i = 0; i < clfunc->versions.size(); i++) {
if (clfunc->versions[i] == this) {
clfunc->versions.erase(clfunc->versions.begin() + i);
this->dependent_callsites.invalidateAll();
found = true;
break;
}
}
if (!found) {
for (int i = 0; i < clfunc->versions.size(); i++) {
printf("%p\n", clfunc->versions[i]);
}
}
assert(found);
}
}
/// Reoptimizes the given function version at the new effort level.
/// The cf must be an active version in its parents CLFunction; the given
/// version will be replaced by the new version, which will be returned.
......@@ -387,8 +427,8 @@ CLFunction* createRTFunction(int num_args, int num_defaults, bool takes_varargs,
CLFunction* boxRTFunction(void* f, ConcreteCompilerType* rtn_type, int num_args, const ParamNames& param_names) {
assert(!param_names.takes_param_names || num_args == param_names.args.size());
assert(param_names.vararg == "");
assert(param_names.kwarg == "");
assert(param_names.vararg.str() == "");
assert(param_names.kwarg.str() == "");
return boxRTFunction(f, rtn_type, num_args, 0, false, false, param_names);
}
......@@ -396,8 +436,8 @@ CLFunction* boxRTFunction(void* f, ConcreteCompilerType* rtn_type, int num_args,
CLFunction* boxRTFunction(void* f, ConcreteCompilerType* rtn_type, int num_args, int num_defaults, bool takes_varargs,
bool takes_kwargs, const ParamNames& param_names) {
assert(!param_names.takes_param_names || num_args == param_names.args.size());
assert(takes_varargs || param_names.vararg == "");
assert(takes_kwargs || param_names.kwarg == "");
assert(takes_varargs || param_names.vararg.str() == "");
assert(takes_kwargs || param_names.kwarg.str() == "");
CLFunction* cl_f = createRTFunction(num_args, num_defaults, takes_varargs, takes_kwargs, param_names);
......
This diff is collapsed.
......@@ -20,6 +20,7 @@
#include "llvm/ADT/iterator_range.h"
#include "llvm/IR/Instructions.h"
#include "core/stringpool.h"
#include "core/types.h"
namespace llvm {
......@@ -38,9 +39,9 @@ struct PatchpointInfo;
class ScopeInfo;
class TypeAnalysis;
typedef std::unordered_map<std::string, CompilerVariable*> SymbolTable;
typedef std::map<std::string, CompilerVariable*> SortedSymbolTable;
typedef std::unordered_map<std::string, ConcreteCompilerVariable*> ConcreteSymbolTable;
typedef std::unordered_map<InternedString, CompilerVariable*> SymbolTable;
typedef std::map<InternedString, CompilerVariable*> SortedSymbolTable;
typedef std::unordered_map<InternedString, ConcreteCompilerVariable*> ConcreteSymbolTable;
extern const std::string CREATED_CLOSURE_NAME;
extern const std::string PASSED_CLOSURE_NAME;
......@@ -162,6 +163,7 @@ public:
void addExprTypeGuard(CFGBlock* cfg_block, llvm::BranchInst* branch, AST_expr* ast_node, CompilerVariable* val,
const SymbolTable& st) {
abort();
ExprTypeGuard*& g = expr_type_guards[ast_node];
assert(g == NULL);
g = new ExprTypeGuard(cfg_block, branch, ast_node, val, st);
......@@ -200,7 +202,7 @@ public:
virtual void doFunctionEntry(const ParamNames& param_names, const std::vector<ConcreteCompilerType*>& arg_types)
= 0;
virtual void giveLocalSymbol(const std::string& name, CompilerVariable* var) = 0;
virtual void giveLocalSymbol(InternedString name, CompilerVariable* var) = 0;
virtual void copySymbolsFrom(SymbolTable* st) = 0;
virtual void run(const CFGBlock* block) = 0;
virtual EndingState getEndingSymbolTable() = 0;
......
......@@ -17,6 +17,8 @@
#include <vector>
#include "core/stringpool.h"
namespace llvm {
class Function;
}
......@@ -33,7 +35,7 @@ private:
public:
CompiledFunction* const cf;
AST_Jump* const backedge;
typedef std::map<std::string, ConcreteCompilerType*> ArgMap;
typedef std::map<InternedString, ConcreteCompilerType*> ArgMap;
ArgMap args;
static OSREntryDescriptor* create(CompiledFunction* from_cf, AST_Jump* backedge) {
......
......@@ -43,6 +43,8 @@ private:
int start, end;
FILE* fp;
InternedStringPool* intern_pool;
void ensure(int num) {
if (end - start < num) {
fill();
......@@ -59,7 +61,7 @@ public:
printf("filled, now at %d-%d\n", start, end);
}
BufferedReader(FILE* fp) : start(0), end(0), fp(fp) {}
BufferedReader(FILE* fp) : start(0), end(0), fp(fp), intern_pool(NULL) {}
int bytesBuffered() { return (end - start); }
......@@ -81,8 +83,18 @@ public:
raw = readULL();
return d;
}
std::unique_ptr<InternedStringPool> createInternedPool();
InternedString readAndInternString();
void readAndInternStringVector(std::vector<InternedString>& v);
};
std::unique_ptr<InternedStringPool> BufferedReader::createInternedPool() {
assert(this->intern_pool == NULL);
this->intern_pool = new InternedStringPool();
return std::unique_ptr<InternedStringPool>(this->intern_pool);
}
AST* readASTMisc(BufferedReader* reader);
AST_expr* readASTExpr(BufferedReader* reader);
AST_stmt* readASTStmt(BufferedReader* reader);
......@@ -96,6 +108,20 @@ static std::string readString(BufferedReader* reader) {
return std::string(chars.begin(), chars.end());
}
InternedString BufferedReader::readAndInternString() {
std::string str = readString(this);
return intern_pool->get(std::move(str));
}
void BufferedReader::readAndInternStringVector(std::vector<InternedString>& v) {
int num_elts = readShort();
if (VERBOSITY("parsing") >= 2)
printf("%d elts to read\n", num_elts);
for (int i = 0; i < num_elts; i++) {
v.push_back(readAndInternString());
}
}
static void readStringVector(std::vector<std::string>& vec, BufferedReader* reader) {
int num_elts = reader->readShort();
if (VERBOSITY("parsing") >= 2)
......@@ -142,8 +168,8 @@ static int readColOffset(BufferedReader* reader) {
}
AST_alias* read_alias(BufferedReader* reader) {
std::string asname = readString(reader);
std::string name = readString(reader);
InternedString asname = reader->readAndInternString();
InternedString name = reader->readAndInternString();
AST_alias* rtn = new AST_alias(name, asname);
rtn->col_offset = -1;
......@@ -155,14 +181,15 @@ AST_alias* read_alias(BufferedReader* reader) {
AST_arguments* read_arguments(BufferedReader* reader) {
if (VERBOSITY("parsing") >= 2)
printf("reading arguments\n");
AST_arguments* rtn = new AST_arguments();
readExprVector(rtn->args, reader);
rtn->col_offset = -1;
readExprVector(rtn->defaults, reader);
rtn->kwarg = readString(reader);
rtn->kwarg = reader->readAndInternString();
rtn->lineno = -1;
rtn->vararg = readString(reader);
rtn->vararg = reader->readAndInternString();
return rtn;
}
......@@ -200,7 +227,7 @@ AST_AugAssign* read_augassign(BufferedReader* reader) {
AST_Attribute* read_attribute(BufferedReader* reader) {
AST_Attribute* rtn = new AST_Attribute();
rtn->attr = readString(reader);
rtn->attr = reader->readAndInternString();
rtn->col_offset = readColOffset(reader);
rtn->ctx_type = (AST_TYPE::AST_TYPE)reader->readByte();
rtn->lineno = reader->readULL();
......@@ -293,7 +320,7 @@ AST_ClassDef* read_classdef(BufferedReader* reader) {
rtn->col_offset = readColOffset(reader);
readExprVector(rtn->decorator_list, reader);
rtn->lineno = reader->readULL();
rtn->name = readString(reader);
rtn->name = reader->readAndInternString();
return rtn;
}
......@@ -395,7 +422,7 @@ AST_FunctionDef* read_functiondef(BufferedReader* reader) {
rtn->col_offset = readColOffset(reader);
readExprVector(rtn->decorator_list, reader);
rtn->lineno = reader->readULL();
rtn->name = readString(reader);
rtn->name = reader->readAndInternString();
return rtn;
}
......@@ -414,7 +441,7 @@ AST_Global* read_global(BufferedReader* reader) {
rtn->col_offset = readColOffset(reader);
rtn->lineno = reader->readULL();
readStringVector(rtn->names, reader);
reader->readAndInternStringVector(rtn->names);
return rtn;
}
......@@ -455,7 +482,7 @@ AST_ImportFrom* read_importfrom(BufferedReader* reader) {
rtn->col_offset = readColOffset(reader);
rtn->level = reader->readULL();
rtn->lineno = reader->readULL();
rtn->module = readString(reader);
rtn->module = reader->readAndInternString();
readMiscVector(rtn->names, reader);
return rtn;
}
......@@ -473,7 +500,7 @@ AST_Index* read_index(BufferedReader* reader) {
AST_keyword* read_keyword(BufferedReader* reader) {
AST_keyword* rtn = new AST_keyword();
rtn->arg = readString(reader);
rtn->arg = reader->readAndInternString();
rtn->col_offset = -1;
rtn->lineno = -1;
rtn->value = readASTExpr(reader);
......@@ -513,7 +540,8 @@ AST_ListComp* read_listcomp(BufferedReader* reader) {
AST_Module* read_module(BufferedReader* reader) {
if (VERBOSITY("parsing") >= 2)
printf("reading module\n");
AST_Module* rtn = new AST_Module();
AST_Module* rtn = new AST_Module(reader->createInternedPool());
readStmtVector(rtn->body, reader);
rtn->col_offset = -1;
......@@ -524,7 +552,7 @@ AST_Module* read_module(BufferedReader* reader) {
AST_Name* read_name(BufferedReader* reader) {
auto col_offset = readColOffset(reader);
auto ctx_type = (AST_TYPE::AST_TYPE)reader->readByte();
auto id = readString(reader);
auto id = reader->readAndInternString();
auto lineno = reader->readULL();
return new AST_Name(std::move(id), ctx_type, lineno, col_offset);
......
This diff is collapsed.
......@@ -248,6 +248,7 @@ void initGlobalFuncs(GlobalState& g) {
g.funcs.__cxa_end_catch = addFunc((void*)__cxa_end_catch, g.void_);
GET(raise0);
GET(raise3);
GET(deopt);
GET(div_float_float);
GET(floordiv_float_float);
......
......@@ -48,6 +48,7 @@ struct GlobalFuncs {
llvm::Value* __cxa_begin_catch, *__cxa_end_catch;
llvm::Value* raise0, *raise3;
llvm::Value* deopt;
llvm::Value* div_float_float, *floordiv_float_float, *mod_float_float, *pow_float_float;
......
......@@ -28,6 +28,7 @@
#include "codegen/compvars.h"
#include "codegen/irgen/hooks.h"
#include "codegen/stackmaps.h"
#include "core/util.h"
#include "runtime/types.h"
......@@ -526,8 +527,38 @@ BoxedDict* getLocals(bool only_user_visible) {
unsigned offset = ip - cf->code_start;
assert(cf->location_map);
// We have to detect + ignore any entries for variables that
// could have been defined (so they have entries) but aren't (so the
// entries point to uninitialized memory).
std::unordered_set<std::string> is_undefined;
for (const auto& p : cf->location_map->names) {
if (!startswith(p.first, "!is_defined_"))
continue;
for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) {
if (e.offset < offset && offset <= e.offset + e.length) {
const auto& locs = e.locations;
assert(locs.size() == 1);
uint64_t v = frame_info.readLocation(locs[0]);
if ((v & 1) == 0)
is_undefined.insert(p.first.substr(12));
break;
}
}
}
for (const auto& p : cf->location_map->names) {
if (only_user_visible && (p.first[0] == '#' || p.first[0] == '!'))
if (p.first[0] == '!')
continue;
if (only_user_visible && p.first[0] == '#')
continue;
if (is_undefined.count(p.first))
continue;
for (const LocationMap::LocationTable::LocationEntry& e : p.second.locations) {
......@@ -552,13 +583,18 @@ BoxedDict* getLocals(bool only_user_visible) {
return d;
} else if (frame_info.getId().type == PythonFrameId::INTERPRETED) {
return localsForInterpretedFrame((void*)frame_info.getId().bp, only_user_visible);
} else {
abort();
}
abort();
}
RELEASE_ASSERT(0, "Internal error: unable to find any python frames");
}
ExecutionPoint getExecutionPoint() {
auto frame = getTopPythonFrame();
auto cf = frame->getCF();
auto current_stmt = frame->getCurrentStatement();
return ExecutionPoint({.cf = cf, .current_stmt = current_stmt });
}
llvm::JITEventListener* makeTracebacksListener() {
return new TracebacksEventListener();
......
......@@ -32,6 +32,12 @@ BoxedDict* getLocals(bool only_user_visible);
// Fetches a writeable pointer to the frame-local excinfo object,
// calculating it if necessary (from previous frames).
ExcInfo* getFrameExcInfo();
struct ExecutionPoint {
CompiledFunction* cf;
AST_stmt* current_stmt;
};
ExecutionPoint getExecutionPoint();
}
#endif
......@@ -967,7 +967,7 @@ void PrintVisitor::printIndent() {
bool PrintVisitor::visit_alias(AST_alias* node) {
printf("%s", node->name.c_str());
if (node->asname.size())
if (node->asname.str().size())
printf(" as %s", node->asname.c_str());
return true;
}
......
......@@ -24,6 +24,7 @@
#include "llvm/ADT/StringRef.h"
#include "core/common.h"
#include "core/stringpool.h"
namespace pyston {
......@@ -181,11 +182,11 @@ public:
class AST_alias : public AST {
public:
std::string name, asname;
InternedString name, asname;
virtual void accept(ASTVisitor* v);
AST_alias(const std::string& name, const std::string& asname) : AST(AST_TYPE::alias), name(name), asname(asname) {}
AST_alias(InternedString name, InternedString asname) : AST(AST_TYPE::alias), name(name), asname(asname) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::alias;
};
......@@ -197,7 +198,7 @@ public:
// These are represented as strings, not names; not sure why.
// If they don't exist, the string is empty.
std::string kwarg, vararg;
InternedString kwarg, vararg;
virtual void accept(ASTVisitor* v);
......@@ -262,14 +263,14 @@ class AST_Attribute : public AST_expr {
public:
AST_expr* value;
AST_TYPE::AST_TYPE ctx_type;
std::string attr;
InternedString attr;
virtual void accept(ASTVisitor* v);
virtual void* accept_expr(ExprVisitor* v);
AST_Attribute() : AST_expr(AST_TYPE::Attribute) {}
AST_Attribute(AST_expr* value, AST_TYPE::AST_TYPE ctx_type, const std::string& attr)
AST_Attribute(AST_expr* value, AST_TYPE::AST_TYPE ctx_type, InternedString attr)
: AST_expr(AST_TYPE::Attribute), value(value), ctx_type(ctx_type), attr(attr) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Attribute;
......@@ -359,7 +360,7 @@ public:
std::vector<AST_expr*> bases, decorator_list;
std::vector<AST_stmt*> body;
std::string name;
InternedString name;
AST_ClassDef() : AST_stmt(AST_TYPE::ClassDef) {}
......@@ -490,7 +491,7 @@ class AST_FunctionDef : public AST_stmt {
public:
std::vector<AST_stmt*> body;
std::vector<AST_expr*> decorator_list;
std::string name;
InternedString name;
AST_arguments* args;
virtual void accept(ASTVisitor* v);
......@@ -516,7 +517,7 @@ public:
class AST_Global : public AST_stmt {
public:
std::vector<std::string> names;
std::vector<InternedString> names;
virtual void accept(ASTVisitor* v);
virtual void accept_stmt(StmtVisitor* v);
......@@ -565,7 +566,7 @@ public:
class AST_ImportFrom : public AST_stmt {
public:
std::string module;
InternedString module;
std::vector<AST_alias*> names;
int level;
......@@ -593,7 +594,7 @@ class AST_keyword : public AST {
public:
// no lineno, col_offset attributes
AST_expr* value;
std::string arg;
InternedString arg;
virtual void accept(ASTVisitor* v);
......@@ -643,12 +644,15 @@ public:
class AST_Module : public AST {
public:
std::unique_ptr<InternedStringPool> interned_strings;
// no lineno, col_offset attributes
std::vector<AST_stmt*> body;
virtual void accept(ASTVisitor* v);
AST_Module() : AST(AST_TYPE::Module) {}
AST_Module(std::unique_ptr<InternedStringPool> interned_strings)
: AST(AST_TYPE::Module), interned_strings(std::move(interned_strings)) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Module;
};
......@@ -656,12 +660,12 @@ public:
class AST_Name : public AST_expr {
public:
AST_TYPE::AST_TYPE ctx_type;
std::string id;
InternedString id;
virtual void accept(ASTVisitor* v);
virtual void* accept_expr(ExprVisitor* v);
AST_Name(const std::string& id, AST_TYPE::AST_TYPE ctx_type, int lineno, int col_offset = 0)
AST_Name(InternedString id, AST_TYPE::AST_TYPE ctx_type, int lineno, int col_offset = 0)
: AST_expr(AST_TYPE::Name, lineno, col_offset), ctx_type(ctx_type), id(id) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Name;
......@@ -939,7 +943,7 @@ public:
class AST_ClsAttribute : public AST_expr {
public:
AST_expr* value;
std::string attr;
InternedString attr;
virtual void accept(ASTVisitor* v);
virtual void* accept_expr(ExprVisitor* v);
......
This diff is collapsed.
......@@ -31,6 +31,7 @@
#include "core/ast.h"
#include "core/common.h"
#include "core/stringpool.h"
namespace pyston {
......
// 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_CORE_STRINGPOOL_H
#define PYSTON_CORE_STRINGPOOL_H
#include <algorithm>
#include <cstdio>
#include <sys/time.h>
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringRef.h"
#include "core/common.h"
namespace std {
template <> struct hash<llvm::StringRef> {
size_t operator()(const llvm::StringRef s) const { return llvm::hash_value(s); }
};
}
namespace pyston {
class InternedStringPool;
class InternedString {
private:
const std::string* _str;
#ifndef NDEBUG
// Only for testing purposes:
InternedStringPool* pool;
InternedString(const std::string* str, InternedStringPool* pool) : _str(str), pool(pool) {}
#else
InternedString(const std::string* str) : _str(str) {}
#endif
public:
#ifndef NDEBUG
InternedString() : _str(NULL), pool(NULL) {}
#else
InternedString() : _str(NULL) {}
#endif
// operator const std::string&() { return *_str; }
const std::string& str() const {
assert(this->_str);
return *_str;
}
const char* c_str() const {
assert(this->_str);
return _str->c_str();
}
bool operator==(InternedString rhs) const {
assert(this->_str);
assert(this->pool == rhs.pool);
return this->_str == rhs._str;
}
bool operator<(InternedString rhs) const {
assert(this->_str);
assert(this->pool == rhs.pool);
return this->_str < rhs._str;
}
friend class InternedStringPool;
friend struct std::hash<InternedString>;
friend struct std::less<InternedString>;
};
class InternedStringPool {
private:
// We probably don't need to pull in llvm::StringRef as the key, but it's better than std::string
// which I assume forces extra allocations.
// (We could define a custom string-pointer container but is it worth it?)
std::unordered_map<llvm::StringRef, std::string*> interned;
public:
~InternedStringPool() {
for (auto& p : interned) {
delete p.second;
}
}
template <class T> InternedString get(T&& arg) {
auto it = interned.find(llvm::StringRef(arg));
std::string* s;
if (it != interned.end()) {
s = it->second;
} else {
s = new std::string(std::forward<T>(arg));
interned.insert(it, std::make_pair(llvm::StringRef(*s), s));
}
#ifndef NDEBUG
return InternedString(s, this);
#else
return InternedString(s);
#endif
}
};
} // namespace pyston
namespace std {
template <> struct hash<pyston::InternedString> {
size_t operator()(const pyston::InternedString s) const { return reinterpret_cast<intptr_t>(s._str) >> 3; }
};
template <> struct less<pyston::InternedString> {
bool operator()(const pyston::InternedString lhs, const pyston::InternedString rhs) const {
assert(lhs.pool && lhs.pool == rhs.pool);
// TODO: we should be able to do this comparison on the pointer value, not on the string value,
// but there are apparently parts of the code that rely on string sorting being actually alphabetical.
// We could create a faster "consistent ordering but not alphabetical" comparator if it makes a difference.
return *lhs._str < *rhs._str;
}
};
}
#endif
......@@ -28,6 +28,7 @@
#include "core/common.h"
#include "core/stats.h"
#include "core/stringpool.h"
namespace llvm {
class Function;
......@@ -192,7 +193,7 @@ public:
EffortLevel::EffortLevel effort;
int64_t times_called;
int64_t times_called, times_speculation_failed;
ICInvalidator dependent_callsites;
LocationMap* location_map; // only meaningful if this is a compiled frame
......@@ -203,25 +204,31 @@ public:
llvm::Value* llvm_code, EffortLevel::EffortLevel effort,
const OSREntryDescriptor* entry_descriptor)
: clfunc(NULL), func(func), spec(spec), entry_descriptor(entry_descriptor), is_interpreted(is_interpreted),
code(code), llvm_code(llvm_code), effort(effort), times_called(0), location_map(nullptr) {}
code(code), llvm_code(llvm_code), effort(effort), times_called(0), times_speculation_failed(0),
location_map(nullptr) {}
// TODO this will need to be implemented eventually; things to delete:
// - line_table if it exists
// - location_map if it exists
// - all entries in ics (after deregistering them)
~CompiledFunction();
// Call this when a speculation inside this version failed
void speculationFailed();
};
struct ParamNames {
bool takes_param_names;
std::vector<std::string> args;
std::string vararg, kwarg;
std::vector<llvm::StringRef> args;
llvm::StringRef vararg, kwarg;
explicit ParamNames(AST* ast);
ParamNames(const std::vector<std::string>& args, const std::string& vararg, const std::string& kwarg);
ParamNames(const std::vector<llvm::StringRef>& args, llvm::StringRef vararg, llvm::StringRef kwarg);
static ParamNames empty() { return ParamNames(); }
int totalParameters() const { return args.size() + (vararg.size() == 0 ? 0 : 1) + (kwarg.size() == 0 ? 0 : 1); }
int totalParameters() const {
return args.size() + (vararg.str().size() == 0 ? 0 : 1) + (kwarg.str().size() == 0 ? 0 : 1);
}
private:
ParamNames() : takes_param_names(false) {}
......@@ -229,6 +236,7 @@ private:
class BoxedModule;
class ScopeInfo;
class InternedStringPool;
class SourceInfo {
public:
BoxedModule* parent_module;
......@@ -239,6 +247,8 @@ public:
PhiAnalysis* phis;
bool is_generator;
InternedStringPool& getInternedStrings();
ScopeInfo* getScopeInfo();
// TODO we're currently copying the body of the AST into here, since lambdas don't really have a statement-based
......@@ -246,7 +256,7 @@ public:
const std::vector<AST_stmt*> body;
const std::string getName();
std::string mangleName(const std::string& id);
InternedString mangleName(InternedString id);
SourceInfo(BoxedModule* m, ScopingAnalysis* scoping, AST* ast, const std::vector<AST_stmt*>& body);
};
......@@ -357,17 +367,17 @@ namespace gc {
enum class GCKind : uint8_t {
PYTHON = 1,
CONSERVATIVE = 2,
UNTRACKED = 3,
PRECISE = 3,
UNTRACKED = 4,
HIDDEN_CLASS = 5,
};
extern "C" void* gc_alloc(size_t nbytes, GCKind kind);
}
class ConservativeGCObject {
template <gc::GCKind gc_kind> class GCAllocated {
public:
void* operator new(size_t size) __attribute__((visibility("default"))) {
return gc_alloc(size, gc::GCKind::CONSERVATIVE);
}
void* operator new(size_t size) __attribute__((visibility("default"))) { return gc_alloc(size, gc_kind); }
void operator delete(void* ptr) __attribute__((visibility("default"))) { abort(); }
};
......@@ -380,7 +390,7 @@ struct DelattrRewriteArgs;
struct HCAttrs {
public:
struct AttrList : ConservativeGCObject {
struct AttrList : public GCAllocated<gc::GCKind::PRECISE> {
Box* attrs[0];
};
......
......@@ -20,6 +20,7 @@
#include <sys/time.h>
#include "core/common.h"
#include "core/stringpool.h"
namespace pyston {
......@@ -52,7 +53,7 @@ bool endswith(const std::string& s, const std::string& pattern);
void removeDirectoryIfExists(const std::string& path);
template <class T1, class T2> void compareKeyset(T1* lhs, T2* rhs) {
std::vector<std::string> lv, rv;
std::vector<InternedString> lv, rv;
for (typename T1::iterator it = lhs->begin(); it != lhs->end(); it++) {
lv.push_back(it->first);
}
......@@ -63,8 +64,8 @@ template <class T1, class T2> void compareKeyset(T1* lhs, T2* rhs) {
std::sort(lv.begin(), lv.end());
std::sort(rv.begin(), rv.end());
std::vector<std::string> lextra(lv.size());
std::vector<std::string>::iterator diffend
std::vector<InternedString> lextra(lv.size());
std::vector<InternedString>::iterator diffend
= std::set_difference(lv.begin(), lv.end(), rv.begin(), rv.end(), lextra.begin());
lextra.resize(diffend - lextra.begin());
......@@ -77,7 +78,7 @@ template <class T1, class T2> void compareKeyset(T1* lhs, T2* rhs) {
good = false;
}
std::vector<std::string> rextra(rv.size());
std::vector<InternedString> rextra(rv.size());
diffend = std::set_difference(rv.begin(), rv.end(), lv.begin(), lv.end(), rextra.begin());
rextra.resize(diffend - rextra.begin());
......
......@@ -39,11 +39,44 @@ namespace gc {
class TraceStack {
private:
std::vector<void*> v;
const int CHUNK_SIZE = 256;
const int MAX_FREE_CHUNKS = 50;
std::vector<void**> chunks;
static std::vector<void**> free_chunks;
void** cur;
void** start;
void** end;
void get_chunk() {
if (free_chunks.size()) {
start = free_chunks.back();
free_chunks.pop_back();
} else {
start = (void**)malloc(sizeof(void*) * CHUNK_SIZE);
}
cur = start;
end = start + CHUNK_SIZE;
}
void release_chunk(void** chunk) {
if (free_chunks.size() == MAX_FREE_CHUNKS)
free(chunk);
else
free_chunks.push_back(chunk);
}
void pop_chunk() {
start = chunks.back();
chunks.pop_back();
end = start + CHUNK_SIZE;
cur = end;
}
public:
TraceStack() {}
TraceStack() { get_chunk(); }
TraceStack(const std::vector<void*>& rhs) {
get_chunk();
for (void* p : rhs) {
assert(!isMarked(GCAllocation::fromUserData(p)));
push(p);
......@@ -52,27 +85,38 @@ public:
void push(void* p) {
GCAllocation* al = GCAllocation::fromUserData(p);
if (isMarked(al))
return;
if (!isMarked(al)) {
setMark(al);
setMark(al);
v.push_back(p);
*cur++ = p;
if (cur == end) {
chunks.push_back(start);
get_chunk();
}
}
int size() { return v.size(); }
void* pop_chunk_and_item() {
release_chunk(start);
if (chunks.size()) {
pop_chunk();
assert(cur == end);
return *--cur; // no need for any bounds checks here since we're guaranteed we're CHUNK_SIZE from the start
}
return NULL;
}
void reserve(int num) { v.reserve(num + v.size()); }
void* pop() {
if (v.size()) {
void* r = v.back();
v.pop_back();
return r;
}
return NULL;
if (cur > start)
return *--cur;
return pop_chunk_and_item();
}
};
std::vector<void**> TraceStack::free_chunks;
static std::vector<void*> roots;
void registerPermanentRoot(void* obj) {
......@@ -212,6 +256,9 @@ static void markPhase() {
} else if (kind_id == GCKind::CONSERVATIVE) {
uint32_t bytes = al->kind_data;
visitor.visitPotentialRange((void**)p, (void**)((char*)p + bytes));
} else if (kind_id == GCKind::PRECISE) {
uint32_t bytes = al->kind_data;
visitor.visitRange((void**)p, (void**)((char*)p + bytes));
} else if (kind_id == GCKind::PYTHON) {
Box* b = reinterpret_cast<Box*>(p);
BoxedClass* cls = b->cls;
......@@ -224,6 +271,9 @@ static void markPhase() {
ASSERT(cls->gc_visit, "%s", getTypeName(b)->c_str());
cls->gc_visit(&visitor, b);
}
} else if (kind_id == GCKind::HIDDEN_CLASS) {
HiddenClass* hcls = reinterpret_cast<HiddenClass*>(p);
hcls->gc_visit(&visitor);
} else {
RELEASE_ASSERT(0, "Unhandled kind: %d", (int)kind_id);
}
......@@ -238,6 +288,17 @@ static void sweepPhase() {
global_heap.freeUnmarked();
}
static bool gc_enabled = true;
bool gcIsEnabled() {
return gc_enabled;
}
void enableGC() {
gc_enabled = true;
}
void disableGC() {
gc_enabled = false;
}
static int ncollections = 0;
void runCollection() {
static StatCounter sc("gc_collections");
......
......@@ -47,6 +47,13 @@ public:
void runCollection();
// Python programs are allowed to pause the GC. This is supposed to pause automatic GC,
// but does not seem to pause manual calls to gc.collect(). So, callers should check gcIsEnabled(),
// if appropriate, before calling runCollection().
bool gcIsEnabled();
void disableGC();
void enableGC();
// These are mostly for debugging:
bool isValidGCObject(void* p);
bool isNonheapRoot(void* p);
......
......@@ -45,6 +45,9 @@ void _collectIfNeeded(size_t bytes) {
thread_bytesAllocatedSinceCollection = 0;
if (bytesAllocatedSinceCollection >= ALLOCBYTES_PER_COLLECTION) {
if (!gcIsEnabled())
return;
// bytesAllocatedSinceCollection = 0;
// threading::GLPromoteRegion _lock;
// runCollection();
......@@ -130,6 +133,9 @@ static Block* alloc_block(uint64_t size, Block** prev) {
Block* rtn = (Block*)small_arena.doMmap(sizeof(Block));
assert(rtn);
rtn->size = size;
rtn->num_obj = BLOCK_SIZE / size;
rtn->min_obj_index = (BLOCK_HEADER_SIZE + size - 1) / size;
rtn->atoms_per_obj = size / ATOM_SIZE;
rtn->prev = prev;
rtn->next = NULL;
......@@ -371,7 +377,7 @@ GCAllocation* Heap::getAllocationFromInteriorPointer(void* ptr) {
if (obj_idx < b->minObjIndex() || obj_idx >= b->numObjects())
return NULL;
int atom_idx = obj_idx * (size / ATOM_SIZE);
int atom_idx = obj_idx * b->atomsPerObj();
if (b->isfree.isSet(atom_idx))
return NULL;
......
......@@ -87,18 +87,19 @@ public:
void clear(int idx) { data[idx / 64] &= ~(1UL << (idx % 64)); }
int scanForNext(Scanner& sc) {
uint64_t mask = 0;
while (true) {
mask = data[sc.next_to_check];
if (likely(mask != 0L)) {
break;
}
sc.next_to_check++;
if (sc.next_to_check == N / 64) {
sc.next_to_check = 0;
return -1;
uint64_t mask = data[sc.next_to_check];
if (unlikely(mask == 0L)) {
while (true) {
sc.next_to_check++;
if (sc.next_to_check == N / 64) {
sc.next_to_check = 0;
return -1;
}
mask = data[sc.next_to_check];
if (likely(mask != 0L)) {
break;
}
}
}
......@@ -134,7 +135,10 @@ struct Block {
union {
struct {
Block* next, **prev;
uint64_t size;
uint32_t size;
uint16_t num_obj;
uint8_t min_obj_index;
uint8_t atoms_per_obj;
Bitmap<ATOMS_PER_BLOCK> isfree;
Bitmap<ATOMS_PER_BLOCK>::Scanner next_to_check;
void* _header_end[0];
......@@ -142,11 +146,11 @@ struct Block {
Atoms atoms[ATOMS_PER_BLOCK];
};
inline int minObjIndex() { return (BLOCK_HEADER_SIZE + size - 1) / size; }
inline int minObjIndex() const { return min_obj_index; }
inline int numObjects() { return BLOCK_SIZE / size; }
inline int numObjects() const { return num_obj; }
inline int atomsPerObj() { return size / ATOM_SIZE; }
inline int atomsPerObj() const { return atoms_per_obj; }
static Block* forPointer(void* ptr) { return (Block*)((uintptr_t)ptr & ~(BLOCK_SIZE - 1)); }
};
......
......@@ -202,7 +202,7 @@ int main(int argc, char** argv) {
if (m->body.size() > 0 && m->body[0]->type == AST_TYPE::Expr) {
AST_Expr* e = ast_cast<AST_Expr>(m->body[0]);
AST_Call* c = new AST_Call();
AST_Name* r = new AST_Name("repr", AST_TYPE::Load, 0);
AST_Name* r = new AST_Name(m->interned_strings->get("repr"), AST_TYPE::Load, 0);
c->func = r;
c->starargs = NULL;
c->kwargs = NULL;
......
......@@ -615,7 +615,7 @@ BoxedClass* BaseException, *Exception, *StandardError, *AssertionError, *Attribu
*NameError, *KeyError, *IndexError, *IOError, *OSError, *ZeroDivisionError, *ValueError, *UnboundLocalError,
*RuntimeError, *ImportError, *StopIteration, *Warning, *SyntaxError, *OverflowError, *DeprecationWarning,
*MemoryError, *LookupError, *EnvironmentError, *ArithmeticError, *BufferError, *KeyboardInterrupt, *SystemExit,
*SystemError, *NotImplementedError, *PendingDeprecationWarning;
*SystemError, *NotImplementedError, *PendingDeprecationWarning, *EOFError;
Box* PyExc_RecursionErrorInst;
Box* PyExc_MemoryErrorInst;
......@@ -1040,6 +1040,7 @@ void setupBuiltins() {
SystemError = makeBuiltinException(StandardError, "SystemError");
NotImplementedError = makeBuiltinException(RuntimeError, "NotImplementedError");
PendingDeprecationWarning = makeBuiltinException(Warning, "PendingDeprecationWarning");
EOFError = makeBuiltinException(StandardError, "EOFError");
EnvironmentError->gc_visit = BoxedEnvironmentError::gcHandler;
EnvironmentError->giveAttr(
......
......@@ -18,14 +18,31 @@
namespace pyston {
Box* gcCollect() {
static Box* gcCollect() {
gc::runCollection();
return None;
}
static Box* isEnabled() {
return boxBool(gc::gcIsEnabled());
}
static Box* disable() {
gc::disableGC();
return None;
}
static Box* enable() {
gc::enableGC();
return None;
}
void setupGC() {
BoxedModule* gc_module = createModule("gc", "__builtin__");
gc_module->giveAttr("__hex__", new BoxedFunction(boxRTFunction((void*)gcCollect, NONE, 0)));
gc_module->giveAttr("isenabled", new BoxedFunction(boxRTFunction((void*)isEnabled, BOXED_BOOL, 0)));
gc_module->giveAttr("disable", new BoxedFunction(boxRTFunction((void*)disable, NONE, 0)));
gc_module->giveAttr("enable", new BoxedFunction(boxRTFunction((void*)enable, NONE, 0)));
}
}
......@@ -45,6 +45,22 @@ Box* startNewThread(Box* target, Box* args) {
return boxInt(thread_id ^ 0x12345678901L);
}
#define CHECK_STATUS(name) \
if (status != 0) { \
perror(name); \
error = 1; \
}
/*
* As of February 2002, Cygwin thread implementations mistakenly report error
* codes in the return value of the sem_ calls (like the pthread_ functions).
* Correct implementations return -1 and put the code in errno. This supports
* either.
*/
static int fix_status(int status) {
return (status == -1) ? errno : status;
}
static BoxedClass* thread_lock_cls;
class BoxedThreadLock : public Box {
private:
......@@ -55,12 +71,38 @@ public:
DEFAULT_CLASS(thread_lock_cls);
static Box* acquire(Box* _self) {
static Box* acquire(Box* _self, Box* _waitflag) {
RELEASE_ASSERT(_self->cls == thread_lock_cls, "");
BoxedThreadLock* self = static_cast<BoxedThreadLock*>(_self);
pthread_mutex_lock(&self->lock);
return None;
RELEASE_ASSERT(_waitflag->cls == int_cls, "");
int waitflag = static_cast<BoxedInt*>(_waitflag)->n;
// Copied + adapted from CPython:
int success;
auto thelock = &self->lock;
int status, error = 0;
{
threading::GLAllowThreadsReadRegion _allow_threads;
do {
if (waitflag)
status = fix_status(pthread_mutex_lock(thelock));
else
status = fix_status(pthread_mutex_trylock(thelock));
} while (status == EINTR); /* Retry if interrupted by a signal */
}
if (waitflag) {
CHECK_STATUS("mutex_lock");
} else if (status != EBUSY) {
CHECK_STATUS("mutex_trylock");
}
success = (status == 0) ? 1 : 0;
return boxBool(status == 0);
}
static Box* release(Box* _self) {
......@@ -70,32 +112,57 @@ public:
pthread_mutex_unlock(&self->lock);
return None;
}
static Box* exit(Box* _self, Box* arg1, Box* arg2, Box** args) { return release(_self); }
};
Box* allocateLock() {
return new BoxedThreadLock();
}
static BoxedClass* thread_local_cls;
class BoxedThreadLocal : public Box {
public:
BoxedThreadLocal() {}
DEFAULT_CLASS(thread_local_cls);
};
Box* getIdent() {
return boxInt(pthread_self());
}
Box* stackSize() {
Py_FatalError("unimplemented");
}
void setupThread() {
thread_module = createModule("thread", "__builtin__");
thread_module->giveAttr("start_new_thread", new BoxedFunction(boxRTFunction((void*)startNewThread, BOXED_INT, 2)));
thread_module->giveAttr("allocate_lock", new BoxedFunction(boxRTFunction((void*)allocateLock, UNKNOWN, 0)));
thread_module->giveAttr("get_ident", new BoxedFunction(boxRTFunction((void*)getIdent, BOXED_INT, 0)));
thread_module->giveAttr("stack_size", new BoxedFunction(boxRTFunction((void*)stackSize, BOXED_INT, 0)));
thread_lock_cls = new BoxedHeapClass(object_cls, NULL, 0, sizeof(BoxedThreadLock), false);
thread_lock_cls->giveAttr("__name__", boxStrConstant("lock"));
thread_lock_cls->giveAttr("__module__", boxStrConstant("thread"));
thread_lock_cls->giveAttr("acquire", new BoxedFunction(boxRTFunction((void*)BoxedThreadLock::acquire, NONE, 1)));
thread_lock_cls->giveAttr(
"acquire",
new BoxedFunction(boxRTFunction((void*)BoxedThreadLock::acquire, NONE, 2, 1, false, false), { boxInt(1) }));
thread_lock_cls->giveAttr("release", new BoxedFunction(boxRTFunction((void*)BoxedThreadLock::release, NONE, 1)));
thread_lock_cls->giveAttr("acquire_lock", thread_lock_cls->getattr("acquire"));
thread_lock_cls->giveAttr("release_lock", thread_lock_cls->getattr("release"));
thread_lock_cls->giveAttr("__enter__", thread_lock_cls->getattr("acquire"));
thread_lock_cls->giveAttr("__exit__", new BoxedFunction(boxRTFunction((void*)BoxedThreadLock::exit, NONE, 4)));
thread_lock_cls->freeze();
thread_local_cls = new BoxedHeapClass(object_cls, NULL, 0, sizeof(BoxedThreadLocal), false);
thread_local_cls->giveAttr("__name__", boxStrConstant("_local"));
thread_local_cls->giveAttr("__module__", boxStrConstant("thread"));
thread_local_cls->freeze();
thread_module->giveAttr("_local", thread_local_cls);
BoxedClass* ThreadError
= new BoxedHeapClass(Exception, NULL, Exception->attrs_offset, Exception->tp_basicsize, false);
ThreadError->giveAttr("__name__", boxStrConstant("error"));
......
......@@ -34,12 +34,10 @@ namespace pyston {
BoxedClass* method_cls;
#define MAKE_CHECK(NAME, cls_name) \
extern "C" bool Py##NAME##_Check(PyObject* op) noexcept { return isSubclass(op->cls, cls_name); }
#define MAKE_CHECK2(NAME, cls_name) \
extern "C" bool _Py##NAME##_Check(PyObject* op) noexcept { return isSubclass(op->cls, cls_name); }
MAKE_CHECK2(Int, int_cls)
MAKE_CHECK2(String, str_cls)
MAKE_CHECK(Int, int_cls)
MAKE_CHECK(String, str_cls)
MAKE_CHECK(Long, long_cls)
MAKE_CHECK(List, list_cls)
MAKE_CHECK(Tuple, tuple_cls)
......@@ -688,7 +686,8 @@ void checkAndThrowCAPIException() {
assert(!cur_thread_state.curexc_value);
if (_type) {
RELEASE_ASSERT(cur_thread_state.curexc_traceback == NULL, "unsupported");
RELEASE_ASSERT(cur_thread_state.curexc_traceback == NULL || cur_thread_state.curexc_traceback == None,
"unsupported");
BoxedClass* type = static_cast<BoxedClass*>(_type);
assert(isInstance(_type, type_cls) && isSubclass(static_cast<BoxedClass*>(type), BaseException)
&& "Only support throwing subclass of BaseException for now");
......@@ -902,7 +901,8 @@ extern "C" PyObject* PyNumber_Add(PyObject* lhs, PyObject* rhs) noexcept {
try {
return binop(lhs, rhs, AST_TYPE::Add);
} catch (ExcInfo e) {
Py_FatalError("unimplemented");
setCAPIException(e);
return NULL;
}
}
......
......@@ -237,7 +237,44 @@ extern "C" PyObject* PyDict_GetItem(PyObject* dict, PyObject* key) noexcept {
}
extern "C" int PyDict_Next(PyObject* op, Py_ssize_t* ppos, PyObject** pkey, PyObject** pvalue) noexcept {
Py_FatalError("unimplemented");
assert(op->cls == dict_cls);
BoxedDict* self = static_cast<BoxedDict*>(op);
// Callers of PyDict_New() provide a pointer to some storage for this function to use, in
// the form of a Py_ssize_t* -- ie they allocate a Py_ssize_t on their stack, and let us use
// it.
//
// We want to store an unordered_map::iterator in that. In my glibc it would fit, but to keep
// things a little bit more portable, allocate separate storage for the iterator, and store the
// pointer to this storage in the Py_ssize_t slot.
//
// Results in lots of indirection unfortunately. If it becomes an issue we can try to switch
// to storing the iterator directly in the stack slot.
typedef BoxedDict::DictMap::iterator iterator;
static_assert(sizeof(Py_ssize_t) == sizeof(iterator*), "");
iterator** it_ptr = reinterpret_cast<iterator**>(ppos);
// Clients are supposed to zero-initialize *ppos:
if (*it_ptr == NULL) {
*it_ptr = (iterator*)malloc(sizeof(iterator));
** it_ptr = self->d.begin();
}
iterator* it = *it_ptr;
if (*it == self->d.end()) {
free(it);
return 0;
}
*pkey = (*it)->first;
*pvalue = (*it)->second;
++(*it);
return 1;
}
extern "C" PyObject* PyDict_GetItemString(PyObject* dict, const char* key) noexcept {
......
......@@ -188,8 +188,16 @@ Box* fileIterHasNext(Box* s) {
return boxBool(!fileEof(self));
}
extern "C" void PyFile_SetFP(PyObject* _f, FILE* fp) noexcept {
assert(_f->cls == file_cls);
BoxedFile* f = static_cast<BoxedFile*>(_f);
assert(f->f == NULL);
f->f = fp;
}
extern "C" PyObject* PyFile_FromFile(FILE* fp, char* name, char* mode, int (*close)(FILE*)) noexcept {
Py_FatalError("unimplemented");
RELEASE_ASSERT(close == fclose, "unsupported");
return new BoxedFile(fp, name, mode);
}
extern "C" FILE* PyFile_AsFile(PyObject* f) noexcept {
......@@ -258,11 +266,59 @@ extern "C" int PyFile_WriteString(const char* s, PyObject* f) noexcept {
}
extern "C" void PyFile_SetBufSize(PyObject* f, int bufsize) noexcept {
Py_FatalError("unimplemented");
assert(f->cls == file_cls);
if (bufsize >= 0) {
if (bufsize == 0) {
setvbuf(static_cast<BoxedFile*>(f)->f, NULL, _IONBF, 0);
} else {
Py_FatalError("unimplemented");
}
}
}
extern "C" int _PyFile_SanitizeMode(char* mode) noexcept {
Py_FatalError("unimplemented");
char* upos;
size_t len = strlen(mode);
if (!len) {
PyErr_SetString(PyExc_ValueError, "empty mode string");
return -1;
}
upos = strchr(mode, 'U');
if (upos) {
memmove(upos, upos + 1, len - (upos - mode)); /* incl null char */
if (mode[0] == 'w' || mode[0] == 'a') {
PyErr_Format(PyExc_ValueError, "universal newline "
"mode can only be used with modes "
"starting with 'r'");
return -1;
}
if (mode[0] != 'r') {
memmove(mode + 1, mode, strlen(mode) + 1);
mode[0] = 'r';
}
if (!strchr(mode, 'b')) {
memmove(mode + 2, mode + 1, strlen(mode));
mode[1] = 'b';
}
} else if (mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') {
PyErr_Format(PyExc_ValueError, "mode string must begin with "
"one of 'r', 'w', 'a' or 'U', not '%.200s'",
mode);
return -1;
}
#ifdef Py_VERIFY_WINNT
/* additional checks on NT with visual studio 2005 and higher */
if (!_PyVerify_Mode_WINNT(mode)) {
PyErr_Format(PyExc_ValueError, "Invalid mode ('%.50s')", mode);
return -1;
}
#endif
return 0;
}
extern "C" int PyObject_AsFileDescriptor(PyObject* o) noexcept {
......
......@@ -106,6 +106,7 @@ void force() {
FORCE(raise0);
FORCE(raise3);
FORCE(deopt);
FORCE(div_i64_i64);
FORCE(mod_i64_i64);
......
......@@ -39,10 +39,6 @@ extern "C" int PyList_Append(PyObject* op, PyObject* newitem) noexcept {
return 0;
}
extern "C" int PyList_SetItem(PyObject* op, Py_ssize_t i, PyObject* newitem) noexcept {
Py_FatalError("unimplemented");
}
extern "C" Box* listRepr(BoxedList* self) {
LOCK_REGION(self->lock.asRead());
......@@ -180,6 +176,16 @@ extern "C" Box* listGetitem(BoxedList* self, Box* slice) {
}
}
static void _listSetitem(BoxedList* self, int64_t n, Box* v) {
if (n < 0)
n = self->size + n;
if (n < 0 || n >= self->size) {
raiseExcHelper(IndexError, "list index out of range");
}
self->elts->elts[n] = v;
}
extern "C" Box* listSetitemInt(BoxedList* self, BoxedInt* slice, Box* v) {
// I think r lock is ok here, since we don't change the list structure:
......@@ -188,17 +194,22 @@ extern "C" Box* listSetitemInt(BoxedList* self, BoxedInt* slice, Box* v) {
assert(self->cls == list_cls);
assert(isSubclass(slice->cls, int_cls));
int64_t n = slice->n;
if (n < 0)
n = self->size + n;
if (n < 0 || n >= self->size) {
raiseExcHelper(IndexError, "list index out of range");
}
_listSetitem(self, n, v);
self->elts->elts[n] = v;
return None;
}
extern "C" int PyList_SetItem(PyObject* op, Py_ssize_t i, PyObject* newitem) noexcept {
assert(op->cls == list_cls);
try {
_listSetitem(static_cast<BoxedList*>(op), i, newitem);
} catch (ExcInfo e) {
abort();
}
return 0;
}
Box* listIAdd(BoxedList* self, Box* _rhs);
// Analogue of _PyEval_SliceIndex
......@@ -554,12 +565,21 @@ extern "C" Box* listNew(Box* cls, Box* container) {
}
extern "C" PyObject* PyList_New(Py_ssize_t size) noexcept {
// This function is supposed to return a list of `size` NULL elements.
// That will probably trip an assert somewhere if we try to create that (ex
// I think the GC will expect them to be real objects so they can be relocated).
RELEASE_ASSERT(size == 0, "");
try {
return new BoxedList();
BoxedList* l = new BoxedList();
if (size) {
// This function is supposed to return a list of `size` NULL elements.
// That will probably trip an assert somewhere if we try to create that (ex
// I think the GC will expect them to be real objects so they can be relocated),
// so put None in instead
l->ensure(size);
for (Py_ssize_t i = 0; i < size; i++) {
l->elts->elts[i] = None;
}
l->size = size;
}
return l;
} catch (ExcInfo e) {
abort();
}
......
This diff is collapsed.
......@@ -36,6 +36,8 @@ extern "C" void raise3(Box*, Box*, Box*) __attribute__((__noreturn__));
void raiseExc(Box* exc_obj) __attribute__((__noreturn__));
void raiseRaw(const ExcInfo& e) __attribute__((__noreturn__));
extern "C" Box* deopt(AST_expr* expr, Box* value);
// helper function for raising from the runtime:
void raiseExcHelper(BoxedClass*, const char* fmt, ...) __attribute__((__noreturn__));
......@@ -70,8 +72,8 @@ extern "C" BoxedInt* len(Box* obj);
extern "C" i64 unboxedLen(Box* obj);
extern "C" Box* binop(Box* lhs, Box* rhs, int op_type);
extern "C" Box* augbinop(Box* lhs, Box* rhs, int op_type);
extern "C" Box* getGlobal(BoxedModule* m, std::string* name);
extern "C" void delGlobal(BoxedModule* m, std::string* name);
extern "C" Box* getGlobal(BoxedModule* m, const std::string* name);
extern "C" void delGlobal(BoxedModule* m, const std::string* name);
extern "C" Box* getitem(Box* value, Box* slice);
extern "C" void setitem(Box* target, Box* slice, Box* value);
extern "C" void delitem(Box* target, Box* slice);
......
......@@ -195,12 +195,47 @@ Box* setAdd(BoxedSet* self, Box* v) {
return None;
}
Box* setRemove(BoxedSet* self, Box* v) {
assert(self->cls == set_cls);
auto it = self->s.find(v);
if (it == self->s.end()) {
BoxedString* s = reprOrNull(v);
if (s)
raiseExcHelper(KeyError, "%s", s->s.c_str());
else
raiseExcHelper(KeyError, "");
}
self->s.erase(it);
return None;
}
Box* setClear(BoxedSet* self, Box* v) {
assert(self->cls == set_cls);
self->s.clear();
return None;
}
Box* setUpdate(BoxedSet* self, BoxedTuple* args) {
assert(self->cls == set_cls);
assert(args->cls == tuple_cls);
for (auto l : args->elts) {
if (l->cls == set_cls) {
BoxedSet* s2 = static_cast<BoxedSet*>(l);
self->s.insert(s2->s.begin(), s2->s.end());
} else {
for (auto e : l->pyElements()) {
self->s.insert(e);
}
}
}
return None;
}
Box* setContains(BoxedSet* self, Box* v) {
assert(self->cls == set_cls || self->cls == frozenset_cls);
return boxBool(self->s.count(v) != 0);
......@@ -281,8 +316,10 @@ void setupSet() {
frozenset_cls->giveAttr("__nonzero__", set_cls->getattr("__nonzero__"));
set_cls->giveAttr("add", new BoxedFunction(boxRTFunction((void*)setAdd, NONE, 2)));
set_cls->giveAttr("remove", new BoxedFunction(boxRTFunction((void*)setRemove, NONE, 2)));
set_cls->giveAttr("clear", new BoxedFunction(boxRTFunction((void*)setClear, NONE, 1)));
set_cls->giveAttr("update", new BoxedFunction(boxRTFunction((void*)setUpdate, NONE, 1, 0, true, false)));
set_cls->freeze();
frozenset_cls->freeze();
......
......@@ -508,7 +508,7 @@ BoxedClass* object_cls, *type_cls, *none_cls, *bool_cls, *int_cls, *float_cls, *
BoxedTuple* EmptyTuple;
}
extern "C" Box* createUserClass(std::string* name, Box* _bases, Box* _attr_dict) {
extern "C" Box* createUserClass(const std::string* name, Box* _bases, Box* _attr_dict) {
ASSERT(_attr_dict->cls == dict_cls, "%s", getTypeName(_attr_dict)->c_str());
BoxedDict* attr_dict = static_cast<BoxedDict*>(_attr_dict);
......@@ -1029,7 +1029,6 @@ void setupRuntime() {
int_cls = new BoxedHeapClass(object_cls, NULL, 0, sizeof(BoxedInt), false);
bool_cls = new BoxedHeapClass(int_cls, NULL, 0, sizeof(BoxedBool), false);
complex_cls = new BoxedHeapClass(object_cls, NULL, 0, sizeof(BoxedComplex), false);
// TODO we're leaking long memory!
long_cls = new BoxedHeapClass(object_cls, &BoxedLong::gchandler, 0, sizeof(BoxedLong), false);
float_cls = new BoxedHeapClass(object_cls, NULL, 0, sizeof(BoxedFloat), false);
function_cls = new BoxedHeapClass(object_cls, &functionGCHandler, offsetof(BoxedFunction, attrs),
......
......@@ -78,9 +78,9 @@ extern "C" Box* getSysStdout();
extern "C" {
extern BoxedClass* object_cls, *type_cls, *bool_cls, *int_cls, *long_cls, *float_cls, *str_cls, *function_cls,
*none_cls, *instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *file_cls, *xrange_cls,
*member_cls, *method_cls, *closure_cls, *generator_cls, *complex_cls, *basestring_cls, *unicode_cls, *property_cls,
*staticmethod_cls, *classmethod_cls, *attrwrapper_cls;
*none_cls, *instancemethod_cls, *list_cls, *slice_cls, *module_cls, *dict_cls, *tuple_cls, *file_cls,
*enumerate_cls, *xrange_cls, *member_cls, *method_cls, *closure_cls, *generator_cls, *complex_cls, *basestring_cls,
*unicode_cls, *property_cls, *staticmethod_cls, *classmethod_cls, *attrwrapper_cls;
}
extern "C" {
extern Box* None, *NotImplemented, *True, *False;
......@@ -118,7 +118,7 @@ extern "C" void listAppendArrayInternal(Box* self, Box** v, int nelts);
extern "C" Box* boxCLFunction(CLFunction* f, BoxedClosure* closure, bool isGenerator,
std::initializer_list<Box*> defaults);
extern "C" CLFunction* unboxCLFunction(Box* b);
extern "C" Box* createUserClass(std::string* name, Box* base, Box* attr_dict);
extern "C" Box* createUserClass(const std::string* name, Box* base, Box* attr_dict);
extern "C" double unboxFloat(Box* b);
extern "C" Box* createDict();
extern "C" Box* createList();
......@@ -255,7 +255,7 @@ static_assert(offsetof(pyston::BoxedHeapClass, as_buffer) == offsetof(PyHeapType
static_assert(sizeof(pyston::BoxedHeapClass) == sizeof(PyHeapTypeObject), "");
class HiddenClass : public ConservativeGCObject {
class HiddenClass : public GCAllocated<gc::GCKind::HIDDEN_CLASS> {
private:
HiddenClass() {}
HiddenClass(const HiddenClass* parent) : attr_offsets(parent->attr_offsets) {}
......@@ -270,8 +270,8 @@ public:
return new HiddenClass();
}
conservative_unordered_map<std::string, int> attr_offsets;
conservative_unordered_map<std::string, HiddenClass*> children;
std::unordered_map<std::string, int> attr_offsets;
std::unordered_map<std::string, HiddenClass*> children;
HiddenClass* getOrMakeChild(const std::string& attr);
......@@ -282,6 +282,12 @@ public:
return it->second;
}
HiddenClass* delAttrToMakeHC(const std::string& attr);
void gc_visit(GCVisitor* visitor) {
for (const auto& p : children) {
visitor->visit(p.second);
}
}
};
class BoxedInt : public Box {
......
# expected: fail
# - metaclasses
# - object.__getattribute__ doesn't exist
# attr-getting resolution.
......
# expected: fail
# - lambdas, varargs
# Testing im-boxing:
class C(object):
def __repr__(self):
return "<C>"
def __call__(*args):
print args
return args
def mul(*args):
return args
class C1(object):
def __repr__(self):
return "<C1>"
__add__ = C()
__sub__ = lambda *args: args
__mul__ = mul
......
# expected: fail
# - arbitrary stuff in classdefs
# objmodel classattrs (like __add__) can be non-functions, so might not get bound into instancemethods:
class Adder(object):
......@@ -10,6 +7,8 @@ class Adder(object):
class C(object):
__add__ = Adder()
def __repr__(self):
return "<C>"
c = C()
print c + c
# expected: fail
# - inheritance not implemented
class C(object):
x = 1
......@@ -13,4 +10,5 @@ print d.x
# But also when doing instance-level lookups!
print D.x
print D.__dict__
print 'x' in C.__dict__
print 'x' in D.__dict__
This diff is collapsed.
# expected: fail
# - descriptors not implemented yet
# Make sure that we guard in a getattr IC to make sure that
# we don't subsequently get an object with a __get__ defined.
class D(object):
pass
def __repr__(self):
return "<D>"
class C(object):
def __repr__(self):
return "<C>"
d = D()
c = C()
......
# expected: fail
# - descriptors not implemented yet
def f1():
class D(object):
def __get__(self, instance, owner):
......@@ -13,6 +10,9 @@ def f1():
class C(object):
d = D()
def __repr__(self):
return "<C>"
print C.d
print C().d
......@@ -33,9 +33,15 @@ def f2():
print "__getattribute__", attr
return 1
def __repr__(self):
return "<MaybeDescriptor>"
class HasDescriptor(object):
x = MaybeDescriptor()
def __repr__(self):
return "<HasDescriptor>"
hd = HasDescriptor()
# Getting hd.x will look up type(hd.__dict__[x]).__get__
# and not go through __getattribute__
......
# expected: fail
# - inheritance
# - exception printing
class BadException(Exception):
def __str__(self):
......
# expected: fail
# execfile() not implemented yet
# - we throw some very weird error here
try:
execfile("doesnt_exist.py")
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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