Commit 87539176 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Allow users to specify *args and **kw

Required a bit of refactoring in terms of how
parameter specifiers are passed around.
parent 0614be95
......@@ -142,10 +142,10 @@ private:
typedef DefinednessAnalysis::DefinitionLevel DefinitionLevel;
CFG* cfg;
AST_arguments* arguments;
const SourceInfo::ArgNames& arg_names;
public:
DefinednessBBAnalyzer(CFG* cfg, AST_arguments* arguments) : cfg(cfg), arguments(arguments) {}
DefinednessBBAnalyzer(CFG* cfg, const SourceInfo::ArgNames& arg_names) : cfg(cfg), arg_names(arg_names) {}
virtual DefinitionLevel merge(DefinitionLevel from, DefinitionLevel into) const {
assert(from != DefinednessAnalysis::Undefined);
......@@ -244,16 +244,25 @@ public:
}
return true;
}
friend class DefinednessBBAnalyzer;
};
void DefinednessBBAnalyzer::processBB(Map& starting, CFGBlock* block) const {
DefinednessVisitor visitor(starting);
if (block == cfg->getStartingBlock() && arg_names.args) {
for (auto e : (*arg_names.args))
visitor._doSet(e);
if (arg_names.vararg->size())
visitor._doSet(*arg_names.vararg);
if (arg_names.kwarg->size())
visitor._doSet(*arg_names.kwarg);
}
for (int i = 0; i < block->body.size(); i++) {
block->body[i]->accept(&visitor);
}
if (block == cfg->getStartingBlock() && arguments) {
arguments->accept(&visitor);
}
if (VERBOSITY("analysis") >= 2) {
printf("At end of block %d:\n", block->idx);
......@@ -263,9 +272,9 @@ void DefinednessBBAnalyzer::processBB(Map& starting, CFGBlock* block) const {
}
}
DefinednessAnalysis::DefinednessAnalysis(AST_arguments* args, CFG* cfg, ScopeInfo* scope_info)
DefinednessAnalysis::DefinednessAnalysis(const SourceInfo::ArgNames& arg_names, CFG* cfg, ScopeInfo* scope_info)
: scope_info(scope_info) {
results = computeFixedPoint(cfg, DefinednessBBAnalyzer(cfg, args), false);
results = computeFixedPoint(cfg, DefinednessBBAnalyzer(cfg, arg_names), false);
for (const auto& p : results) {
RequiredSet required;
......@@ -291,8 +300,10 @@ const DefinednessAnalysis::RequiredSet& DefinednessAnalysis::getDefinedNamesAt(C
return defined[block];
}
PhiAnalysis::PhiAnalysis(AST_arguments* args, CFG* cfg, LivenessAnalysis* liveness, ScopeInfo* scope_info)
: definedness(args, cfg, scope_info), liveness(liveness) {
PhiAnalysis::PhiAnalysis(const SourceInfo::ArgNames& arg_names, CFG* cfg, LivenessAnalysis* liveness,
ScopeInfo* scope_info)
: definedness(arg_names, cfg, scope_info), liveness(liveness) {
for (CFGBlock* block : cfg->blocks) {
RequiredSet required;
if (block->predecessors.size() < 2)
......@@ -351,7 +362,8 @@ LivenessAnalysis* computeLivenessInfo(CFG*) {
return new LivenessAnalysis();
}
PhiAnalysis* computeRequiredPhis(AST_arguments* args, CFG* cfg, LivenessAnalysis* liveness, ScopeInfo* scope_info) {
PhiAnalysis* computeRequiredPhis(const SourceInfo::ArgNames& args, CFG* cfg, LivenessAnalysis* liveness,
ScopeInfo* scope_info) {
return new PhiAnalysis(args, cfg, liveness, scope_info);
}
}
......@@ -19,6 +19,8 @@
#include <unordered_map>
#include <unordered_set>
#include "core/types.h"
namespace pyston {
class AST_arguments;
......@@ -51,7 +53,7 @@ private:
ScopeInfo* scope_info;
public:
DefinednessAnalysis(AST_arguments* args, CFG* cfg, ScopeInfo* scope_info);
DefinednessAnalysis(const SourceInfo::ArgNames& args, CFG* cfg, ScopeInfo* scope_info);
DefinitionLevel isDefinedAt(const std::string& name, CFGBlock* block);
const RequiredSet& getDefinedNamesAt(CFGBlock* block);
......@@ -66,7 +68,7 @@ private:
std::unordered_map<CFGBlock*, const RequiredSet> required_phis;
public:
PhiAnalysis(AST_arguments*, CFG* cfg, LivenessAnalysis* liveness, ScopeInfo* scope_info);
PhiAnalysis(const SourceInfo::ArgNames&, CFG* cfg, LivenessAnalysis* liveness, ScopeInfo* scope_info);
bool isRequired(const std::string& name, CFGBlock* block);
bool isRequiredAfter(const std::string& name, CFGBlock* block);
......@@ -76,7 +78,7 @@ public:
};
LivenessAnalysis* computeLivenessInfo(CFG*);
PhiAnalysis* computeRequiredPhis(AST_arguments*, CFG*, LivenessAnalysis*, ScopeInfo* scope_Info);
PhiAnalysis* computeRequiredPhis(const SourceInfo::ArgNames&, CFG*, LivenessAnalysis*, ScopeInfo* scope_Info);
}
#endif
......@@ -602,23 +602,37 @@ public:
return changed;
}
static PropagatingTypeAnalysis* doAnalysis(CFG* cfg, const std::vector<AST_expr*>& arg_names,
static PropagatingTypeAnalysis* doAnalysis(CFG* cfg, const SourceInfo::ArgNames& arg_names,
const std::vector<ConcreteCompilerType*>& arg_types,
SpeculationLevel speculation, ScopeInfo* scope_info) {
AllTypeMap starting_types;
ExprTypeMap expr_types;
TypeSpeculations type_speculations;
assert(arg_names.size() == arg_types.size());
assert(arg_names.totalParameters() == arg_types.size());
{
if (arg_names.args) {
TypeMap& initial_types = starting_types[cfg->getStartingBlock()];
for (int i = 0; i < arg_names.size(); i++) {
AST_expr* arg = arg_names[i];
int i = 0;
for (; i < arg_names.args->size(); i++) {
AST_expr* arg = (*arg_names.args)[i];
assert(arg->type == AST_TYPE::Name);
AST_Name* arg_name = ast_cast<AST_Name>(arg);
initial_types[arg_name->id] = unboxedType(arg_types[i]);
}
if (arg_names.vararg->size()) {
initial_types[*arg_names.vararg] = unboxedType(arg_types[i]);
i++;
}
if (arg_names.kwarg->size()) {
initial_types[*arg_names.kwarg] = unboxedType(arg_types[i]);
i++;
}
assert(i == arg_types.size());
}
std::unordered_set<CFGBlock*> in_queue;
......@@ -698,7 +712,7 @@ public:
// public entry point:
TypeAnalysis* doTypeAnalysis(CFG* cfg, const std::vector<AST_expr*>& arg_names,
TypeAnalysis* doTypeAnalysis(CFG* cfg, const SourceInfo::ArgNames& arg_names,
const std::vector<ConcreteCompilerType*>& arg_types,
TypeAnalysis::SpeculationLevel speculation, ScopeInfo* scope_info) {
// return new NullTypeAnalysis();
......
......@@ -43,7 +43,7 @@ public:
};
// TypeAnalysis* analyze(CFG *cfg, std::unordered_map<std::string, ConcreteCompilerType*> arg_types);
TypeAnalysis* doTypeAnalysis(CFG* cfg, const std::vector<AST_expr*>& arg_names,
TypeAnalysis* doTypeAnalysis(CFG* cfg, const SourceInfo::ArgNames& arg_names,
const std::vector<ConcreteCompilerType*>& arg_types,
TypeAnalysis::SpeculationLevel speculation, ScopeInfo* scope_info);
}
......
......@@ -275,8 +275,7 @@ computeBlockTraversalOrder(const BlockSet& full_blocks, const BlockSet& partial_
}
static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_guards, const GuardList& in_guards,
TypeAnalysis* types, const std::vector<AST_expr*>& arg_names,
const OSREntryDescriptor* entry_descriptor, const BlockSet& full_blocks,
TypeAnalysis* types, const OSREntryDescriptor* entry_descriptor, const BlockSet& full_blocks,
const BlockSet& partial_blocks) {
SourceInfo* source = irstate->getSourceInfo();
EffortLevel::EffortLevel effort = irstate->getEffortLevel();
......@@ -564,7 +563,7 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
emitter->getBuilder()->SetInsertPoint(llvm_entry_blocks[source->cfg->getStartingBlock()]);
}
generator->doFunctionEntry(arg_names, cf->spec->arg_types);
generator->doFunctionEntry(source->arg_names, cf->spec->arg_types);
// Function-entry safepoint:
// TODO might be more efficient to do post-call safepoints?
......@@ -896,8 +895,7 @@ static std::string getUniqueFunctionName(std::string nameprefix, EffortLevel::Ef
}
CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_descriptor,
EffortLevel::EffortLevel effort, FunctionSpecialization* spec,
const std::vector<AST_expr*>& arg_names, std::string nameprefix) {
EffortLevel::EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix) {
Timer _t("in doCompile");
if (VERBOSITY("irgen") >= 1)
......@@ -912,7 +910,7 @@ CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_
////
// Initializing the llvm-level structures:
int nargs = arg_names.size();
int nargs = source->arg_names.totalParameters();
ASSERT(nargs == spec->arg_types.size(), "%d %ld", nargs, spec->arg_types.size());
std::vector<llvm::Type*> llvm_arg_types;
......@@ -954,7 +952,7 @@ CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_
TypeAnalysis::SpeculationLevel speculation_level = TypeAnalysis::NONE;
if (ENABLE_SPECULATION && effort >= EffortLevel::MODERATE)
speculation_level = TypeAnalysis::SOME;
TypeAnalysis* types = doTypeAnalysis(source->cfg, arg_names, spec->arg_types, speculation_level,
TypeAnalysis* types = doTypeAnalysis(source->cfg, source->arg_names, spec->arg_types, speculation_level,
source->scoping->getScopeInfoForNode(source->ast));
GuardList guards;
......@@ -971,7 +969,7 @@ CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_
IRGenState irstate(cf, source, getGCBuilder(), dbg_funcinfo);
emitBBs(&irstate, "opt", guards, GuardList(), types, arg_names, entry_descriptor, full_blocks, partial_blocks);
emitBBs(&irstate, "opt", guards, GuardList(), types, entry_descriptor, full_blocks, partial_blocks);
// De-opt handling:
......@@ -990,10 +988,9 @@ CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_
assert(deopt_full_blocks.size() || deopt_partial_blocks.size());
TypeAnalysis* deopt_types = doTypeAnalysis(source->cfg, arg_names, spec->arg_types, TypeAnalysis::NONE,
TypeAnalysis* deopt_types = doTypeAnalysis(source->cfg, source->arg_names, spec->arg_types, TypeAnalysis::NONE,
source->scoping->getScopeInfoForNode(source->ast));
emitBBs(&irstate, "deopt", deopt_guards, guards, deopt_types, arg_names, NULL, deopt_full_blocks,
deopt_partial_blocks);
emitBBs(&irstate, "deopt", deopt_guards, guards, deopt_types, NULL, deopt_full_blocks, deopt_partial_blocks);
assert(deopt_guards.isEmpty());
deopt_guards.assertGotPatched();
......
......@@ -80,8 +80,7 @@ public:
};
CompiledFunction* doCompile(SourceInfo* source, const OSREntryDescriptor* entry_descriptor,
EffortLevel::EffortLevel effort, FunctionSpecialization* spec,
const std::vector<AST_expr*>& arg_names, std::string nameprefix);
EffortLevel::EffortLevel effort, FunctionSpecialization* spec, std::string nameprefix);
class TypeRecorder;
class OpInfo {
......
......@@ -42,45 +42,32 @@
namespace pyston {
// TODO terrible place for these!
const std::string SourceInfo::getName() {
assert(ast);
switch (ast->type) {
case AST_TYPE::FunctionDef:
return ast_cast<AST_FunctionDef>(ast)->name;
case AST_TYPE::Module:
return this->parent_module->name();
default:
RELEASE_ASSERT(0, "%d", ast->type);
SourceInfo::ArgNames::ArgNames(AST* ast) {
if (ast->type == AST_TYPE::Module) {
args = NULL;
kwarg = vararg = NULL;
} else if (ast->type == AST_TYPE::FunctionDef) {
AST_FunctionDef* f = ast_cast<AST_FunctionDef>(ast);
args = &f->args->args;
vararg = &f->args->vararg;
kwarg = &f->args->kwarg;
} else {
RELEASE_ASSERT(0, "%d", ast->type);
}
}
AST_arguments* SourceInfo::getArgsAST() {
const std::string SourceInfo::getName() {
assert(ast);
switch (ast->type) {
case AST_TYPE::FunctionDef:
return ast_cast<AST_FunctionDef>(ast)->args;
return ast_cast<AST_FunctionDef>(ast)->name;
case AST_TYPE::Module:
return NULL;
return this->parent_module->name();
default:
RELEASE_ASSERT(0, "%d", ast->type);
}
}
const std::vector<AST_expr*>& SourceInfo::getArgNames() {
static std::vector<AST_expr*> empty;
AST_arguments* args = getArgsAST();
if (args == NULL)
return empty;
return args->args;
}
const std::vector<AST_expr*>* CLFunction::getArgNames() {
if (!source)
return NULL;
return &source->getArgNames();
}
const std::vector<AST_stmt*>& SourceInfo::getBody() {
assert(ast);
switch (ast->type) {
......@@ -151,14 +138,6 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
assert(source);
std::string name = source->getName();
const std::vector<AST_expr*>& arg_names = source->getArgNames();
AST_arguments* args = source->getArgsAST();
if (args) {
// args object can be NULL if this is a module scope
assert(!args->vararg.size());
assert(!args->kwarg.size());
}
if (VERBOSITY("irgen") >= 1) {
std::string s;
......@@ -188,11 +167,11 @@ CompiledFunction* compileFunction(CLFunction* f, FunctionSpecialization* spec, E
assert(source->ast);
source->cfg = computeCFG(source->ast->type, source->getBody());
source->liveness = computeLivenessInfo(source->cfg);
source->phis = computeRequiredPhis(args, source->cfg, source->liveness,
source->phis = computeRequiredPhis(source->arg_names, source->cfg, source->liveness,
source->scoping->getScopeInfoForNode(source->ast));
}
CompiledFunction* cf = doCompile(source, entry, effort, spec, arg_names, name);
CompiledFunction* cf = doCompile(source, entry, effort, spec, name);
compileIR(cf, effort);
f->addVersion(cf);
......@@ -248,11 +227,10 @@ void compileAndRunModule(AST_Module* m, BoxedModule* bm) {
ScopingAnalysis* scoping = runScopingAnalysis(m);
SourceInfo* si = new SourceInfo(bm, scoping);
SourceInfo* si = new SourceInfo(bm, scoping, m);
si->cfg = computeCFG(AST_TYPE::Module, m->body);
si->ast = m;
si->liveness = computeLivenessInfo(si->cfg);
si->phis = computeRequiredPhis(NULL, si->cfg, si->liveness, si->scoping->getScopeInfoForNode(si->ast));
si->phis = computeRequiredPhis(si->arg_names, si->cfg, si->liveness, si->scoping->getScopeInfoForNode(si->ast));
CLFunction* cl_f = new CLFunction(0, 0, false, false, si);
......
......@@ -1479,7 +1479,8 @@ private:
CLFunction*& cl = made[node];
if (cl == NULL) {
SourceInfo* si = new SourceInfo(irstate->getSourceInfo()->parent_module, irstate->getSourceInfo()->scoping);
SourceInfo* si
= new SourceInfo(irstate->getSourceInfo()->parent_module, irstate->getSourceInfo()->scoping, node);
si->ast = node;
cl = new CLFunction(node->args->args.size(), node->args->defaults.size(), node->args->vararg.size(),
node->args->kwarg.size(), si);
......@@ -2138,8 +2139,10 @@ public:
return CLOSURE;
}
void doFunctionEntry(const std::vector<AST_expr*>& arg_names,
void doFunctionEntry(const SourceInfo::ArgNames& arg_names,
const std::vector<ConcreteCompilerType*>& arg_types) override {
assert(arg_names.totalParameters() == arg_types.size());
auto scope_info = irstate->getScopeInfo();
llvm::Value* passed_closure = NULL;
......@@ -2159,27 +2162,50 @@ public:
}
int i = 0;
llvm::Value* argarray = NULL;
for (; AI != irstate->getLLVMFunction()->arg_end(); ++AI, i++) {
std::vector<llvm::Value*> python_parameters;
for (int i = 0; i < arg_types.size(); i++) {
assert(AI != irstate->getLLVMFunction()->arg_end());
if (i == 3) {
argarray = AI;
assert(++AI == irstate->getLLVMFunction()->arg_end());
for (int i = 3; i < arg_types.size(); i++) {
llvm::Value* ptr = emitter.getBuilder()->CreateConstGEP1_32(AI, i - 3);
llvm::Value* loaded = emitter.getBuilder()->CreateLoad(ptr);
if (arg_types[i]->llvmType() == g.i64)
loaded = emitter.getBuilder()->CreatePtrToInt(loaded, arg_types[i]->llvmType());
else
assert(arg_types[i]->llvmType() == g.llvm_value_type_ptr);
python_parameters.push_back(loaded);
}
++AI;
break;
}
loadArgument(arg_names[i], arg_types[i], AI, ExcInfo::none());
python_parameters.push_back(AI);
++AI;
}
for (int i = 3; i < arg_types.size(); i++) {
llvm::Value* ptr = emitter.getBuilder()->CreateConstGEP1_32(argarray, i - 3);
llvm::Value* loaded = emitter.getBuilder()->CreateLoad(ptr);
assert(AI == irstate->getLLVMFunction()->arg_end());
assert(python_parameters.size() == arg_names.totalParameters());
if (arg_types[i]->llvmType() == g.i64)
loaded = emitter.getBuilder()->CreatePtrToInt(loaded, arg_types[i]->llvmType());
else
assert(arg_types[i]->llvmType() == g.llvm_value_type_ptr);
if (arg_names.args) {
int i = 0;
for (; i < arg_names.args->size(); i++) {
loadArgument((*arg_names.args)[i], arg_types[i], python_parameters[i], ExcInfo::none());
}
if (arg_names.vararg->size()) {
loadArgument(*arg_names.vararg, arg_types[i], python_parameters[i], ExcInfo::none());
i++;
}
if (arg_names.kwarg->size()) {
loadArgument(*arg_names.kwarg, arg_types[i], python_parameters[i], ExcInfo::none());
i++;
}
loadArgument(arg_names[i], arg_types[i], loaded, ExcInfo::none());
assert(i == arg_types.size());
}
}
......
......@@ -183,7 +183,7 @@ public:
virtual ~IRGenerator() {}
virtual void doFunctionEntry(const std::vector<AST_expr*>& arg_names,
virtual void doFunctionEntry(const SourceInfo::ArgNames& arg_names,
const std::vector<ConcreteCompilerType*>& arg_types) = 0;
virtual void giveLocalSymbol(const std::string& name, CompilerVariable* var) = 0;
......
......@@ -204,13 +204,27 @@ public:
LivenessAnalysis* liveness;
PhiAnalysis* phis;
struct ArgNames {
const std::vector<AST_expr*>* args;
const std::string* vararg, *kwarg;
explicit ArgNames(AST* ast);
int totalParameters() const {
if (!args)
return 0;
return args->size() + (vararg->size() == 0 ? 0 : 1) + (kwarg->size() == 0 ? 0 : 1);
}
};
ArgNames arg_names;
const std::string getName();
AST_arguments* getArgsAST();
const std::vector<AST_expr*>& getArgNames();
// AST_arguments* getArgsAST();
const std::vector<AST_stmt*>& getBody();
SourceInfo(BoxedModule* m, ScopingAnalysis* scoping)
: parent_module(m), scoping(scoping), ast(NULL), cfg(NULL), liveness(NULL), phis(NULL) {}
SourceInfo(BoxedModule* m, ScopingAnalysis* scoping, AST* ast)
: parent_module(m), scoping(scoping), ast(ast), cfg(NULL), liveness(NULL), phis(NULL), arg_names(ast) {}
};
typedef std::vector<CompiledFunction*> FunctionList;
......@@ -242,7 +256,7 @@ public:
int numReceivedArgs() { return num_args + (takes_varargs ? 1 : 0) + (takes_kwargs ? 1 : 0); }
const std::vector<AST_expr*>* getArgNames();
// const std::vector<AST_expr*>* getArgNames();
void addVersion(CompiledFunction* compiled) {
assert(compiled);
......
......@@ -1833,7 +1833,7 @@ Box* callFunc(BoxedFunction* func, CallRewriteArgs* rewrite_args, ArgPassSpec ar
getArg(f->num_args + (f->takes_varargs ? 1 : 0), oarg1, oarg2, oarg3, oargs) = okwargs;
}
const std::vector<AST_expr*>* arg_names = f->getArgNames();
const std::vector<AST_expr*>* arg_names = f->source ? f->source->arg_names.args : NULL;
if (arg_names == nullptr && argspec.num_keywords) {
raiseExcHelper(TypeError, "<function @%p>() doesn't take keyword arguments", f->versions[0]->code);
}
......
# expected: fail
# - varargs
# varargs (but not starargs yet)
# varargs
def f(*args):
print args
......
# expected: fail
# - varargs, kwarg
# Regression test: make sure that args and kw get properly treated as potentially-saved-in-closure
def f1(*args):
......
......@@ -10,3 +10,19 @@ f(1, b="2", c=3)
f(1, b=2, c="3")
f(1, c="2", b=3)
f(1, c=2, b="3")
def f(*args, **kw):
print args, kw
f()
f(1)
f(1, 2)
f((1, 2))
f(*(1, 2))
f({1:2}, b=2)
def f(a=1, b=2, **kw):
print a, b, kw
f(b=3, c=4)
f(b=3, a=4)
......@@ -38,6 +38,6 @@ TEST(func_analysis, augassign) {
ASSERT_TRUE(liveness->isLiveAtEnd("a", block));
}
PhiAnalysis* phis = computeRequiredPhis(func->args, cfg, liveness, scope_info);
PhiAnalysis* phis = computeRequiredPhis(SourceInfo::ArgNames(func), cfg, liveness, scope_info);
}
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