Commit 9a33763c authored by Kevin Modzelewski's avatar Kevin Modzelewski

Some initial refactoring work for arg passing

Replace a simple "num_args" argument with a packed struct that takes
num_args and adds num_keywords, has_varargs, and has_kwargs.  Tried
to add asserts in all the places that don't allow
keywords/varargs/starargs

Started refactoring things; got to the point of attempting
argument->parameter shuffling, but it's tricky if we allow every
compilation to have a different signature (used by builtins).
Really they all have the same signatures but different
specializations; to get to that point, need to add defaults.
parent a8c83341
......@@ -273,7 +273,7 @@ pyston_all: pyston_dbg pyston pyston_oprof pyston_prof $(OPTIONAL_SRCS:.cpp=.o)
ALL_HEADERS := $(wildcard */*.h) $(wildcard */*/*.h)
tags: $(SRCS) $(OPTIONAL_SRCS) $(ALL_HEADERS)
$(ECHO) Computing tags...
$(ECHO) Calculating tags...
$(VERB) ctags $^
$(UNITTEST_SRCS:.cpp=.o): CXXFLAGS := $(CXXFLAGS) -isystem $(GTEST_DIR)/include
......
......@@ -210,7 +210,7 @@ private:
std::vector<CompilerType*> arg_types;
arg_types.push_back(right);
CompilerType* rtn = attr_type->callType(arg_types);
CompilerType* rtn = attr_type->callType(ArgPassSpec(2), arg_types, NULL);
if (left == right && (left == INT || left == FLOAT)) {
ASSERT((rtn == left || rtn == UNKNOWN) && "not strictly required but probably something worth looking into",
......@@ -237,7 +237,7 @@ private:
std::vector<CompilerType*> arg_types;
arg_types.push_back(right);
CompilerType* rtn = attr_type->callType(arg_types);
CompilerType* rtn = attr_type->callType(ArgPassSpec(2), arg_types, NULL);
if (left == right && (left == INT || left == FLOAT)) {
ASSERT((rtn == left || rtn == UNKNOWN) && "not strictly required but probably something worth looking into",
......@@ -287,7 +287,7 @@ private:
return UNKNOWN;
}
CompilerType* rtn_type = func->callType(arg_types);
CompilerType* rtn_type = func->callType(ArgPassSpec(arg_types.size()), arg_types, NULL);
// Should be unboxing things before getting here:
ASSERT(rtn_type == unboxedType(rtn_type->getConcreteType()), "%s", rtn_type->debugName().c_str());
......@@ -322,7 +322,7 @@ private:
std::vector<CompilerType*> arg_types;
arg_types.push_back(right);
return attr_type->callType(arg_types);
return attr_type->callType(ArgPassSpec(2), arg_types, NULL);
}
virtual void* visit_dict(AST_Dict* node) {
......@@ -404,7 +404,7 @@ private:
CompilerType* getitem_type = val->getattrType(&name, true);
std::vector<CompilerType*> args;
args.push_back(slice);
return getitem_type->callType(args);
return getitem_type->callType(ArgPassSpec(1), args, NULL);
}
virtual void* visit_tuple(AST_Tuple* node) {
......@@ -422,7 +422,7 @@ private:
const std::string& name = getOpName(node->op_type);
CompilerType* attr_type = operand->getattrType(&name, true);
std::vector<CompilerType*> arg_types;
return attr_type->callType(arg_types);
return attr_type->callType(ArgPassSpec(0), arg_types, NULL);
}
......
......@@ -72,6 +72,7 @@ struct GlobalState {
llvm::Type* llvm_clfunction_type_ptr;
llvm::Type* llvm_module_type_ptr, *llvm_bool_type_ptr;
llvm::Type* i1, *i8, *i8_ptr, *i32, *i64, *void_, *double_;
llvm::Type* vector_ptr;
GlobalFuncs funcs;
......
......@@ -79,15 +79,19 @@ public:
return rtn;
}
virtual CompilerType* callType(std::vector<CompilerType*>& arg_types) {
virtual CompilerType* callType(ArgPassSpec argspec, const std::vector<CompilerType*>& arg_types,
const std::vector<const std::string*>* keyword_names) {
std::vector<CompilerType*> new_args(arg_types);
new_args.insert(new_args.begin(), obj_type);
return function_type->callType(new_args);
ArgPassSpec new_argspec(argspec.num_args + 1u, argspec.num_keywords, argspec.has_starargs, argspec.has_kwargs);
return function_type->callType(new_argspec, new_args, keyword_names);
}
std::string debugName() {
return "instanceMethod(" + obj_type->debugName() + " ; " + function_type->debugName() + ")";
}
virtual void drop(IREmitter& emitter, VAR* var) {
checkVar(var);
RawInstanceMethod* val = var->getValue();
......@@ -95,14 +99,19 @@ public:
val->func->decvref(emitter);
delete val;
}
virtual CompilerVariable* call(IREmitter& emitter, const OpInfo& info,
ValuedCompilerVariable<RawInstanceMethod*>* var,
const std::vector<CompilerVariable*>& args) {
ValuedCompilerVariable<RawInstanceMethod*>* var, ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
std::vector<CompilerVariable*> new_args;
new_args.push_back(var->getValue()->obj);
new_args.insert(new_args.end(), args.begin(), args.end());
return var->getValue()->func->call(emitter, info, new_args);
ArgPassSpec new_argspec(argspec.num_args + 1u, argspec.num_keywords, argspec.has_starargs, argspec.has_kwargs);
return var->getValue()->func->call(emitter, info, new_argspec, new_args, keyword_names);
}
virtual bool canConvertTo(ConcreteCompilerType* other_type) { return other_type == UNKNOWN; }
virtual ConcreteCompilerType* getConcreteType() { return typeFromClass(instancemethod_cls); }
virtual ConcreteCompilerType* getBoxType() { return getConcreteType(); }
......@@ -176,10 +185,12 @@ public:
virtual CompilerVariable* getattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::string* attr, bool cls_only);
virtual CompilerVariable* call(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::vector<CompilerVariable*>& args);
ArgPassSpec argspec, const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names);
virtual CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::string* attr, bool clsonly,
const std::vector<CompilerVariable*>& args);
const std::string* attr, bool clsonly, ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names);
virtual ConcreteCompilerVariable* nonzero(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var);
void setattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var, const std::string* attr,
......@@ -218,7 +229,10 @@ public:
}
virtual CompilerType* getattrType(const std::string* attr, bool cls_only) { return UNKNOWN; }
virtual CompilerType* callType(std::vector<CompilerType*>& arg_types) { return UNKNOWN; }
virtual CompilerType* callType(ArgPassSpec argspec, const std::vector<CompilerType*>& arg_types,
const std::vector<const std::string*>* keyword_names) {
return UNKNOWN;
}
virtual BoxedClass* guaranteedClass() { return NULL; }
virtual ConcreteCompilerType* getBoxType() { return this; }
virtual ConcreteCompilerVariable* makeConverted(IREmitter& emitter, ConcreteCompilerVariable* var,
......@@ -312,8 +326,13 @@ CompilerVariable* UnknownType::getattr(IREmitter& emitter, const OpInfo& info, C
}
static ConcreteCompilerVariable* _call(IREmitter& emitter, const OpInfo& info, llvm::Value* func, void* func_addr,
const std::vector<llvm::Value*> other_args,
const std::vector<CompilerVariable*> args, ConcreteCompilerType* rtn_type) {
const std::vector<llvm::Value*> other_args, ArgPassSpec argspec,
const std::vector<CompilerVariable*> args,
const std::vector<const std::string*>* keyword_names,
ConcreteCompilerType* rtn_type) {
bool pass_keyword_names = (keyword_names != nullptr);
assert(pass_keyword_names == (argspec.num_keywords > 0));
std::vector<BoxedClass*> guaranteed_classes;
std::vector<ConcreteCompilerVariable*> converted_args;
for (int i = 0; i < args.size(); i++) {
......@@ -327,12 +346,20 @@ static ConcreteCompilerVariable* _call(IREmitter& emitter, const OpInfo& info, l
if (args.size() >= 1) {
llvm_args.push_back(converted_args[0]->getValue());
} else if (pass_keyword_names) {
llvm_args.push_back(embedConstantPtr(NULL, g.llvm_value_type_ptr));
}
if (args.size() >= 2) {
llvm_args.push_back(converted_args[1]->getValue());
} else if (pass_keyword_names) {
llvm_args.push_back(embedConstantPtr(NULL, g.llvm_value_type_ptr));
}
if (args.size() >= 3) {
llvm_args.push_back(converted_args[2]->getValue());
} else if (pass_keyword_names) {
llvm_args.push_back(embedConstantPtr(NULL, g.llvm_value_type_ptr));
}
llvm::Value* mallocsave = NULL;
......@@ -358,6 +385,11 @@ static ConcreteCompilerVariable* _call(IREmitter& emitter, const OpInfo& info, l
emitter.getBuilder()->CreateStore(converted_args[i]->getValue(), ptr);
}
llvm_args.push_back(arg_array);
llvm_args.push_back(embedConstantPtr(keyword_names, g.vector_ptr));
} else if (pass_keyword_names) {
llvm_args.push_back(embedConstantPtr(NULL, g.llvm_value_type_ptr->getPointerTo()));
llvm_args.push_back(embedConstantPtr(keyword_names, g.vector_ptr));
}
// f->dump();
......@@ -368,6 +400,10 @@ static ConcreteCompilerVariable* _call(IREmitter& emitter, const OpInfo& info, l
llvm::Value* rtn;
// func->dump();
// for (auto a : llvm_args)
// a->dump();
bool do_patchpoint = ENABLE_ICCALLSITES && !info.isInterpreted()
&& (func_addr == runtimeCall || func_addr == pyston::callattr);
if (do_patchpoint) {
......@@ -405,15 +441,21 @@ static ConcreteCompilerVariable* _call(IREmitter& emitter, const OpInfo& info, l
}
CompilerVariable* UnknownType::call(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::vector<CompilerVariable*>& args) {
ArgPassSpec argspec, const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
bool pass_keywords = (argspec.num_keywords != 0);
int npassed_args = argspec.totalPassed();
llvm::Value* func;
if (args.size() == 0)
if (pass_keywords)
func = g.funcs.runtimeCall;
else if (npassed_args == 0)
func = g.funcs.runtimeCall0;
else if (args.size() == 1)
else if (npassed_args == 1)
func = g.funcs.runtimeCall1;
else if (args.size() == 2)
else if (npassed_args == 2)
func = g.funcs.runtimeCall2;
else if (args.size() == 3)
else if (npassed_args == 3)
func = g.funcs.runtimeCall3;
else
func = g.funcs.runtimeCall;
......@@ -421,14 +463,19 @@ CompilerVariable* UnknownType::call(IREmitter& emitter, const OpInfo& info, Conc
std::vector<llvm::Value*> other_args;
other_args.push_back(var->getValue());
llvm::Value* nargs = llvm::ConstantInt::get(g.i64, args.size(), false);
other_args.push_back(nargs);
return _call(emitter, info, func, (void*)runtimeCall, other_args, args, UNKNOWN);
llvm::Value* llvm_argspec = llvm::ConstantInt::get(g.i32, argspec.asInt(), false);
other_args.push_back(llvm_argspec);
return _call(emitter, info, func, (void*)runtimeCall, other_args, argspec, args, keyword_names, UNKNOWN);
}
CompilerVariable* UnknownType::callattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::string* attr, bool clsonly,
const std::vector<CompilerVariable*>& args) {
const std::string* attr, bool clsonly, ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
RELEASE_ASSERT(!argspec.has_starargs, "");
RELEASE_ASSERT(!argspec.has_kwargs, "");
RELEASE_ASSERT(argspec.num_keywords == 0, "");
llvm::Value* func;
if (args.size() == 0)
func = g.funcs.callattr0;
......@@ -446,9 +493,9 @@ CompilerVariable* UnknownType::callattr(IREmitter& emitter, const OpInfo& info,
other_args.push_back(embedConstantPtr(attr, g.llvm_str_type_ptr));
other_args.push_back(getConstantInt(clsonly, g.i1));
llvm::Value* nargs = llvm::ConstantInt::get(g.i64, args.size(), false);
other_args.push_back(nargs);
return _call(emitter, info, func, (void*)pyston::callattr, other_args, args, UNKNOWN);
llvm::Value* llvm_argspec = llvm::ConstantInt::get(g.i32, argspec.asInt(), false);
other_args.push_back(llvm_argspec);
return _call(emitter, info, func, (void*)pyston::callattr, other_args, argspec, args, keyword_names, UNKNOWN);
}
ConcreteCompilerVariable* UnknownType::nonzero(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var) {
......@@ -500,7 +547,12 @@ public:
virtual CompilerType* getattrType(const std::string* attr, bool cls_only) { return UNDEF; }
virtual CompilerType* callType(std::vector<CompilerType*>& arg_types) {
virtual CompilerType* callType(ArgPassSpec argspec, const std::vector<CompilerType*>& arg_types,
const std::vector<const std::string*>* keyword_names) {
RELEASE_ASSERT(!argspec.has_starargs, "");
RELEASE_ASSERT(!argspec.has_kwargs, "");
RELEASE_ASSERT(argspec.num_keywords == 0, "");
for (int i = 0; i < sigs.size(); i++) {
Sig* sig = sigs[i];
if (sig->arg_types.size() != arg_types.size())
......@@ -612,10 +664,11 @@ public:
}
virtual CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::string* attr, bool clsonly,
const std::vector<CompilerVariable*>& args) {
const std::string* attr, bool clsonly, ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
ConcreteCompilerVariable* converted = var->makeConverted(emitter, BOXED_INT);
CompilerVariable* rtn = converted->callattr(emitter, info, attr, clsonly, args);
CompilerVariable* rtn = converted->callattr(emitter, info, attr, clsonly, argspec, args, keyword_names);
converted->decvref(emitter);
return rtn;
}
......@@ -789,7 +842,12 @@ public:
return rtn;
}
virtual CompilerType* callType(std::vector<CompilerType*>& arg_types) {
virtual CompilerType* callType(ArgPassSpec argspec, const std::vector<CompilerType*>& arg_types,
const std::vector<const std::string*>* keyword_names) {
RELEASE_ASSERT(!argspec.has_starargs, "");
RELEASE_ASSERT(!argspec.has_kwargs, "");
RELEASE_ASSERT(argspec.num_keywords == 0, "");
bool is_well_defined = (cls == xrange_cls);
assert(is_well_defined);
return typeFromClass(cls);
......@@ -856,7 +914,11 @@ public:
return UNKNOWN;
}
virtual CompilerType* callType(std::vector<CompilerType*>& arg_types) { return UNKNOWN; }
virtual CompilerType* callType(ArgPassSpec argspec, const std::vector<CompilerType*>& arg_types,
const std::vector<const std::string*>* keyword_names) {
return UNKNOWN;
}
CompilerVariable* getattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::string* attr, bool cls_only) {
......@@ -917,16 +979,18 @@ public:
}
virtual CompilerVariable* call(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::vector<CompilerVariable*>& args) {
ArgPassSpec argspec, const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
ConcreteCompilerVariable* converted = var->makeConverted(emitter, UNKNOWN);
CompilerVariable* rtn = converted->call(emitter, info, args);
CompilerVariable* rtn = converted->call(emitter, info, argspec, args, keyword_names);
converted->decvref(emitter);
return rtn;
}
virtual CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::string* attr, bool clsonly,
const std::vector<CompilerVariable*>& args) {
const std::string* attr, bool clsonly, ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
if (cls->is_constant && !cls->instancesHaveAttrs() && cls->hasGenericGetattr()) {
Box* rtattr = cls->getattr(*attr);
if (rtattr == NULL) {
......@@ -938,6 +1002,12 @@ public:
}
if (rtattr->cls == function_cls) {
// Functions themselves can't remunge their passed arguments;
// that should either happen here, or we skip things that aren't a simple match
RELEASE_ASSERT(!argspec.has_starargs, "");
RELEASE_ASSERT(!argspec.has_kwargs, "");
RELEASE_ASSERT(argspec.num_keywords == 0, "");
CLFunction* cl = unboxRTFunction(rtattr);
assert(cl);
......@@ -997,8 +1067,8 @@ public:
std::vector<llvm::Value*> other_args;
ConcreteCompilerVariable* rtn
= _call(emitter, info, linked_function, cf->code, other_args, new_args, cf->sig->rtn_type);
ConcreteCompilerVariable* rtn = _call(emitter, info, linked_function, cf->code, other_args, argspec,
new_args, keyword_names, cf->sig->rtn_type);
assert(rtn->getType() == cf->sig->rtn_type);
assert(cf->sig->rtn_type != BOXED_INT);
......@@ -1010,7 +1080,7 @@ public:
}
ConcreteCompilerVariable* converted = var->makeConverted(emitter, UNKNOWN);
CompilerVariable* rtn = converted->callattr(emitter, info, attr, clsonly, args);
CompilerVariable* rtn = converted->callattr(emitter, info, attr, clsonly, argspec, args, keyword_names);
converted->decvref(emitter);
return rtn;
}
......@@ -1071,9 +1141,10 @@ public:
}
virtual CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, VAR* var, const std::string* attr,
bool clsonly, const std::vector<CompilerVariable*>& args) {
bool clsonly, ArgPassSpec argspec, const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
ConcreteCompilerVariable* converted = var->makeConverted(emitter, STR);
CompilerVariable* rtn = converted->callattr(emitter, info, attr, clsonly, args);
CompilerVariable* rtn = converted->callattr(emitter, info, attr, clsonly, argspec, args, keyword_names);
converted->decvref(emitter);
return rtn;
}
......@@ -1308,8 +1379,10 @@ public:
}
virtual CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, VAR* var, const std::string* attr,
bool clsonly, const std::vector<CompilerVariable*>& args) {
return makeConverted(emitter, var, getConcreteType())->callattr(emitter, info, attr, clsonly, args);
bool clsonly, ArgPassSpec argspec, const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
return makeConverted(emitter, var, getConcreteType())
->callattr(emitter, info, attr, clsonly, argspec, args, keyword_names);
}
};
......@@ -1339,8 +1412,9 @@ public:
return llvm::Type::getInt16Ty(g.context);
}
virtual CompilerVariable* call(IREmitter& emitter, const OpInfo& info, VAR* var,
const std::vector<CompilerVariable*>& args) {
virtual CompilerVariable* call(IREmitter& emitter, const OpInfo& info, VAR* var, ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
return undefVariable();
}
virtual void drop(IREmitter& emitter, VAR* var) {}
......@@ -1366,11 +1440,15 @@ public:
}
virtual CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, VAR* var, const std::string* attr,
bool clsonly, const std::vector<CompilerVariable*>& args) {
bool clsonly, ArgPassSpec argspec, const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
return undefVariable();
}
virtual CompilerType* callType(std::vector<CompilerType*>& arg_types) { return UNDEF; }
virtual CompilerType* callType(ArgPassSpec argspec, const std::vector<CompilerType*>& arg_types,
const std::vector<const std::string*>* keyword_names) {
return UNDEF;
}
virtual ConcreteCompilerVariable* nonzero(IREmitter& emitter, const OpInfo& info, VAR* var) {
return new ConcreteCompilerVariable(BOOL, llvm::UndefValue::get(g.i1), true);
......
......@@ -40,7 +40,8 @@ public:
virtual ConcreteCompilerType* getBoxType() = 0;
virtual bool canConvertTo(ConcreteCompilerType* other_type) = 0;
virtual CompilerType* getattrType(const std::string* attr, bool cls_only) = 0;
virtual CompilerType* callType(std::vector<CompilerType*>& arg_types) = 0;
virtual CompilerType* callType(ArgPassSpec argspec, const std::vector<CompilerType*>& arg_types,
const std::vector<const std::string*>* keyword_names) = 0;
virtual BoxedClass* guaranteedClass() = 0;
};
......@@ -93,12 +94,15 @@ public:
abort();
}
virtual CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, VAR* value, const std::string* attr,
bool clsonly, const std::vector<CompilerVariable*>& args) {
bool clsonly, struct ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
printf("callattr not defined for %s\n", debugName().c_str());
abort();
}
virtual CompilerVariable* call(IREmitter& emitter, const OpInfo& info, VAR* value,
const std::vector<CompilerVariable*>& args) {
virtual CompilerVariable* call(IREmitter& emitter, const OpInfo& info, VAR* value, struct ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
printf("call not defined for %s\n", debugName().c_str());
abort();
}
......@@ -122,7 +126,8 @@ public:
printf("getattrType not defined for %s\n", debugName().c_str());
abort();
}
CompilerType* callType(std::vector<CompilerType*>& arg_types) override {
CompilerType* callType(struct ArgPassSpec argspec, const std::vector<CompilerType*>& arg_types,
const std::vector<const std::string*>* keyword_names) override {
printf("callType not defined for %s\n", debugName().c_str());
abort();
}
......@@ -213,9 +218,11 @@ public:
= 0;
virtual void setattr(IREmitter& emitter, const OpInfo& info, const std::string* attr, CompilerVariable* v) = 0;
virtual CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, const std::string* attr, bool clsonly,
const std::vector<CompilerVariable*>& args) = 0;
virtual CompilerVariable* call(IREmitter& emitter, const OpInfo& info, const std::vector<CompilerVariable*>& args)
= 0;
struct ArgPassSpec argspec, const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) = 0;
virtual CompilerVariable* call(IREmitter& emitter, const OpInfo& info, struct ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) = 0;
virtual void print(IREmitter& emitter, const OpInfo& info) = 0;
virtual ConcreteCompilerVariable* len(IREmitter& emitter, const OpInfo& info) = 0;
virtual CompilerVariable* getitem(IREmitter& emitter, const OpInfo& info, CompilerVariable*) = 0;
......@@ -273,12 +280,14 @@ public:
type->setattr(emitter, info, this, attr, v);
}
virtual CompilerVariable* callattr(IREmitter& emitter, const OpInfo& info, const std::string* attr, bool clsonly,
const std::vector<CompilerVariable*>& args) {
return type->callattr(emitter, info, this, attr, clsonly, args);
}
CompilerVariable* call(IREmitter& emitter, const OpInfo& info,
const std::vector<CompilerVariable*>& args) override {
return type->call(emitter, info, this, args);
struct ArgPassSpec argspec, const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) {
return type->callattr(emitter, info, this, attr, clsonly, argspec, args, keyword_names);
}
CompilerVariable* call(IREmitter& emitter, const OpInfo& info, struct ArgPassSpec argspec,
const std::vector<CompilerVariable*>& args,
const std::vector<const std::string*>* keyword_names) override {
return type->call(emitter, info, this, argspec, args, keyword_names);
}
void print(IREmitter& emitter, const OpInfo& info) override { type->print(emitter, info, this); }
ConcreteCompilerVariable* len(IREmitter& emitter, const OpInfo& info) override {
......
......@@ -881,10 +881,10 @@ static std::string getUniqueFunctionName(std::string nameprefix, EffortLevel::Ef
return os.str();
}
CompiledFunction* compileFunction(SourceInfo* source, const OSREntryDescriptor* entry_descriptor,
CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_descriptor,
EffortLevel::EffortLevel effort, FunctionSignature* sig,
const std::vector<AST_expr*>& arg_names, std::string nameprefix) {
Timer _t("in compileFunction");
Timer _t("in doCompile");
if (VERBOSITY("irgen") >= 1)
source->cfg->print();
......
......@@ -76,7 +76,7 @@ public:
const std::vector<llvm::Value*>& args, ExcInfo exc_info) = 0;
};
CompiledFunction* compileFunction(SourceInfo* source, const OSREntryDescriptor* entry_descriptor,
CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_descriptor,
EffortLevel::EffortLevel effort, FunctionSignature* sig,
const std::vector<AST_expr*>& arg_names, std::string nameprefix);
......
......@@ -68,6 +68,7 @@ AST_arguments* SourceInfo::getArgsAST() {
const std::vector<AST_expr*>& SourceInfo::getArgNames() {
static std::vector<AST_expr*> empty;
assert(this);
AST_arguments* args = getArgsAST();
if (args == NULL)
......@@ -87,6 +88,14 @@ const std::vector<AST_stmt*>& SourceInfo::getBody() {
}
}
EffortLevel::EffortLevel initialEffort() {
if (FORCE_OPTIMIZE)
return EffortLevel::MAXIMAL;
if (ENABLE_INTERPRETER)
return EffortLevel::INTERPRETED;
return EffortLevel::MINIMAL;
}
static void compileIR(CompiledFunction* cf, EffortLevel::EffortLevel effort) {
assert(cf);
assert(cf->func);
......@@ -126,9 +135,9 @@ static void compileIR(CompiledFunction* cf, EffortLevel::EffortLevel effort) {
// Compiles a new version of the function with the given signature and adds it to the list;
// should only be called after checking to see if the other versions would work.
static CompiledFunction* _doCompile(CLFunction* f, FunctionSignature* sig, EffortLevel::EffortLevel effort,
CompiledFunction* compileFunction(CLFunction* f, FunctionSignature* sig, EffortLevel::EffortLevel effort,
const OSREntryDescriptor* entry) {
Timer _t("for _doCompile()");
Timer _t("for compileFunction()");
assert(sig);
ASSERT(f->versions.size() < 20, "%ld", f->versions.size());
......@@ -178,7 +187,7 @@ static CompiledFunction* _doCompile(CLFunction* f, FunctionSignature* sig, Effor
source->scoping->getScopeInfoForNode(source->ast));
}
CompiledFunction* cf = compileFunction(source, entry, effort, sig, arg_names, name);
CompiledFunction* cf = doCompile(source, entry, effort, sig, arg_names, name);
compileIR(cf, effort);
f->addVersion(cf);
......@@ -224,14 +233,6 @@ static CompiledFunction* _doCompile(CLFunction* f, FunctionSignature* sig, Effor
return cf;
}
static EffortLevel::EffortLevel initialEffort() {
if (FORCE_OPTIMIZE)
return EffortLevel::MAXIMAL;
if (ENABLE_INTERPRETER)
return EffortLevel::INTERPRETED;
return EffortLevel::MINIMAL;
}
void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
Timer _t("for compileModule()");
......@@ -247,7 +248,8 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
EffortLevel::EffortLevel effort = initialEffort();
CompiledFunction* cf = _doCompile(cl_f, new FunctionSignature(VOID, false), effort, NULL);
CompiledFunction* cf
= compileFunction(cl_f, new FunctionSignature(VOID, &si->getArgNames(), 0, false, false), effort, NULL);
assert(cf->clfunc->versions.size());
_t.end();
......@@ -279,7 +281,7 @@ static CompiledFunction* _doReopt(CompiledFunction* cf, EffortLevel::EffortLevel
versions.erase(versions.begin() + i);
CompiledFunction* new_cf
= _doCompile(clfunc, cf->sig, new_effort,
= compileFunction(clfunc, cf->sig, new_effort,
NULL); // this pushes the new CompiledVersion to the back of the version list
cf->dependent_callsites.invalidateAll();
......@@ -308,7 +310,8 @@ void* compilePartialFunc(OSRExit* exit) {
new_effort = EffortLevel::MINIMAL;
// EffortLevel::EffortLevel new_effort = (EffortLevel::EffortLevel)(exit->parent_cf->effort + 1);
// new_effort = EffortLevel::MAXIMAL;
CompiledFunction* compiled = _doCompile(exit->parent_cf->clfunc, exit->parent_cf->sig, new_effort, exit->entry);
CompiledFunction* compiled
= compileFunction(exit->parent_cf->clfunc, exit->parent_cf->sig, new_effort, exit->entry);
assert(compiled = new_cf);
}
......@@ -328,187 +331,22 @@ extern "C" char* reoptCompiledFunc(CompiledFunction* cf) {
return (char*)new_cf->code;
}
CompiledFunction* resolveCLFunc(CLFunction* f, int64_t nargs, Box* arg1, Box* arg2, Box* arg3, Box** args) {
static StatCounter slowpath_resolveclfunc("slowpath_resolveclfunc");
slowpath_resolveclfunc.log();
FunctionList& versions = f->versions;
for (int i = 0; i < versions.size(); i++) {
CompiledFunction* cf = versions[i];
FunctionSignature* sig = cf->sig;
if (sig->rtn_type->llvmType() != g.llvm_value_type_ptr)
continue;
if ((!sig->is_vararg && sig->arg_types.size() != nargs) || (sig->is_vararg && nargs < sig->arg_types.size()))
continue;
int nsig_args = sig->arg_types.size();
if (nsig_args >= 1) {
if (sig->arg_types[0]->isFitBy(arg1->cls)) {
// pass
} else {
continue;
}
}
if (nsig_args >= 2) {
if (sig->arg_types[1]->isFitBy(arg2->cls)) {
// pass
} else {
continue;
}
}
if (nsig_args >= 3) {
if (sig->arg_types[2]->isFitBy(arg3->cls)) {
// pass
} else {
continue;
}
}
bool bad = false;
for (int j = 3; j < nsig_args; j++) {
if (sig->arg_types[j]->isFitBy(args[j - 3]->cls)) {
// pass
} else {
bad = true;
break;
}
}
if (bad)
continue;
assert(cf);
assert(!cf->entry_descriptor);
assert(cf->is_interpreted == (cf->code == NULL));
return cf;
}
if (f->source == NULL) {
printf("Error: couldn't find suitable function version and no source to recompile!\n");
printf("%ld args:", nargs);
for (int i = 0; i < nargs; i++) {
if (i == 3) {
printf(" [and more]");
break;
}
Box* firstargs[] = { arg1, arg2, arg3 };
printf(" %s", getTypeName(firstargs[i])->c_str());
}
printf("\n");
for (int j = 0; j < f->versions.size(); j++) {
std::string func_name = g.func_addr_registry.getFuncNameAtAddress(f->versions[j]->code, true);
printf("Version %d, %s:", j, func_name.c_str());
FunctionSignature* sig = f->versions[j]->sig;
for (int i = 0; i < sig->arg_types.size(); i++) {
printf(" %s", sig->arg_types[i]->debugName().c_str());
}
if (sig->is_vararg)
printf(" *vararg");
printf("\n");
// printf(" @%p %s\n", f->versions[j]->code, func_name.c_str());
}
abort();
}
assert(f->source->getArgsAST()->vararg.size() == 0);
bool is_vararg = false;
std::vector<ConcreteCompilerType*> arg_types;
if (nargs >= 1) {
arg_types.push_back(typeFromClass(arg1->cls));
}
if (nargs >= 2) {
arg_types.push_back(typeFromClass(arg2->cls));
}
if (nargs >= 3) {
arg_types.push_back(typeFromClass(arg3->cls));
}
for (int j = 3; j < nargs; j++) {
arg_types.push_back(typeFromClass(args[j - 3]->cls));
}
FunctionSignature* sig = new FunctionSignature(UNKNOWN, arg_types, is_vararg);
EffortLevel::EffortLevel new_effort = initialEffort();
CompiledFunction* cf
= _doCompile(f, sig, new_effort, NULL); // this pushes the new CompiledVersion to the back of the version list
assert(cf->is_interpreted == (cf->code == NULL));
return cf;
}
Box* callCompiledFunc(CompiledFunction* cf, int64_t nargs, Box* arg1, Box* arg2, Box* arg3, Box** args) {
assert(cf);
// TODO these shouldn't have to be initialized, but I don't want to issue a #pragma
// that disables the warning for the whole file:
Box* rarg1 = arg1, *rarg2 = arg2, *rarg3 = arg3;
Box** rargs = NULL;
if (nargs > 3) {
if (cf->sig->is_vararg) {
// the +2 is for the varargs and kwargs
rargs = (Box**)alloca((nargs - 3 + 2) * sizeof(Box*));
memcpy(rargs, args, (nargs - 3) * sizeof(Box*));
} else {
rargs = args;
}
assert(rargs);
}
int nsig_args = cf->sig->arg_types.size();
BoxedList* made_vararg = NULL;
if (cf->sig->is_vararg) {
made_vararg = (BoxedList*)createList();
if (nsig_args == 0)
rarg1 = made_vararg;
else if (nsig_args == 1)
rarg2 = made_vararg;
else if (nsig_args == 2)
rarg3 = made_vararg;
else
rargs[nsig_args - 3] = made_vararg;
for (int i = nsig_args; i < nargs; i++) {
if (i == 0)
listAppendInternal(made_vararg, arg1);
else if (i == 1)
listAppendInternal(made_vararg, arg2);
else if (i == 2)
listAppendInternal(made_vararg, arg3);
else
listAppendInternal(made_vararg, args[i - 3]);
}
}
if (!cf->is_interpreted) {
if (cf->sig->is_vararg) {
Box* rtn = cf->call(rarg1, rarg2, rarg3, rargs);
return rtn;
} else {
return cf->call(rarg1, rarg2, rarg3, rargs);
}
} else {
return interpretFunction(cf->func, nargs, rarg1, rarg2, rarg3, rargs);
}
}
CLFunction* createRTFunction() {
return new CLFunction(NULL);
}
extern "C" CLFunction* boxRTFunction(void* f, ConcreteCompilerType* rtn_type, int nargs, bool is_vararg) {
extern "C" CLFunction* boxRTFunction(void* f, ConcreteCompilerType* rtn_type, int nargs, bool takes_varargs,
bool takes_kwargs) {
CLFunction* cl_f = createRTFunction();
addRTFunction(cl_f, f, rtn_type, nargs, is_vararg);
addRTFunction(cl_f, f, rtn_type, nargs, takes_varargs, takes_kwargs);
return cl_f;
}
void addRTFunction(CLFunction* cl_f, void* f, ConcreteCompilerType* rtn_type, int nargs, bool is_vararg) {
void addRTFunction(CLFunction* cl_f, void* f, ConcreteCompilerType* rtn_type, int nargs, bool takes_varargs,
bool takes_kwargs) {
std::vector<ConcreteCompilerType*> arg_types(nargs, NULL);
return addRTFunction(cl_f, f, rtn_type, arg_types, is_vararg);
return addRTFunction(cl_f, f, rtn_type, arg_types, takes_varargs, takes_kwargs);
}
static ConcreteCompilerType* processType(ConcreteCompilerType* type) {
......@@ -518,11 +356,24 @@ static ConcreteCompilerType* processType(ConcreteCompilerType* type) {
}
void addRTFunction(CLFunction* cl_f, void* f, ConcreteCompilerType* rtn_type,
const std::vector<ConcreteCompilerType*>& arg_types, bool is_vararg) {
FunctionSignature* sig = new FunctionSignature(processType(rtn_type), is_vararg);
std::vector<llvm::Type*> llvm_arg_types;
const std::vector<ConcreteCompilerType*>& arg_types, bool takes_varargs, bool takes_kwargs) {
FunctionSignature* sig = new FunctionSignature(processType(rtn_type), NULL, 0, takes_varargs, takes_kwargs);
for (int i = 0; i < arg_types.size(); i++) {
sig->arg_types.push_back(processType(arg_types[i]));
}
std::vector<llvm::Type*> llvm_arg_types;
int npassed_args = arg_types.size();
if (takes_varargs)
npassed_args++;
if (takes_kwargs)
npassed_args++;
for (int i = 0; i < npassed_args; i++) {
if (i == 3) {
llvm_arg_types.push_back(g.llvm_value_type_ptr->getPointerTo());
break;
}
llvm_arg_types.push_back(g.llvm_value_type_ptr);
}
......
......@@ -190,6 +190,17 @@ IREmitter* createIREmitter(IRGenState* irstate, llvm::BasicBlock*& curblock) {
return new IREmitterImpl(irstate, curblock);
}
static std::unordered_map<AST_expr*, std::vector<const std::string*>*> made_keyword_storage;
static std::vector<const std::string*>* getKeywordNameStorage(AST_Call* node) {
auto it = made_keyword_storage.find(node);
if (it != made_keyword_storage.end())
return it->second;
auto rtn = new std::vector<const std::string*>();
made_keyword_storage.insert(it, std::make_pair(node, rtn));
return rtn;
}
class IRGeneratorImpl : public IRGenerator {
private:
IRGenState* irstate;
......@@ -623,10 +634,6 @@ private:
CompilerVariable* evalCall(AST_Call* node, ExcInfo exc_info) {
assert(state != PARTIAL);
assert(!node->starargs);
assert(!node->kwargs);
assert(!node->keywords.size());
bool is_callattr;
bool callattr_clsonly = false;
std::string* attr = NULL;
......@@ -649,19 +656,42 @@ private:
}
std::vector<CompilerVariable*> args;
std::vector<const std::string*>* keyword_names;
if (node->keywords.size()) {
keyword_names = getKeywordNameStorage(node);
} else {
keyword_names = NULL;
}
for (int i = 0; i < node->args.size(); i++) {
CompilerVariable* a = evalExpr(node->args[i], exc_info);
args.push_back(a);
}
for (int i = 0; i < node->keywords.size(); i++) {
CompilerVariable* a = evalExpr(node->keywords[i]->value, exc_info);
args.push_back(a);
keyword_names->push_back(&node->keywords[i]->arg);
}
if (node->starargs)
args.push_back(evalExpr(node->starargs, exc_info));
if (node->kwargs)
args.push_back(evalExpr(node->kwargs, exc_info));
struct ArgPassSpec argspec(node->args.size(), node->keywords.size(), node->starargs != NULL,
node->kwargs != NULL);
// if (VERBOSITY("irgen") >= 1)
//_addAnnotation("before_call");
CompilerVariable* rtn;
if (is_callattr) {
rtn = func->callattr(emitter, getOpInfoForNode(node, exc_info), attr, callattr_clsonly, args);
rtn = func->callattr(emitter, getOpInfoForNode(node, exc_info), attr, callattr_clsonly, argspec, args,
keyword_names);
} else {
rtn = func->call(emitter, getOpInfoForNode(node, exc_info), args);
rtn = func->call(emitter, getOpInfoForNode(node, exc_info), argspec, args, keyword_names);
}
func->decvref(emitter);
......@@ -669,9 +699,6 @@ private:
args[i]->decvref(emitter);
}
// if (VERBOSITY("irgen") >= 1)
//_addAnnotation("end_of_call");
return rtn;
}
......@@ -693,7 +720,7 @@ private:
args.push_back(key);
args.push_back(value);
// TODO could use the internal _listAppend function to avoid incref/decref'ing None
CompilerVariable* rtn = setitem->call(emitter, getEmptyOpInfo(exc_info), args);
CompilerVariable* rtn = setitem->call(emitter, getEmptyOpInfo(exc_info), ArgPassSpec(2), args, NULL);
rtn->decvref(emitter);
key->decvref(emitter);
......
......@@ -642,12 +642,18 @@ Box* interpretFunction(llvm::Function* f, int nargs, Box* arg1, Box* arg2, Box*
int64_t)>(f)(args[0].n, args[1].n, args[2].n, args[3].n,
args[4].n, args[5].n, args[6].n);
break;
case 0b1000000000:
case 0b1000000000: // 512
r = reinterpret_cast<int64_t (*)(int64_t, int64_t, int64_t, int64_t, int64_t, int64_t,
int64_t, int64_t)>(f)(args[0].n, args[1].n, args[2].n,
args[3].n, args[4].n, args[5].n,
args[6].n, args[7].n);
break;
case 0b10000000000: // 1024
r = reinterpret_cast<int64_t (*)(int64_t, int64_t, int64_t, int64_t, int64_t, int64_t,
int64_t, int64_t, int64_t)>(
f)(args[0].n, args[1].n, args[2].n, args[3].n, args[4].n, args[5].n, args[6].n,
args[7].n, args[8].n);
break;
default:
inst->dump();
RELEASE_ASSERT(0, "%d", mask);
......
......@@ -132,6 +132,9 @@ void initGlobalFuncs(GlobalState& g) {
g.llvm_str_type_ptr = lookupFunction("boxStringPtr")->arg_begin()->getType();
auto vector_type = g.stdlib_module->getTypeByName("class.std::vector");
assert(vector_type);
g.vector_ptr = vector_type->getPointerTo();
#define GET(N) g.funcs.N = getFunc((void*)N, STRINGIFY(N))
......@@ -185,23 +188,23 @@ void initGlobalFuncs(GlobalState& g) {
GET(listAppendInternal);
g.funcs.runtimeCall = getFunc((void*)runtimeCall, "runtimeCall");
g.funcs.runtimeCall0 = addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i64);
g.funcs.runtimeCall0 = addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i32);
g.funcs.runtimeCall1
= addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i64, g.llvm_value_type_ptr);
g.funcs.runtimeCall2 = addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i64,
= addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i32, g.llvm_value_type_ptr);
g.funcs.runtimeCall2 = addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i32,
g.llvm_value_type_ptr, g.llvm_value_type_ptr);
g.funcs.runtimeCall3 = addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i64,
g.funcs.runtimeCall3 = addFunc((void*)runtimeCall, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.i32,
g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.llvm_value_type_ptr);
g.funcs.callattr = getFunc((void*)callattr, "callattr");
g.funcs.callattr0
= addFunc((void*)callattr, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.llvm_str_type_ptr, g.i1, g.i64);
= addFunc((void*)callattr, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.llvm_str_type_ptr, g.i1, g.i32);
g.funcs.callattr1 = addFunc((void*)callattr, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.llvm_str_type_ptr,
g.i1, g.i64, g.llvm_value_type_ptr);
g.i1, g.i32, g.llvm_value_type_ptr);
g.funcs.callattr2 = addFunc((void*)callattr, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.llvm_str_type_ptr,
g.i1, g.i64, g.llvm_value_type_ptr, g.llvm_value_type_ptr);
g.i1, g.i32, g.llvm_value_type_ptr, g.llvm_value_type_ptr);
g.funcs.callattr3 = addFunc((void*)callattr, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.llvm_str_type_ptr,
g.i1, g.i64, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.llvm_value_type_ptr);
g.i1, g.i32, g.llvm_value_type_ptr, g.llvm_value_type_ptr, g.llvm_value_type_ptr);
g.funcs.reoptCompiledFunc = addFunc((void*)reoptCompiledFunc, g.i8_ptr, g.i8_ptr);
g.funcs.compilePartialFunc = addFunc((void*)compilePartialFunc, g.i8_ptr, g.i8_ptr);
......
......@@ -32,6 +32,31 @@ class Value;
namespace pyston {
struct ArgPassSpec {
bool has_starargs : 1;
bool has_kwargs : 1;
unsigned int num_keywords : 14;
unsigned int num_args : 16;
static const int MAX_ARGS = (1 << 16) - 1;
static const int MAX_KEYWORDS = (1 << 14) - 1;
explicit ArgPassSpec(int num_args) : has_starargs(false), has_kwargs(false), num_keywords(0), num_args(num_args) {
assert(num_args <= MAX_ARGS);
assert(num_keywords <= MAX_KEYWORDS);
}
explicit ArgPassSpec(int num_args, int num_keywords, bool has_starargs, bool has_kwargs)
: has_starargs(has_starargs), has_kwargs(has_kwargs), num_keywords(num_keywords), num_args(num_args) {
assert(num_args <= MAX_ARGS);
assert(num_keywords <= MAX_KEYWORDS);
}
int totalPassed() { return num_args + num_keywords + (has_starargs ? 1 : 0) + (has_kwargs ? 1 : 0); }
uintptr_t asInt() const { return *reinterpret_cast<const uintptr_t*>(this); }
};
static_assert(sizeof(ArgPassSpec) <= sizeof(void*), "ArgPassSpec doesn't fit in register!");
class GCVisitor {
public:
virtual ~GCVisitor() {}
......@@ -129,20 +154,30 @@ public:
struct FunctionSignature {
ConcreteCompilerType* rtn_type;
const std::vector<AST_expr*>* arg_names;
std::vector<ConcreteCompilerType*> arg_types;
bool is_vararg;
FunctionSignature(ConcreteCompilerType* rtn_type, bool is_vararg) : rtn_type(rtn_type), is_vararg(is_vararg) {}
FunctionSignature(ConcreteCompilerType* rtn_type, ConcreteCompilerType* arg1, ConcreteCompilerType* arg2,
bool is_vararg)
: rtn_type(rtn_type), is_vararg(is_vararg) {
int ndefaults;
bool takes_varargs, takes_kwargs;
FunctionSignature(ConcreteCompilerType* rtn_type, const std::vector<AST_expr*>* arg_names, int ndefaults,
bool takes_varargs, bool takes_kwargs)
: rtn_type(rtn_type), arg_names(arg_names), ndefaults(ndefaults), takes_varargs(takes_varargs),
takes_kwargs(takes_kwargs) {}
FunctionSignature(ConcreteCompilerType* rtn_type, const std::vector<AST_expr*>* arg_names,
ConcreteCompilerType* arg1, ConcreteCompilerType* arg2, int ndefaults, bool takes_varargs,
bool takes_kwargs)
: rtn_type(rtn_type), arg_names(arg_names), ndefaults(ndefaults), takes_varargs(takes_varargs),
takes_kwargs(takes_kwargs) {
arg_types.push_back(arg1);
arg_types.push_back(arg2);
}
FunctionSignature(ConcreteCompilerType* rtn_type, std::vector<ConcreteCompilerType*>& arg_types, bool is_vararg)
: rtn_type(rtn_type), arg_types(arg_types), is_vararg(is_vararg) {}
FunctionSignature(ConcreteCompilerType* rtn_type, const std::vector<AST_expr*>* arg_names,
std::vector<ConcreteCompilerType*>& arg_types, int ndefaults, bool takes_varargs,
bool takes_kwargs)
: rtn_type(rtn_type), arg_names(arg_names), arg_types(arg_types), ndefaults(ndefaults),
takes_varargs(takes_varargs), takes_kwargs(takes_kwargs) {}
};
struct CompiledFunction {
......@@ -190,13 +225,23 @@ public:
SourceInfo(BoxedModule* m, ScopingAnalysis* scoping)
: parent_module(m), scoping(scoping), ast(NULL), cfg(NULL), liveness(NULL), phis(NULL) {}
};
typedef std::vector<CompiledFunction*> FunctionList;
class CallRewriteArgs;
struct CLFunction {
SourceInfo* source;
FunctionList
versions; // any compiled versions along with their type parameters; in order from most preferred to least
std::unordered_map<const OSREntryDescriptor*, CompiledFunction*> osr_versions;
// Functions can provide an "internal" version, which will get called instead
// of the normal dispatch through the functionlist.
// This can be used to implement functions which know how to rewrite themselves,
// such as typeCall.
typedef Box* (*InternalCallable)(BoxedFunction*, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*, Box**,
const std::vector<const std::string*>*);
InternalCallable internal_callable = NULL;
CLFunction(SourceInfo* source) : source(source) {}
void addVersion(CompiledFunction* compiled) {
......@@ -215,14 +260,20 @@ struct CLFunction {
};
extern "C" CLFunction* createRTFunction();
extern "C" CLFunction* boxRTFunction(void* f, ConcreteCompilerType* rtn_type, int nargs, bool is_vararg);
void addRTFunction(CLFunction* cf, void* f, ConcreteCompilerType* rtn_type, int nargs, bool is_vararg);
extern "C" CLFunction* boxRTFunction(void* f, ConcreteCompilerType* rtn_type, int nargs, bool takes_varargs = false,
bool takes_kwargs = false);
void addRTFunction(CLFunction* cf, void* f, ConcreteCompilerType* rtn_type, int nargs, bool takes_varargs = false,
bool takes_kwargs = false);
void addRTFunction(CLFunction* cf, void* f, ConcreteCompilerType* rtn_type,
const std::vector<ConcreteCompilerType*>& arg_types, bool is_vararg);
const std::vector<ConcreteCompilerType*>& arg_types, bool takes_varargs = false,
bool takes_kwargs = false);
CLFunction* unboxRTFunction(Box*);
// extern "C" CLFunction* boxRTFunctionVariadic(const char* name, int nargs_min, int nargs_max, void* f);
extern "C" CompiledFunction* resolveCLFunc(CLFunction* f, int64_t nargs, Box* arg1, Box* arg2, Box* arg3, Box** args);
extern "C" Box* callCompiledFunc(CompiledFunction* cf, int64_t nargs, Box* arg1, Box* arg2, Box* arg3, Box** args);
// Compiles a new version of the function with the given signature and adds it to the list;
// should only be called after checking to see if the other versions would work.
CompiledFunction* compileFunction(CLFunction* f, FunctionSignature* sig, EffortLevel::EffortLevel effort,
const OSREntryDescriptor* entry);
EffortLevel::EffortLevel initialEffort();
typedef bool i1;
typedef int64_t i64;
......
......@@ -40,7 +40,8 @@ extern "C" Box* dir1(Box* obj) {
BoxedList* result = nullptr;
// If __dir__ is present just call it and return what it returns
static std::string attr_dir = "__dir__";
Box* dir_result = callattrInternal(obj, &attr_dir, CLASS_ONLY, nullptr, 0, nullptr, nullptr, nullptr, nullptr);
Box* dir_result = callattrInternal(obj, &attr_dir, CLASS_ONLY, nullptr, ArgPassSpec(0), nullptr, nullptr, nullptr,
nullptr, nullptr);
if (dir_result && dir_result->cls == list_cls) {
return dir_result;
}
......@@ -366,7 +367,7 @@ Box* hasattr(Box* obj, Box* _str) {
Box* map2(Box* f, Box* container) {
Box* rtn = new BoxedList();
for (Box* e : container->pyElements()) {
listAppendInternal(rtn, runtimeCall(f, 1, e, NULL, NULL, NULL));
listAppendInternal(rtn, runtimeCall(f, ArgPassSpec(1), e, NULL, NULL, NULL, NULL));
}
return rtn;
}
......
......@@ -27,7 +27,9 @@
#include "asm_writing/icinfo.h"
#include "asm_writing/rewriter.h"
#include "asm_writing/rewriter2.h"
#include "codegen/compvars.h"
#include "codegen/irgen/hooks.h"
#include "codegen/llvm_interpreter.h"
#include "codegen/parser.h"
#include "codegen/type_recording.h"
#include "core/ast.h"
......@@ -144,24 +146,23 @@ struct CompareRewriteArgs {
: rewriter(rewriter), lhs(lhs), rhs(rhs), out_success(false) {}
};
Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, int64_t nargs, Box* arg1, Box* arg2, Box* arg3,
Box** args);
static Box* (*runtimeCallInternal0)(Box*, CallRewriteArgs*, int64_t)
= (Box * (*)(Box*, CallRewriteArgs*, int64_t))runtimeCallInternal;
static Box* (*runtimeCallInternal1)(Box*, CallRewriteArgs*, int64_t, Box*)
= (Box * (*)(Box*, CallRewriteArgs*, int64_t, Box*))runtimeCallInternal;
static Box* (*runtimeCallInternal2)(Box*, CallRewriteArgs*, int64_t, Box*, Box*)
= (Box * (*)(Box*, CallRewriteArgs*, int64_t, Box*, Box*))runtimeCallInternal;
static Box* (*runtimeCallInternal3)(Box*, CallRewriteArgs*, int64_t, Box*, Box*, Box*)
= (Box * (*)(Box*, CallRewriteArgs*, int64_t, Box*, Box*, Box*))runtimeCallInternal;
Box* typeCallInternal(CallRewriteArgs* rewrite_args, int64_t nargs, Box* obj, Box* arg1, Box* arg2, Box** args);
static Box* (*typeCallInternal1)(CallRewriteArgs* rewrite_args, int64_t nargs, Box*)
= (Box * (*)(CallRewriteArgs*, int64_t, Box*))typeCallInternal;
static Box* (*typeCallInternal2)(CallRewriteArgs* rewrite_args, int64_t nargs, Box*, Box*)
= (Box * (*)(CallRewriteArgs*, int64_t, Box*, Box*))typeCallInternal;
static Box* (*typeCallInternal3)(CallRewriteArgs* rewrite_args, int64_t nargs, Box*, Box*, Box*)
= (Box * (*)(CallRewriteArgs*, int64_t, Box*, Box*, Box*))typeCallInternal;
Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3,
Box** args, const std::vector<const std::string*>* keyword_names);
static Box* (*runtimeCallInternal0)(Box*, CallRewriteArgs*, ArgPassSpec)
= (Box * (*)(Box*, CallRewriteArgs*, ArgPassSpec))runtimeCallInternal;
static Box* (*runtimeCallInternal1)(Box*, CallRewriteArgs*, ArgPassSpec, Box*)
= (Box * (*)(Box*, CallRewriteArgs*, ArgPassSpec, Box*))runtimeCallInternal;
static Box* (*runtimeCallInternal2)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, Box*)
= (Box * (*)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, Box*))runtimeCallInternal;
static Box* (*runtimeCallInternal3)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*)
= (Box * (*)(Box*, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*))runtimeCallInternal;
static Box* (*typeCallInternal1)(BoxedFunction*, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box*)
= (Box * (*)(BoxedFunction*, CallRewriteArgs*, ArgPassSpec, Box*))typeCallInternal;
static Box* (*typeCallInternal2)(BoxedFunction*, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box*, Box*)
= (Box * (*)(BoxedFunction*, CallRewriteArgs*, ArgPassSpec, Box*, Box*))typeCallInternal;
static Box* (*typeCallInternal3)(BoxedFunction*, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box*, Box*, Box*)
= (Box * (*)(BoxedFunction*, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*))typeCallInternal;
bool checkClass(LookupScope scope) {
return (scope & CLASS_ONLY) != 0;
......@@ -169,14 +170,15 @@ bool checkClass(LookupScope scope) {
bool checkInst(LookupScope scope) {
return (scope & INST_ONLY) != 0;
}
static Box* (*callattrInternal0)(Box*, const std::string*, LookupScope, CallRewriteArgs*, int64_t)
= (Box * (*)(Box*, const std::string*, LookupScope, CallRewriteArgs*, int64_t))callattrInternal;
static Box* (*callattrInternal1)(Box*, const std::string*, LookupScope, CallRewriteArgs*, int64_t, Box*)
= (Box * (*)(Box*, const std::string*, LookupScope, CallRewriteArgs*, int64_t, Box*))callattrInternal;
static Box* (*callattrInternal2)(Box*, const std::string*, LookupScope, CallRewriteArgs*, int64_t, Box*, Box*)
= (Box * (*)(Box*, const std::string*, LookupScope, CallRewriteArgs*, int64_t, Box*, Box*))callattrInternal;
static Box* (*callattrInternal3)(Box*, const std::string*, LookupScope, CallRewriteArgs*, int64_t, Box*, Box*, Box*)
= (Box * (*)(Box*, const std::string*, LookupScope, CallRewriteArgs*, int64_t, Box*, Box*, Box*))callattrInternal;
static Box* (*callattrInternal0)(Box*, const std::string*, LookupScope, CallRewriteArgs*, ArgPassSpec)
= (Box * (*)(Box*, const std::string*, LookupScope, CallRewriteArgs*, ArgPassSpec))callattrInternal;
static Box* (*callattrInternal1)(Box*, const std::string*, LookupScope, CallRewriteArgs*, ArgPassSpec, Box*)
= (Box * (*)(Box*, const std::string*, LookupScope, CallRewriteArgs*, ArgPassSpec, Box*))callattrInternal;
static Box* (*callattrInternal2)(Box*, const std::string*, LookupScope, CallRewriteArgs*, ArgPassSpec, Box*, Box*)
= (Box * (*)(Box*, const std::string*, LookupScope, CallRewriteArgs*, ArgPassSpec, Box*, Box*))callattrInternal;
static Box* (*callattrInternal3)(Box*, const std::string*, LookupScope, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*)
= (Box
* (*)(Box*, const std::string*, LookupScope, CallRewriteArgs*, ArgPassSpec, Box*, Box*, Box*))callattrInternal;
size_t PyHasher::operator()(Box* b) const {
if (b->cls == str_cls) {
......@@ -714,10 +716,11 @@ RELEASE_ASSERT(gotten, "%s:%s", getTypeName(obj)->c_str(), attr);
return gotten;
}
static Box* (*runtimeCall0)(Box*, int64_t) = (Box * (*)(Box*, int64_t))runtimeCall;
static Box* (*runtimeCall1)(Box*, int64_t, Box*) = (Box * (*)(Box*, int64_t, Box*))runtimeCall;
static Box* (*runtimeCall2)(Box*, int64_t, Box*, Box*) = (Box * (*)(Box*, int64_t, Box*, Box*))runtimeCall;
static Box* (*runtimeCall3)(Box*, int64_t, Box*, Box*, Box*) = (Box * (*)(Box*, int64_t, Box*, Box*, Box*))runtimeCall;
static Box* (*runtimeCall0)(Box*, ArgPassSpec) = (Box * (*)(Box*, ArgPassSpec))runtimeCall;
static Box* (*runtimeCall1)(Box*, ArgPassSpec, Box*) = (Box * (*)(Box*, ArgPassSpec, Box*))runtimeCall;
static Box* (*runtimeCall2)(Box*, ArgPassSpec, Box*, Box*) = (Box * (*)(Box*, ArgPassSpec, Box*, Box*))runtimeCall;
static Box* (*runtimeCall3)(Box*, ArgPassSpec, Box*, Box*, Box*)
= (Box * (*)(Box*, ArgPassSpec, Box*, Box*, Box*))runtimeCall;
Box* getattr_internal(Box* obj, const std::string& attr, bool check_cls, bool allow_custom,
GetattrRewriteArgs* rewrite_args, GetattrRewriteArgs2* rewrite_args2) {
......@@ -728,7 +731,7 @@ Box* getattr_internal(Box* obj, const std::string& attr, bool check_cls, bool al
if (getattribute) {
// TODO this is a good candidate for interning?
Box* boxstr = boxString(attr);
Box* rtn = runtimeCall1(getattribute, 1, boxstr);
Box* rtn = runtimeCall1(getattribute, ArgPassSpec(1), boxstr);
return rtn;
}
......@@ -795,7 +798,7 @@ Box* getattr_internal(Box* obj, const std::string& attr, bool check_cls, bool al
Box* getattr = getclsattr_internal(obj, "__getattr__", NULL, NULL);
if (getattr) {
Box* boxstr = boxString(attr);
Box* rtn = runtimeCall1(getattr, 1, boxstr);
Box* rtn = runtimeCall1(getattr, ArgPassSpec(1), boxstr);
return rtn;
}
......@@ -991,7 +994,7 @@ extern "C" bool nonzero(Box* obj) {
return true;
}
Box* r = runtimeCall0(func, 0);
Box* r = runtimeCall0(func, ArgPassSpec(0));
if (r->cls == bool_cls) {
BoxedBool* b = static_cast<BoxedBool*>(r);
bool rtn = b->b;
......@@ -1019,7 +1022,7 @@ extern "C" BoxedString* str(Box* obj) {
snprintf(buf, 80, "<%s object at %p>", getTypeName(obj)->c_str(), obj);
return boxStrConstant(buf);
} else {
obj = runtimeCallInternal0(str, NULL, 0);
obj = runtimeCallInternal0(str, NULL, ArgPassSpec(0));
}
}
if (obj->cls != str_cls) {
......@@ -1045,7 +1048,7 @@ extern "C" Box* repr(Box* obj) {
}
return boxStrConstant(buf);
} else {
obj = runtimeCall0(repr, 0);
obj = runtimeCall0(repr, ArgPassSpec(0));
}
if (obj->cls != str_cls) {
......@@ -1100,7 +1103,7 @@ extern "C" BoxedInt* hash(Box* obj) {
return static_cast<BoxedInt*>(boxInt((i64)obj));
}
Box* rtn = runtimeCall0(hash, 0);
Box* rtn = runtimeCall0(hash, ArgPassSpec(0));
if (rtn->cls != int_cls) {
raiseExcHelper(TypeError, "an integer is required");
}
......@@ -1113,13 +1116,13 @@ extern "C" BoxedInt* lenInternal(Box* obj, LenRewriteArgs* rewrite_args) {
if (rewrite_args) {
CallRewriteArgs crewrite_args(rewrite_args->rewriter, rewrite_args->obj);
crewrite_args.preferred_dest_reg = rewrite_args->preferred_dest_reg;
rtn = callattrInternal0(obj, &attr_str, CLASS_ONLY, &crewrite_args, 0);
rtn = callattrInternal0(obj, &attr_str, CLASS_ONLY, &crewrite_args, ArgPassSpec(0));
if (!crewrite_args.out_success)
rewrite_args = NULL;
else if (rtn)
rewrite_args->out_rtn = crewrite_args.out_rtn;
} else {
rtn = callattrInternal0(obj, &attr_str, CLASS_ONLY, NULL, 0);
rtn = callattrInternal0(obj, &attr_str, CLASS_ONLY, NULL, ArgPassSpec(0));
}
if (rtn == NULL) {
......@@ -1218,14 +1221,21 @@ extern "C" void dump(void* p) {
// For rewriting purposes, this function assumes that nargs will be constant.
// That's probably fine for some uses (ex binops), but otherwise it should be guarded on beforehand.
extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope scope, CallRewriteArgs* rewrite_args,
int64_t nargs, Box* arg1, Box* arg2, Box* arg3, Box** args) {
ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args,
const std::vector<const std::string*>* keyword_names) {
RELEASE_ASSERT(!argspec.has_starargs, "");
RELEASE_ASSERT(!argspec.has_kwargs, "");
RELEASE_ASSERT(argspec.num_keywords == 0, "");
int npassed_args = argspec.totalPassed();
if (rewrite_args) {
// if (VERBOSITY()) {
// printf("callattrInternal: %d", rewrite_args->obj.getArgnum());
// if (nargs >= 1) printf(" %d", rewrite_args->arg1.getArgnum());
// if (nargs >= 2) printf(" %d", rewrite_args->arg2.getArgnum());
// if (nargs >= 3) printf(" %d", rewrite_args->arg3.getArgnum());
// if (nargs >= 4) printf(" %d", rewrite_args->args.getArgnum());
// if (npassed_args >= 1) printf(" %d", rewrite_args->arg1.getArgnum());
// if (npassed_args >= 2) printf(" %d", rewrite_args->arg2.getArgnum());
// if (npassed_args >= 3) printf(" %d", rewrite_args->arg3.getArgnum());
// if (npassed_args >= 4) printf(" %d", rewrite_args->args.getArgnum());
// printf("\n");
//}
if (rewrite_args->obj.getArgnum() == -1) {
......@@ -1240,19 +1250,19 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
// already fit, either since the type inferencer could determine that,
// or because they only need to fit into an UNKNOWN slot.
if (nargs >= 1)
if (npassed_args >= 1)
rewrite_args->arg1.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)arg1->cls);
if (nargs >= 2)
if (npassed_args >= 2)
rewrite_args->arg2.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)arg2->cls);
// Have to move(-1) since the arg is (probably/maybe) on the stack;
// TODO ideally would handle that case, but for now just do the move() which
// it knows how to handle
if (nargs >= 3)
if (npassed_args >= 3)
rewrite_args->arg3.move(-2).addAttrGuard(BOX_CLS_OFFSET, (intptr_t)arg3->cls);
if (nargs > 3) {
if (npassed_args > 3) {
RewriterVar r_args = rewrite_args->args.move(-3);
for (int i = 3; i < nargs; i++) {
for (int i = 3; i < npassed_args; i++) {
// TODO if there are a lot of args (>16), might be better to increment a pointer
// rather index them directly?
r_args.getAttr((i - 3) * sizeof(Box*), -2).addAttrGuard(BOX_CLS_OFFSET, (intptr_t)args[i - 3]->cls);
......@@ -1290,13 +1300,13 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
r_instattr.addGuard((intptr_t)inst_attr);
rewrite_args->func_guarded = true;
rtn = runtimeCallInternal(inst_attr, rewrite_args, nargs, arg1, arg2, arg3, args);
rtn = runtimeCallInternal(inst_attr, rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names);
if (rewrite_args->out_success) {
r_instattr = rewrite_args->rewriter->pop(0);
}
} else {
rtn = runtimeCallInternal(inst_attr, NULL, nargs, arg1, arg2, arg3, args);
rtn = runtimeCallInternal(inst_attr, NULL, argspec, arg1, arg2, arg3, args, keyword_names);
}
if (!rtn) {
......@@ -1341,23 +1351,26 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
// TODO copy from runtimeCall
// TODO these two branches could probably be folded together (the first one is becoming
// a subset of the second)
if (nargs <= 2) {
if (npassed_args <= 2) {
Box* rtn;
if (rewrite_args) {
CallRewriteArgs srewrite_args(rewrite_args->rewriter, r_clsattr);
srewrite_args.arg1 = rewrite_args->obj;
// should be no-ops:
if (nargs >= 1)
if (npassed_args >= 1)
srewrite_args.arg2 = rewrite_args->arg1;
if (nargs >= 2)
if (npassed_args >= 2)
srewrite_args.arg3 = rewrite_args->arg2;
srewrite_args.func_guarded = true;
srewrite_args.args_guarded = true;
r_clsattr.push();
rtn = runtimeCallInternal(clsattr, &srewrite_args, nargs + 1, obj, arg1, arg2, NULL);
rtn = runtimeCallInternal(
clsattr, &srewrite_args,
ArgPassSpec(argspec.num_args + 1, argspec.num_keywords, argspec.has_starargs, argspec.has_kwargs),
obj, arg1, arg2, NULL, keyword_names);
if (!srewrite_args.out_success) {
rewrite_args = NULL;
......@@ -1366,18 +1379,20 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
rewrite_args->out_rtn = srewrite_args.out_rtn;
}
} else {
rtn = runtimeCallInternal(clsattr, NULL, nargs + 1, obj, arg1, arg2, NULL);
rtn = runtimeCallInternal(clsattr, NULL, ArgPassSpec(argspec.num_args + 1, argspec.num_keywords,
argspec.has_starargs, argspec.has_kwargs),
obj, arg1, arg2, NULL, keyword_names);
}
if (rewrite_args)
rewrite_args->out_success = true;
return rtn;
} else {
int alloca_size = sizeof(Box*) * (nargs + 1 - 3);
int alloca_size = sizeof(Box*) * (npassed_args + 1 - 3);
Box** new_args = (Box**)alloca(alloca_size);
new_args[0] = arg3;
memcpy(new_args + 1, args, (nargs - 3) * sizeof(Box*));
memcpy(new_args + 1, args, (npassed_args - 3) * sizeof(Box*));
Box* rtn;
if (rewrite_args) {
......@@ -1392,10 +1407,10 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
// Want to move them to
// 1 2 X X
// if (nargs >= 1) rewrite_args->arg1 = rewrite_args->arg1.move(1);
// if (nargs >= 2) rewrite_args->arg2 = rewrite_args->arg2.move(2);
// if (nargs >= 3) rewrite_args->arg3 = rewrite_args->arg3.move(4);
// if (nargs >= 4) rewrite_args->args = rewrite_args->args.move(5);
// if (npassed_args >= 1) rewrite_args->arg1 = rewrite_args->arg1.move(1);
// if (npassed_args >= 2) rewrite_args->arg2 = rewrite_args->arg2.move(2);
// if (npassed_args >= 3) rewrite_args->arg3 = rewrite_args->arg3.move(4);
// if (npassed_args >= 4) rewrite_args->args = rewrite_args->args.move(5);
// There's nothing critical that these are in these registers,
// just that the register assignments for the rest of this
......@@ -1414,7 +1429,7 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
}
// arg3 is now dead
for (int i = 0; i < nargs - 3; i++) {
for (int i = 0; i < npassed_args - 3; i++) {
RewriterVar arg;
if (rewrite_args->args.isInReg())
arg = rewrite_args->args.getAttr(i * sizeof(Box*), -2);
......@@ -1428,18 +1443,21 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
CallRewriteArgs srewrite_args(rewrite_args->rewriter, r_clsattr);
srewrite_args.arg1 = rewrite_args->obj;
if (nargs >= 1)
if (npassed_args >= 1)
srewrite_args.arg2 = rewrite_args->arg1;
if (nargs >= 2)
if (npassed_args >= 2)
srewrite_args.arg3 = rewrite_args->arg2;
if (nargs >= 3)
if (npassed_args >= 3)
srewrite_args.args = r_new_args;
srewrite_args.args_guarded = true;
srewrite_args.func_guarded = true;
if (annotate)
rewrite_args->rewriter->annotate(0);
rtn = runtimeCallInternal(clsattr, &srewrite_args, nargs + 1, obj, arg1, arg2, new_args);
rtn = runtimeCallInternal(
clsattr, &srewrite_args,
ArgPassSpec(argspec.num_args + 1, argspec.num_keywords, argspec.has_starargs, argspec.has_kwargs),
obj, arg1, arg2, new_args, keyword_names);
if (annotate)
rewrite_args->rewriter->annotate(1);
......@@ -1456,7 +1474,9 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
if (annotate)
rewrite_args->rewriter->annotate(2);
} else {
rtn = runtimeCallInternal(clsattr, NULL, nargs + 1, obj, arg1, arg2, new_args);
rtn = runtimeCallInternal(clsattr, NULL, ArgPassSpec(argspec.num_args + 1, argspec.num_keywords,
argspec.has_starargs, argspec.has_kwargs),
obj, arg1, arg2, new_args, keyword_names);
}
return rtn;
}
......@@ -1467,24 +1487,24 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
if (rewrite_args) {
CallRewriteArgs srewrite_args(rewrite_args->rewriter, r_clsattr);
if (nargs >= 1)
if (npassed_args >= 1)
srewrite_args.arg1 = rewrite_args->arg1;
if (nargs >= 2)
if (npassed_args >= 2)
srewrite_args.arg2 = rewrite_args->arg2;
if (nargs >= 3)
if (npassed_args >= 3)
srewrite_args.arg3 = rewrite_args->arg3;
if (nargs >= 4)
if (npassed_args >= 4)
srewrite_args.args = rewrite_args->args;
srewrite_args.args_guarded = true;
rtn = runtimeCallInternal(clsattr, &srewrite_args, nargs, arg1, arg2, arg3, args);
rtn = runtimeCallInternal(clsattr, &srewrite_args, argspec, arg1, arg2, arg3, args, keyword_names);
if (!srewrite_args.out_success)
rewrite_args = NULL;
else
rewrite_args->out_rtn = srewrite_args.out_rtn;
} else {
rtn = runtimeCallInternal(clsattr, NULL, nargs, arg1, arg2, arg3, args);
rtn = runtimeCallInternal(clsattr, NULL, argspec, arg1, arg2, arg3, args, keyword_names);
}
if (!rtn) {
......@@ -1497,14 +1517,22 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope
}
}
extern "C" Box* callattr(Box* obj, std::string* attr, bool clsonly, int64_t nargs, Box* arg1, Box* arg2, Box* arg3,
Box** args) {
extern "C" Box* callattr(Box* obj, std::string* attr, bool clsonly, ArgPassSpec argspec, Box* arg1, Box* arg2,
Box* arg3, Box** args, const std::vector<const std::string*>* keyword_names) {
RELEASE_ASSERT(!argspec.has_starargs, "");
RELEASE_ASSERT(!argspec.has_kwargs, "");
RELEASE_ASSERT(argspec.num_keywords == 0, "");
int npassed_args = argspec.totalPassed();
static StatCounter slowpath_callattr("slowpath_callattr");
slowpath_callattr.log();
assert(attr);
int num_orig_args = 4 + std::min(4L, nargs);
int num_orig_args = 4 + std::min(4, npassed_args);
if (keyword_names)
num_orig_args++;
std::unique_ptr<Rewriter> rewriter(Rewriter::createRewriter(
__builtin_extract_return_addr(__builtin_return_address(0)), num_orig_args, 2, "callattr"));
Box* rtn;
......@@ -1516,18 +1544,18 @@ extern "C" Box* callattr(Box* obj, std::string* attr, bool clsonly, int64_t narg
// TODO feel weird about doing this; it either isn't necessary
// or this kind of thing is necessary in a lot more places
// rewriter->getArg(3).addGuard(nargs);
// rewriter->getArg(3).addGuard(npassed_args);
CallRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0));
if (nargs >= 1)
if (npassed_args >= 1)
rewrite_args.arg1 = rewriter->getArg(4);
if (nargs >= 2)
if (npassed_args >= 2)
rewrite_args.arg2 = rewriter->getArg(5);
if (nargs >= 3)
if (npassed_args >= 3)
rewrite_args.arg3 = rewriter->getArg(6);
if (nargs >= 4)
if (npassed_args >= 4)
rewrite_args.args = rewriter->getArg(7);
rtn = callattrInternal(obj, attr, scope, &rewrite_args, nargs, arg1, arg2, arg3, args);
rtn = callattrInternal(obj, attr, scope, &rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
......@@ -1535,7 +1563,7 @@ extern "C" Box* callattr(Box* obj, std::string* attr, bool clsonly, int64_t narg
rewrite_args.out_rtn.move(-1);
}
} else {
rtn = callattrInternal(obj, attr, scope, NULL, nargs, arg1, arg2, arg3, args);
rtn = callattrInternal(obj, attr, scope, NULL, argspec, arg1, arg2, arg3, args, keyword_names);
}
if (rtn == NULL) {
......@@ -1547,122 +1575,349 @@ extern "C" Box* callattr(Box* obj, std::string* attr, bool clsonly, int64_t narg
return rtn;
}
Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, int64_t nargs, Box* arg1, Box* arg2, Box* arg3,
Box** args) {
// the 10M upper bound isn't a hard max, just almost certainly a bug
// (also the alloca later will probably fail anyway)
ASSERT(nargs >= 0 && nargs < 10000000, "%ld", nargs);
CompiledFunction* resolveCLFunc(CLFunction* f, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args,
const std::vector<const std::string*>* keyword_names) {
RELEASE_ASSERT(!argspec.has_starargs, "");
RELEASE_ASSERT(!argspec.has_kwargs, "");
RELEASE_ASSERT(argspec.num_keywords == 0, "");
Box* orig_obj = obj;
int64_t nargs = argspec.totalPassed();
if (obj->cls != function_cls && obj->cls != instancemethod_cls) {
Box* rtn;
if (rewrite_args) {
// TODO is this ok?
// rewrite_args->rewriter->trap();
rtn = callattrInternal(obj, &_call_str, CLASS_ONLY, rewrite_args, nargs, arg1, arg2, arg3, args);
static StatCounter slowpath_resolveclfunc("slowpath_resolveclfunc");
slowpath_resolveclfunc.log();
FunctionList& versions = f->versions;
for (int i = 0; i < versions.size(); i++) {
CompiledFunction* cf = versions[i];
FunctionSignature* sig = cf->sig;
assert(!sig->takes_kwargs && "not handled yet");
if (sig->rtn_type->llvmType() != UNKNOWN->llvmType())
continue;
if ((!sig->takes_varargs && sig->arg_types.size() != nargs)
|| (sig->takes_varargs && nargs < sig->arg_types.size()))
continue;
int nsig_args = sig->arg_types.size();
if (nsig_args >= 1) {
if (sig->arg_types[0]->isFitBy(arg1->cls)) {
// pass
} else {
rtn = callattrInternal(obj, &_call_str, CLASS_ONLY, NULL, nargs, arg1, arg2, arg3, args);
continue;
}
}
if (nsig_args >= 2) {
if (sig->arg_types[1]->isFitBy(arg2->cls)) {
// pass
} else {
continue;
}
}
if (nsig_args >= 3) {
if (sig->arg_types[2]->isFitBy(arg3->cls)) {
// pass
} else {
continue;
}
if (!rtn)
raiseExcHelper(TypeError, "'%s' object is not callable", getTypeName(obj)->c_str());
return rtn;
}
bool bad = false;
for (int j = 3; j < nsig_args; j++) {
if (sig->arg_types[j]->isFitBy(args[j - 3]->cls)) {
// pass
} else {
bad = true;
break;
}
}
if (bad)
continue;
if (rewrite_args) {
if (!rewrite_args->args_guarded) {
// TODO should know which args don't need to be guarded, ex if we're guaranteed that they
// already fit, either since the type inferencer could determine that,
// or because they only need to fit into an UNKNOWN slot.
assert(cf);
assert(!cf->entry_descriptor);
assert(cf->is_interpreted == (cf->code == NULL));
if (nargs >= 1)
rewrite_args->arg1.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)arg1->cls);
if (nargs >= 2)
rewrite_args->arg2.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)arg2->cls);
if (nargs >= 3)
rewrite_args->arg3.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)arg3->cls);
for (int i = 3; i < nargs; i++) {
rewrite_args->args.getAttr((i - 3) * sizeof(Box*), -1)
.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)args[i - 3]->cls);
return cf;
}
if (f->source == NULL) {
printf("Error: couldn't find suitable function version and no source to recompile!\n");
printf("%ld args:", nargs);
for (int i = 0; i < nargs; i++) {
if (i == 3) {
printf(" [and more]");
break;
}
rewrite_args->rewriter->addDecision(obj->cls == function_cls ? 1 : 0);
Box* firstargs[] = { arg1, arg2, arg3 };
printf(" %s", getTypeName(firstargs[i])->c_str());
}
printf("\n");
for (int j = 0; j < f->versions.size(); j++) {
std::string func_name = g.func_addr_registry.getFuncNameAtAddress(f->versions[j]->code, true);
printf("Version %d, %s:", j, func_name.c_str());
FunctionSignature* sig = f->versions[j]->sig;
for (int i = 0; i < sig->arg_types.size(); i++) {
printf(" %s", sig->arg_types[i]->debugName().c_str());
}
if (sig->takes_varargs)
printf(" *vararg");
if (sig->takes_kwargs)
printf(" *kwarg");
printf("\n");
// printf(" @%p %s\n", f->versions[j]->code, func_name.c_str());
}
abort();
}
if (obj->cls == function_cls) {
BoxedFunction* f = static_cast<BoxedFunction*>(obj);
assert(f->source->getArgsAST()->vararg.size() == 0);
assert(f->source->getArgsAST()->kwarg.size() == 0);
assert(f->source->getArgsAST()->defaults.size() == 0);
bool takes_varargs = false;
bool takes_kwargs = false;
int ndefaults = 0;
std::vector<ConcreteCompilerType*> arg_types;
if (nargs >= 1) {
arg_types.push_back(typeFromClass(arg1->cls));
}
if (nargs >= 2) {
arg_types.push_back(typeFromClass(arg2->cls));
}
if (nargs >= 3) {
arg_types.push_back(typeFromClass(arg3->cls));
}
for (int j = 3; j < nargs; j++) {
arg_types.push_back(typeFromClass(args[j - 3]->cls));
}
FunctionSignature* sig
= new FunctionSignature(UNKNOWN, &f->source->getArgNames(), arg_types, ndefaults, takes_varargs, takes_kwargs);
CompiledFunction* cf = resolveCLFunc(f->f, nargs, arg1, arg2, arg3, args);
EffortLevel::EffortLevel new_effort = initialEffort();
// typeCall (ie the base for constructors) is important enough that it knows
// how to do rewrites, so lets cut directly to the internal function rather
// than hitting its python bindings:
if (cf->code == typeCall) {
Box* rtn;
if (rewrite_args) {
CallRewriteArgs srewrite_args(rewrite_args->rewriter, RewriterVar());
if (nargs >= 1)
srewrite_args.arg1 = rewrite_args->arg1;
if (nargs >= 2)
srewrite_args.arg2 = rewrite_args->arg2;
if (nargs >= 3)
srewrite_args.arg3 = rewrite_args->arg3;
if (nargs >= 4)
srewrite_args.args = rewrite_args->args;
rtn = typeCallInternal(&srewrite_args, nargs, arg1, arg2, arg3, args);
if (!srewrite_args.out_success)
rewrite_args = NULL;
CompiledFunction* cf = compileFunction(f, sig, new_effort,
NULL); // this pushes the new CompiledVersion to the back of the version list
assert(cf->is_interpreted == (cf->code == NULL));
return cf;
}
Box* callCompiledFunc(CompiledFunction* cf, ArgPassSpec argspec, Box* iarg1, Box* iarg2, Box* iarg3, Box** iargs,
const std::vector<const std::string*>* keyword_names) {
RELEASE_ASSERT(!argspec.has_starargs, "");
RELEASE_ASSERT(!argspec.has_kwargs, "");
int64_t npassed_args = argspec.totalPassed();
assert(cf);
// TODO these shouldn't have to be initialized, but I don't want to issue a #pragma
// that disables the warning for the whole file:
Box* oarg1 = NULL, *oarg2 = NULL, *oarg3 = NULL;
Box** oargs = NULL;
int num_out_args = npassed_args;
if (cf->sig->takes_varargs)
num_out_args++;
assert(!cf->sig->takes_kwargs);
if (cf->sig->takes_kwargs)
num_out_args++;
assert(!cf->sig->ndefaults);
num_out_args += cf->sig->ndefaults;
if (num_out_args > 3)
oargs = (Box**)alloca((num_out_args - 3) * sizeof(Box*));
int nsig_args = cf->sig->arg_types.size();
std::vector<bool> keywords_used(false, argspec.num_keywords);
// First, fill formal parameters from positional arguments:
for (int i = 0; i < std::min((int)argspec.num_args, nsig_args); i++) {
if (i == 0)
oarg1 = iarg1;
else if (i == 1)
oarg2 = iarg2;
else if (i == 2)
oarg3 = iarg3;
else
rewrite_args->out_rtn = srewrite_args.out_rtn;
oargs[i - 3] = iargs[i - 3];
}
// Then, fill any remaining formal parameters from keywords:
if (argspec.num_args < nsig_args) {
const std::vector<AST_expr*>* arg_names = cf->sig->arg_names;
assert(arg_names);
assert(arg_names->size() == nsig_args);
for (int j = argspec.num_args; j < nsig_args; j++) {
assert((*arg_names)[j]->type == AST_TYPE::Name); // we should already have picked a good signature
const std::string& name = ast_cast<AST_Name>((*arg_names)[j])->id;
bool found = false;
for (int i = 0; i < keyword_names->size(); i++) {
if (keywords_used[i])
continue;
if (*(*keyword_names)[i] == name) {
found = true;
keywords_used[i] = true;
int from_arg = argspec.num_args + i;
Box* arg;
if (from_arg == 0)
arg = iarg1;
else if (from_arg == 1)
arg = iarg2;
else if (from_arg == 2)
arg = iarg3;
else
arg = iargs[from_arg - 3];
if (j == 0)
oarg1 = arg;
else if (j == 1)
oarg2 = arg;
else if (j == 2)
oarg3 = arg;
else
oargs[j - 3] = arg;
break;
}
}
assert(found); // should have been disallowed
}
}
assert(!cf->sig->takes_kwargs);
for (bool b : keywords_used)
assert(b);
if (cf->sig->takes_varargs) {
BoxedList* made_vararg = (BoxedList*)createList();
if (nsig_args == 0)
oarg1 = made_vararg;
else if (nsig_args == 1)
oarg2 = made_vararg;
else if (nsig_args == 2)
oarg3 = made_vararg;
else
oargs[nsig_args - 3] = made_vararg;
assert(argspec.num_args == npassed_args);
for (int i = nsig_args; i < npassed_args; i++) {
if (i == 0)
listAppendInternal(made_vararg, iarg1);
else if (i == 1)
listAppendInternal(made_vararg, iarg2);
else if (i == 2)
listAppendInternal(made_vararg, iarg3);
else
listAppendInternal(made_vararg, iargs[i - 3]);
}
} else {
rtn = typeCallInternal(NULL, nargs, arg1, arg2, arg3, args);
assert(!cf->sig->takes_kwargs);
assert(nsig_args == npassed_args);
}
if (rewrite_args)
rewrite_args->out_success = true;
return rtn;
if (!cf->is_interpreted) {
return cf->call(oarg1, oarg2, oarg3, oargs);
} else {
return interpretFunction(cf->func, num_out_args, oarg1, oarg2, oarg3, oargs);
}
}
Box* callFunc(BoxedFunction* func, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3,
Box** args, const std::vector<const std::string*>* keyword_names) {
int npassed_args = argspec.totalPassed();
if (cf->sig->is_vararg) {
CompiledFunction* cf = resolveCLFunc(func->f, argspec, arg1, arg2, arg3, args, keyword_names);
if (cf->sig->takes_varargs || cf->sig->takes_kwargs || argspec.num_keywords || argspec.has_starargs
|| argspec.has_kwargs) {
if (rewrite_args && VERBOSITY())
printf("Not patchpointing this varargs function\n");
printf("Not patchpointing this non-simple function call\n");
rewrite_args = NULL;
}
if (cf->is_interpreted)
rewrite_args = NULL;
if (rewrite_args) {
// TODO should it be the func object or its CLFunction?
if (!rewrite_args->func_guarded)
rewrite_args->obj.addGuard((intptr_t)obj);
rewrite_args->obj.addGuard((intptr_t)func);
rewrite_args->rewriter->addDependenceOn(cf->dependent_callsites);
// if (VERBOSITY()) {
// printf("runtimeCallInternal: %d", rewrite_args->obj.getArgnum());
// if (nargs >= 1) printf(" %d", rewrite_args->arg1.getArgnum());
// if (nargs >= 2) printf(" %d", rewrite_args->arg2.getArgnum());
// if (nargs >= 3) printf(" %d", rewrite_args->arg3.getArgnum());
// if (nargs >= 4) printf(" %d", rewrite_args->args.getArgnum());
// printf("\n");
//}
if (nargs >= 1)
if (npassed_args >= 1)
rewrite_args->arg1.move(0);
if (nargs >= 2)
if (npassed_args >= 2)
rewrite_args->arg2.move(1);
if (nargs >= 3)
if (npassed_args >= 3)
rewrite_args->arg3.move(2);
if (nargs >= 4)
if (npassed_args >= 4)
rewrite_args->args.move(3);
RewriterVar r_rtn = rewrite_args->rewriter->call(cf->code);
rewrite_args->out_rtn = r_rtn.move(-1);
}
Box* rtn = callCompiledFunc(cf, nargs, arg1, arg2, arg3, args);
Box* rtn = callCompiledFunc(cf, argspec, arg1, arg2, arg3, args, keyword_names);
if (rewrite_args)
rewrite_args->out_success = true;
return rtn;
}
Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3,
Box** args, const std::vector<const std::string*>* keyword_names) {
int npassed_args = argspec.totalPassed();
Box* orig_obj = obj;
if (obj->cls != function_cls && obj->cls != instancemethod_cls) {
Box* rtn;
if (rewrite_args) {
// TODO is this ok?
// rewrite_args->rewriter->trap();
rtn = callattrInternal(obj, &_call_str, CLASS_ONLY, rewrite_args, argspec, arg1, arg2, arg3, args,
keyword_names);
} else {
rtn = callattrInternal(obj, &_call_str, CLASS_ONLY, NULL, argspec, arg1, arg2, arg3, args, keyword_names);
}
if (!rtn)
raiseExcHelper(TypeError, "'%s' object is not callable", getTypeName(obj)->c_str());
return rtn;
}
if (rewrite_args) {
if (!rewrite_args->args_guarded) {
// TODO should know which args don't need to be guarded, ex if we're guaranteed that they
// already fit, either since the type inferencer could determine that,
// or because they only need to fit into an UNKNOWN slot.
if (npassed_args >= 1)
rewrite_args->arg1.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)arg1->cls);
if (npassed_args >= 2)
rewrite_args->arg2.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)arg2->cls);
if (npassed_args >= 3)
rewrite_args->arg3.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)arg3->cls);
for (int i = 3; i < npassed_args; i++) {
rewrite_args->args.getAttr((i - 3) * sizeof(Box*), -1)
.addAttrGuard(BOX_CLS_OFFSET, (intptr_t)args[i - 3]->cls);
}
}
rewrite_args->rewriter->addDecision(obj->cls == function_cls ? 1 : 0);
}
if (obj->cls == function_cls) {
BoxedFunction* f = static_cast<BoxedFunction*>(obj);
// Some functions are sufficiently important that we want them to be able to patchpoint themselves;
// they can do this by setting the "internal_callable" field:
CLFunction::InternalCallable callable = f->f->internal_callable;
if (callable == NULL)
callable = callFunc;
return callable(f, rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names);
} else if (obj->cls == instancemethod_cls) {
// TODO it's dumb but I should implement patchpoints here as well
// duplicated with callattr
......@@ -1672,7 +1927,7 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, int64_t nargs,
rewrite_args->obj.addAttrGuard(INSTANCEMETHOD_FUNC_OFFSET, (intptr_t)im->func);
}
if (nargs <= 2) {
if (npassed_args <= 2) {
Box* rtn;
if (rewrite_args) {
// Kind of weird that we don't need to give this a valid RewriterVar, but it shouldn't need to access it
......@@ -1682,12 +1937,15 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, int64_t nargs,
srewrite_args.arg1 = rewrite_args->obj.getAttr(INSTANCEMETHOD_OBJ_OFFSET, 0);
srewrite_args.func_guarded = true;
srewrite_args.args_guarded = true;
if (nargs >= 1)
if (npassed_args >= 1)
srewrite_args.arg2 = rewrite_args->arg1;
if (nargs >= 2)
if (npassed_args >= 2)
srewrite_args.arg3 = rewrite_args->arg2;
rtn = runtimeCallInternal(im->func, &srewrite_args, nargs + 1, im->obj, arg1, arg2, NULL);
rtn = runtimeCallInternal(
im->func, &srewrite_args,
ArgPassSpec(argspec.num_args + 1, argspec.num_keywords, argspec.has_starargs, argspec.has_kwargs),
im->obj, arg1, arg2, NULL, keyword_names);
if (!srewrite_args.out_success) {
rewrite_args = NULL;
......@@ -1695,16 +1953,20 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, int64_t nargs,
rewrite_args->out_rtn = srewrite_args.out_rtn.move(-1);
}
} else {
rtn = runtimeCallInternal(im->func, NULL, nargs + 1, im->obj, arg1, arg2, NULL);
rtn = runtimeCallInternal(im->func, NULL, ArgPassSpec(argspec.num_args + 1, argspec.num_keywords,
argspec.has_starargs, argspec.has_kwargs),
im->obj, arg1, arg2, NULL, keyword_names);
}
if (rewrite_args)
rewrite_args->out_success = true;
return rtn;
} else {
Box** new_args = (Box**)alloca(sizeof(Box*) * (nargs + 1 - 3));
Box** new_args = (Box**)alloca(sizeof(Box*) * (npassed_args + 1 - 3));
new_args[0] = arg3;
memcpy(new_args + 1, args, (nargs - 3) * sizeof(Box*));
Box* rtn = runtimeCall(im->func, nargs + 1, im->obj, arg1, arg2, new_args);
memcpy(new_args + 1, args, (npassed_args - 3) * sizeof(Box*));
Box* rtn = runtimeCall(im->func, ArgPassSpec(argspec.num_args + 1, argspec.num_keywords,
argspec.has_starargs, argspec.has_kwargs),
im->obj, arg1, arg2, new_args, keyword_names);
return rtn;
}
}
......@@ -1712,11 +1974,16 @@ Box* runtimeCallInternal(Box* obj, CallRewriteArgs* rewrite_args, int64_t nargs,
abort();
}
extern "C" Box* runtimeCall(Box* obj, int64_t nargs, Box* arg1, Box* arg2, Box* arg3, Box** args) {
extern "C" Box* runtimeCall(Box* obj, ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args,
const std::vector<const std::string*>* keyword_names) {
int npassed_args = argspec.totalPassed();
static StatCounter slowpath_runtimecall("slowpath_runtimecall");
slowpath_runtimecall.log();
int num_orig_args = 2 + std::min(4L, nargs);
int num_orig_args = 2 + std::min(4, npassed_args);
if (argspec.num_keywords > 0)
num_orig_args++;
std::unique_ptr<Rewriter> rewriter(Rewriter::createRewriter(
__builtin_extract_return_addr(__builtin_return_address(0)), num_orig_args, 2, "runtimeCall"));
Box* rtn;
......@@ -1726,18 +1993,18 @@ extern "C" Box* runtimeCall(Box* obj, int64_t nargs, Box* arg1, Box* arg2, Box*
// TODO feel weird about doing this; it either isn't necessary
// or this kind of thing is necessary in a lot more places
// rewriter->getArg(1).addGuard(nargs);
// rewriter->getArg(1).addGuard(npassed_args);
CallRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0));
if (nargs >= 1)
if (npassed_args >= 1)
rewrite_args.arg1 = rewriter->getArg(2);
if (nargs >= 2)
if (npassed_args >= 2)
rewrite_args.arg2 = rewriter->getArg(3);
if (nargs >= 3)
if (npassed_args >= 3)
rewrite_args.arg3 = rewriter->getArg(4);
if (nargs >= 4)
if (npassed_args >= 4)
rewrite_args.args = rewriter->getArg(5);
rtn = runtimeCallInternal(obj, &rewrite_args, nargs, arg1, arg2, arg3, args);
rtn = runtimeCallInternal(obj, &rewrite_args, argspec, arg1, arg2, arg3, args, keyword_names);
if (!rewrite_args.out_success) {
rewriter.reset(NULL);
......@@ -1745,7 +2012,7 @@ extern "C" Box* runtimeCall(Box* obj, int64_t nargs, Box* arg1, Box* arg2, Box*
rewrite_args.out_rtn.move(-1);
}
} else {
rtn = runtimeCallInternal(obj, NULL, nargs, arg1, arg2, arg3, args);
rtn = runtimeCallInternal(obj, NULL, argspec, arg1, arg2, arg3, args, keyword_names);
}
assert(rtn);
......@@ -1780,14 +2047,14 @@ extern "C" Box* binopInternal(Box* lhs, Box* rhs, int op_type, bool inplace, Bin
if (rewrite_args) {
CallRewriteArgs srewrite_args(rewrite_args->rewriter, rewrite_args->lhs);
srewrite_args.arg1 = rewrite_args->rhs;
irtn = callattrInternal1(lhs, &iop_name, CLASS_ONLY, &srewrite_args, 1, rhs);
irtn = callattrInternal1(lhs, &iop_name, CLASS_ONLY, &srewrite_args, ArgPassSpec(1), rhs);
if (!srewrite_args.out_success)
rewrite_args = NULL;
else if (irtn)
rewrite_args->out_rtn = srewrite_args.out_rtn.move(-1);
} else {
irtn = callattrInternal1(lhs, &iop_name, CLASS_ONLY, NULL, 1, rhs);
irtn = callattrInternal1(lhs, &iop_name, CLASS_ONLY, NULL, ArgPassSpec(1), rhs);
}
if (irtn) {
......@@ -1807,14 +2074,14 @@ extern "C" Box* binopInternal(Box* lhs, Box* rhs, int op_type, bool inplace, Bin
if (rewrite_args) {
CallRewriteArgs srewrite_args(rewrite_args->rewriter, rewrite_args->lhs);
srewrite_args.arg1 = rewrite_args->rhs;
lrtn = callattrInternal1(lhs, &op_name, CLASS_ONLY, &srewrite_args, 1, rhs);
lrtn = callattrInternal1(lhs, &op_name, CLASS_ONLY, &srewrite_args, ArgPassSpec(1), rhs);
if (!srewrite_args.out_success)
rewrite_args = NULL;
else if (lrtn)
rewrite_args->out_rtn = srewrite_args.out_rtn.move(-1);
} else {
lrtn = callattrInternal1(lhs, &op_name, CLASS_ONLY, NULL, 1, rhs);
lrtn = callattrInternal1(lhs, &op_name, CLASS_ONLY, NULL, ArgPassSpec(1), rhs);
}
......@@ -1834,7 +2101,7 @@ extern "C" Box* binopInternal(Box* lhs, Box* rhs, int op_type, bool inplace, Bin
}
std::string rop_name = getReverseOpName(op_type);
Box* rrtn = callattrInternal1(rhs, &rop_name, CLASS_ONLY, NULL, 1, lhs);
Box* rrtn = callattrInternal1(rhs, &rop_name, CLASS_ONLY, NULL, ArgPassSpec(1), lhs);
if (rrtn != NULL && rrtn != NotImplemented)
return rrtn;
......@@ -1966,10 +2233,10 @@ Box* compareInternal(Box* lhs, Box* rhs, int op_type, CompareRewriteArgs* rewrit
// TODO do rewrite
static const std::string str_contains("__contains__");
Box* contained = callattrInternal1(rhs, &str_contains, CLASS_ONLY, NULL, 1, lhs);
Box* contained = callattrInternal1(rhs, &str_contains, CLASS_ONLY, NULL, ArgPassSpec(1), lhs);
if (contained == NULL) {
static const std::string str_iter("__iter__");
Box* iter = callattrInternal0(rhs, &str_iter, CLASS_ONLY, NULL, 0);
Box* iter = callattrInternal0(rhs, &str_iter, CLASS_ONLY, NULL, ArgPassSpec(0));
if (iter)
ASSERT(isUserDefined(rhs->cls), "%s should probably have a __contains__", getTypeName(rhs)->c_str());
RELEASE_ASSERT(iter == NULL, "need to try iterating");
......@@ -2006,14 +2273,14 @@ Box* compareInternal(Box* lhs, Box* rhs, int op_type, CompareRewriteArgs* rewrit
if (rewrite_args) {
CallRewriteArgs crewrite_args(rewrite_args->rewriter, rewrite_args->lhs);
crewrite_args.arg1 = rewrite_args->rhs;
lrtn = callattrInternal1(lhs, &op_name, CLASS_ONLY, &crewrite_args, 1, rhs);
lrtn = callattrInternal1(lhs, &op_name, CLASS_ONLY, &crewrite_args, ArgPassSpec(1), rhs);
if (!crewrite_args.out_success)
rewrite_args = NULL;
else if (lrtn)
rewrite_args->out_rtn = crewrite_args.out_rtn;
} else {
lrtn = callattrInternal1(lhs, &op_name, CLASS_ONLY, NULL, 1, rhs);
lrtn = callattrInternal1(lhs, &op_name, CLASS_ONLY, NULL, ArgPassSpec(1), rhs);
}
if (lrtn) {
......@@ -2035,7 +2302,7 @@ Box* compareInternal(Box* lhs, Box* rhs, int op_type, CompareRewriteArgs* rewrit
}
std::string rop_name = getReverseOpName(op_type);
Box* rrtn = callattrInternal1(rhs, &rop_name, CLASS_ONLY, NULL, 1, lhs);
Box* rrtn = callattrInternal1(rhs, &rop_name, CLASS_ONLY, NULL, ArgPassSpec(1), lhs);
if (rrtn != NULL && rrtn != NotImplemented)
return rrtn;
......@@ -2117,7 +2384,7 @@ extern "C" Box* unaryop(Box* operand, int op_type) {
ASSERT(attr_func, "%s.%s", getTypeName(operand)->c_str(), op_name.c_str());
Box* rtn = runtimeCall0(attr_func, 0);
Box* rtn = runtimeCall0(attr_func, ArgPassSpec(0));
return rtn;
}
......@@ -2138,14 +2405,14 @@ extern "C" Box* getitem(Box* value, Box* slice) {
CallRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0));
rewrite_args.arg1 = rewriter->getArg(1);
rtn = callattrInternal1(value, &str_getitem, CLASS_ONLY, &rewrite_args, 1, slice);
rtn = callattrInternal1(value, &str_getitem, CLASS_ONLY, &rewrite_args, ArgPassSpec(1), slice);
if (!rewrite_args.out_success)
rewriter.reset(NULL);
else if (rtn)
rewrite_args.out_rtn.move(-1);
} else {
rtn = callattrInternal1(value, &str_getitem, CLASS_ONLY, NULL, 1, slice);
rtn = callattrInternal1(value, &str_getitem, CLASS_ONLY, NULL, ArgPassSpec(1), slice);
}
if (rtn == NULL) {
......@@ -2181,14 +2448,14 @@ extern "C" void setitem(Box* target, Box* slice, Box* value) {
rewrite_args.arg1 = rewriter->getArg(1);
rewrite_args.arg2 = rewriter->getArg(2);
rtn = callattrInternal2(target, &str_setitem, CLASS_ONLY, &rewrite_args, 2, slice, value);
rtn = callattrInternal2(target, &str_setitem, CLASS_ONLY, &rewrite_args, ArgPassSpec(2), slice, value);
if (!rewrite_args.out_success)
rewriter.reset(NULL);
else if (rtn)
r_rtn = rewrite_args.out_rtn;
} else {
rtn = callattrInternal2(target, &str_setitem, CLASS_ONLY, NULL, 2, slice, value);
rtn = callattrInternal2(target, &str_setitem, CLASS_ONLY, NULL, ArgPassSpec(2), slice, value);
}
if (rtn == NULL) {
......@@ -2215,14 +2482,14 @@ extern "C" void delitem(Box* target, Box* slice) {
CallRewriteArgs rewrite_args(rewriter.get(), rewriter->getArg(0));
rewrite_args.arg1 = rewriter->getArg(1);
rtn = callattrInternal1(target, &str_delitem, CLASS_ONLY, &rewrite_args, 1, slice);
rtn = callattrInternal1(target, &str_delitem, CLASS_ONLY, &rewrite_args, ArgPassSpec(1), slice);
if (!rewrite_args.out_success)
rewriter.reset(NULL);
else if (rtn)
r_rtn = rewrite_args.out_rtn;
} else {
rtn = callattrInternal1(target, &str_delitem, CLASS_ONLY, NULL, 1, slice);
rtn = callattrInternal1(target, &str_delitem, CLASS_ONLY, NULL, ArgPassSpec(1), slice);
}
if (rtn == NULL) {
......@@ -2241,16 +2508,23 @@ static void assertInitNone(Box* obj) {
}
}
Box* typeCallInternal(CallRewriteArgs* rewrite_args, int64_t nargs, Box* arg1, Box* arg2, Box* arg3, Box** args) {
Box* typeCallInternal(BoxedFunction* f, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2,
Box* arg3, Box** args, const std::vector<const std::string*>* keyword_names) {
RELEASE_ASSERT(!argspec.has_starargs, "");
RELEASE_ASSERT(!argspec.has_kwargs, "");
RELEASE_ASSERT(argspec.num_keywords == 0, "");
int npassed_args = argspec.totalPassed();
static StatCounter slowpath_typecall("slowpath_typecall");
slowpath_typecall.log();
// if (rewrite_args && VERBOSITY()) {
// printf("typeCallInternal: %d", rewrite_args->obj.getArgnum());
// if (nargs >= 1) printf(" %d", rewrite_args->arg1.getArgnum());
// if (nargs >= 2) printf(" %d", rewrite_args->arg2.getArgnum());
// if (nargs >= 3) printf(" %d", rewrite_args->arg3.getArgnum());
// if (nargs >= 4) printf(" %d", rewrite_args->args.getArgnum());
// if (npassed_args >= 1) printf(" %d", rewrite_args->arg1.getArgnum());
// if (npassed_args >= 2) printf(" %d", rewrite_args->arg2.getArgnum());
// if (npassed_args >= 3) printf(" %d", rewrite_args->arg3.getArgnum());
// if (npassed_args >= 4) printf(" %d", rewrite_args->args.getArgnum());
// printf("\n");
//}
......@@ -2315,39 +2589,41 @@ Box* typeCallInternal(CallRewriteArgs* rewrite_args, int64_t nargs, Box* arg1, B
if (rewrite_args) {
if (init_attr)
r_init.push();
if (nargs >= 1)
if (npassed_args >= 1)
r_ccls.push();
if (nargs >= 2)
if (npassed_args >= 2)
rewrite_args->arg2.push();
if (nargs >= 3)
if (npassed_args >= 3)
rewrite_args->arg3.push();
if (nargs >= 4)
if (npassed_args >= 4)
rewrite_args->args.push();
CallRewriteArgs srewrite_args(rewrite_args->rewriter, r_new);
if (nargs >= 1)
if (npassed_args >= 1)
srewrite_args.arg1 = r_ccls;
if (nargs >= 2)
if (npassed_args >= 2)
srewrite_args.arg2 = rewrite_args->arg2;
if (nargs >= 3)
if (npassed_args >= 3)
srewrite_args.arg3 = rewrite_args->arg3;
if (nargs >= 4)
if (npassed_args >= 4)
srewrite_args.args = rewrite_args->args;
srewrite_args.args_guarded = true;
srewrite_args.func_guarded = true;
r_new.push();
int new_args = nargs;
if (nargs > 1 && new_attr == typeLookup(object_cls, _new_str, NULL, NULL)) {
auto new_argspec = argspec;
auto new_keyword_names = keyword_names;
if (npassed_args > 1 && new_attr == typeLookup(object_cls, _new_str, NULL, NULL)) {
if (init_attr == typeLookup(object_cls, _init_str, NULL, NULL)) {
raiseExcHelper(TypeError, "object.__new__() takes no parameters");
} else {
new_args = 1;
new_argspec = ArgPassSpec(1);
new_keyword_names = NULL;
}
}
made = runtimeCallInternal(new_attr, &srewrite_args, new_args, cls, arg2, arg3, args);
made = runtimeCallInternal(new_attr, &srewrite_args, new_argspec, cls, arg2, arg3, args, new_keyword_names);
if (!srewrite_args.out_success)
rewrite_args = NULL;
......@@ -2357,19 +2633,19 @@ Box* typeCallInternal(CallRewriteArgs* rewrite_args, int64_t nargs, Box* arg1, B
r_new = rewrite_args->rewriter->pop(0);
r_made = r_made.move(-1);
if (nargs >= 4)
if (npassed_args >= 4)
rewrite_args->args = rewrite_args->rewriter->pop(3);
if (nargs >= 3)
if (npassed_args >= 3)
rewrite_args->arg3 = rewrite_args->rewriter->pop(2);
if (nargs >= 2)
if (npassed_args >= 2)
rewrite_args->arg2 = rewrite_args->rewriter->pop(1);
if (nargs >= 1)
if (npassed_args >= 1)
r_ccls = rewrite_args->arg1 = rewrite_args->rewriter->pop(0);
if (init_attr)
r_init = rewrite_args->rewriter->pop(-2);
}
} else {
made = runtimeCallInternal(new_attr, NULL, nargs, cls, arg2, arg3, args);
made = runtimeCallInternal(new_attr, NULL, argspec, cls, arg2, arg3, args, keyword_names);
}
assert(made);
......@@ -2380,21 +2656,22 @@ Box* typeCallInternal(CallRewriteArgs* rewrite_args, int64_t nargs, Box* arg1, B
Box* initrtn;
if (rewrite_args) {
CallRewriteArgs srewrite_args(rewrite_args->rewriter, r_init);
if (nargs >= 1)
if (npassed_args >= 1)
srewrite_args.arg1 = r_made;
if (nargs >= 2)
if (npassed_args >= 2)
srewrite_args.arg2 = rewrite_args->arg2;
if (nargs >= 3)
if (npassed_args >= 3)
srewrite_args.arg3 = rewrite_args->arg3;
if (nargs >= 4)
if (npassed_args >= 4)
srewrite_args.args = rewrite_args->args;
srewrite_args.args_guarded = true;
srewrite_args.func_guarded = true;
r_made.push();
r_init.push();
// initrtn = callattrInternal(ccls, &_init_str, INST_ONLY, &srewrite_args, nargs, made, arg2, arg3, args);
initrtn = runtimeCallInternal(init_attr, &srewrite_args, nargs, made, arg2, arg3, args);
// initrtn = callattrInternal(ccls, &_init_str, INST_ONLY, &srewrite_args, argspec, made, arg2, arg3, args,
// keyword_names);
initrtn = runtimeCallInternal(init_attr, &srewrite_args, argspec, made, arg2, arg3, args, keyword_names);
if (!srewrite_args.out_success)
rewrite_args = NULL;
......@@ -2406,14 +2683,16 @@ Box* typeCallInternal(CallRewriteArgs* rewrite_args, int64_t nargs, Box* arg1, B
r_made = rewrite_args->rewriter->pop(-1);
}
} else {
// initrtn = callattrInternal(ccls, &_init_str, INST_ONLY, NULL, nargs, made, arg2, arg3, args);
initrtn = runtimeCallInternal(init_attr, NULL, nargs, made, arg2, arg3, args);
// initrtn = callattrInternal(ccls, &_init_str, INST_ONLY, NULL, argspec, made, arg2, arg3, args,
// keyword_names);
initrtn = runtimeCallInternal(init_attr, NULL, argspec, made, arg2, arg3, args, keyword_names);
}
assertInitNone(initrtn);
} else {
// TODO this shouldn't be reached
// assert(0 && "I don't think this should be reached");
if (new_attr == NULL && nargs != 1) {
if (new_attr == NULL && npassed_args != 1) {
// TODO not npassed args, since the starargs or kwargs could be null
raiseExcHelper(TypeError, "object.__new__() takes no parameters");
}
}
......@@ -2428,14 +2707,14 @@ Box* typeCallInternal(CallRewriteArgs* rewrite_args, int64_t nargs, Box* arg1, B
Box* typeCall(Box* obj, BoxedList* vararg) {
assert(vararg->cls == list_cls);
if (vararg->size == 0)
return typeCallInternal1(NULL, 1, obj);
return typeCallInternal1(NULL, NULL, ArgPassSpec(1), obj);
else if (vararg->size == 1)
return typeCallInternal2(NULL, 2, obj, vararg->elts->elts[0]);
return typeCallInternal2(NULL, NULL, ArgPassSpec(2), obj, vararg->elts->elts[0]);
else if (vararg->size == 2)
return typeCallInternal3(NULL, 3, obj, vararg->elts->elts[0], vararg->elts->elts[1]);
return typeCallInternal3(NULL, NULL, ArgPassSpec(3), obj, vararg->elts->elts[0], vararg->elts->elts[1]);
else
return typeCallInternal(NULL, 1 + vararg->size, obj, vararg->elts->elts[0], vararg->elts->elts[1],
&vararg->elts->elts[2]);
return typeCallInternal(NULL, NULL, ArgPassSpec(1 + vararg->size), obj, vararg->elts->elts[0],
vararg->elts->elts[1], &vararg->elts->elts[2], NULL);
}
Box* typeNew(Box* cls, Box* obj) {
......
......@@ -39,8 +39,9 @@ extern "C" void my_assert(bool b);
extern "C" Box* getattr(Box* obj, const char* attr);
extern "C" void setattr(Box* obj, const char* attr, Box* attr_val);
extern "C" bool nonzero(Box* obj);
extern "C" Box* runtimeCall(Box*, int64_t, Box*, Box*, Box*, Box**);
extern "C" Box* callattr(Box*, std::string*, bool, int64_t, Box*, Box*, Box*, Box**);
extern "C" Box* runtimeCall(Box*, ArgPassSpec, Box*, Box*, Box*, Box**, const std::vector<const std::string*>*);
extern "C" Box* callattr(Box*, std::string*, bool, ArgPassSpec, Box*, Box*, Box*, Box**,
const std::vector<const std::string*>*);
extern "C" BoxedString* str(Box* obj);
extern "C" Box* repr(Box* obj);
extern "C" BoxedString* reprOrNull(Box* obj); // similar to repr, but returns NULL on exception
......@@ -73,6 +74,9 @@ extern "C" bool isSubclass(BoxedClass* child, BoxedClass* parent);
class BinopRewriteArgs;
extern "C" Box* binopInternal(Box* lhs, Box* rhs, int op_type, bool inplace, BinopRewriteArgs* rewrite_args);
Box* typeCallInternal(BoxedFunction* f, CallRewriteArgs* rewrite_args, ArgPassSpec argspec, Box* arg1, Box* arg2,
Box* arg3, Box** args, const std::vector<const std::string*>* keyword_names);
class CallRewriteArgs;
enum LookupScope {
CLASS_ONLY = 1,
......@@ -80,7 +84,8 @@ enum LookupScope {
CLASS_OR_INST = 3,
};
extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope, CallRewriteArgs* rewrite_args,
int64_t nargs, Box* arg1, Box* arg2, Box* arg3, Box** args);
ArgPassSpec argspec, Box* arg1, Box* arg2, Box* arg3, Box** args,
const std::vector<const std::string*>* keyword_names);
struct CompareRewriteArgs;
Box* compareInternal(Box* lhs, Box* rhs, int op_type, CompareRewriteArgs* rewrite_args);
......
......@@ -43,9 +43,9 @@ BoxIterator& BoxIterator::operator++() {
static std::string hasnext_str("__hasnext__");
static std::string next_str("next");
Box* hasnext = callattrInternal(iter, &hasnext_str, CLASS_ONLY, NULL, 0, NULL, NULL, NULL, NULL);
Box* hasnext = callattrInternal(iter, &hasnext_str, CLASS_ONLY, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
if (nonzero(hasnext)) {
value = callattrInternal(iter, &next_str, CLASS_ONLY, NULL, 0, NULL, NULL, NULL, NULL);
value = callattrInternal(iter, &next_str, CLASS_ONLY, NULL, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
} else {
iter = nullptr;
value = nullptr;
......@@ -56,7 +56,7 @@ BoxIterator& BoxIterator::operator++() {
llvm::iterator_range<BoxIterator> Box::pyElements() {
static std::string iter_str("__iter__");
Box* iter = callattr(const_cast<Box*>(this), &iter_str, true, 0, NULL, NULL, NULL, NULL);
Box* iter = callattr(const_cast<Box*>(this), &iter_str, true, ArgPassSpec(0), NULL, NULL, NULL, NULL, NULL);
if (iter) {
return llvm::iterator_range<BoxIterator>(++BoxIterator(iter), BoxIterator(nullptr));
}
......@@ -466,8 +466,11 @@ void setupRuntime() {
object_cls->giveAttr("__new__", new BoxedFunction(object_new));
object_cls->freeze();
auto typeCallObj = boxRTFunction((void*)typeCall, NULL, 1, true);
typeCallObj->internal_callable = &typeCallInternal;
type_cls->giveAttr("__call__", new BoxedFunction(typeCallObj));
type_cls->giveAttr("__name__", boxStrConstant("type"));
type_cls->giveAttr("__call__", new BoxedFunction(boxRTFunction((void*)typeCall, NULL, 1, true)));
type_cls->giveAttr("__new__", new BoxedFunction(boxRTFunction((void*)typeNew, NULL, 2, true)));
type_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)typeRepr, NULL, 1, true)));
type_cls->giveAttr("__str__", type_cls->getattr("__repr__"));
......
# expected: fail
# - keywords
def f(a, b, c):
print a, b, c
f(1, 2, 3)
f(1, b=2, c=3)
f(1, b=2, c=3)
f(1, c=2, b=3)
f(1, b="2", c=3)
f(1, b=2, c="3")
f(1, c="2", b=3)
f(1, c=2, b="3")
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