Commit 8604ba34 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Only defined locals go into the class dict

Before we would write all local names into the class dict;
this is close, but was including undefined names.

Have to get help from the IR generator to determine the live names,
so add a LOCALS opcode (CPython has a similar one), and use that to
generate the dict that a classdef returns.

The current support is not quite enough to support the locals() function,
since even though we have that information available at the IR generator,
it's not easy to make it available to the runtime.
parent a9b94f08
......@@ -95,6 +95,9 @@ public:
};
bool LivenessAnalysis::isLiveAtEnd(const std::string& name, CFGBlock* block) {
if (name[0] != '#')
return true;
if (block->successors.size() == 0)
return false;
......
......@@ -359,6 +359,8 @@ private:
return BOOL;
case AST_LangPrimitive::LANDINGPAD:
return UNKNOWN;
case AST_LangPrimitive::LOCALS:
return DICT;
default:
RELEASE_ASSERT(0, "%d", node->opcode);
}
......
......@@ -350,6 +350,53 @@ private:
}
return new ConcreteCompilerVariable(UNKNOWN, exc_obj, true);
}
case AST_LangPrimitive::LOCALS: {
assert(node->args.size() == 0);
llvm::Value* v = emitter.getBuilder()->CreateCall(g.funcs.createDict);
ConcreteCompilerVariable* rtn = new ConcreteCompilerVariable(DICT, v, true);
for (auto& p : symbol_table) {
if (p.first[0] == '!')
continue;
ConcreteCompilerVariable* is_defined_var = static_cast<ConcreteCompilerVariable*>(
_getFake(_getFakeName("is_defined", p.first.c_str()), true));
static const std::string setitem_str("__setitem__");
if (!is_defined_var) {
ConcreteCompilerVariable* converted = p.second->makeConverted(emitter, p.second->getBoxType());
// TODO super dumb that it reallocates the name again
CompilerVariable* _r
= rtn->callattr(emitter, getEmptyOpInfo(exc_info), &setitem_str, true, ArgPassSpec(2),
{ makeStr(new std::string(p.first)), converted }, NULL);
converted->decvref(emitter);
_r->decvref(emitter);
} else {
assert(is_defined_var->getType() == BOOL);
llvm::BasicBlock* was_defined
= llvm::BasicBlock::Create(g.context, "was_defined", irstate->getLLVMFunction());
llvm::BasicBlock* join
= llvm::BasicBlock::Create(g.context, "join", irstate->getLLVMFunction());
emitter.getBuilder()->CreateCondBr(is_defined_var->getValue(), was_defined, join);
emitter.getBuilder()->SetInsertPoint(was_defined);
ConcreteCompilerVariable* converted = p.second->makeConverted(emitter, p.second->getBoxType());
// TODO super dumb that it reallocates the name again
CompilerVariable* _r
= rtn->callattr(emitter, getEmptyOpInfo(exc_info), &setitem_str, true, ArgPassSpec(2),
{ makeStr(new std::string(p.first)), converted }, NULL);
converted->decvref(emitter);
_r->decvref(emitter);
emitter.getBuilder()->CreateBr(join);
emitter.getBuilder()->SetInsertPoint(join);
}
}
return rtn;
}
default:
RELEASE_ASSERT(0, "%d", node->opcode);
}
......@@ -634,9 +681,10 @@ private:
}
std::string defined_name = _getFakeName("is_defined", node->id.c_str());
ConcreteCompilerVariable* is_defined = static_cast<ConcreteCompilerVariable*>(_popFake(defined_name, true));
ConcreteCompilerVariable* is_defined_var
= static_cast<ConcreteCompilerVariable*>(_getFake(defined_name, true));
if (is_defined) {
if (is_defined_var) {
// classdefs have different scoping rules than functions:
if (irstate->getSourceInfo()->ast->type == AST_TYPE::ClassDef) {
llvm::BasicBlock* from_local
......@@ -645,7 +693,7 @@ private:
= llvm::BasicBlock::Create(g.context, "from_global", irstate->getLLVMFunction());
llvm::BasicBlock* join = llvm::BasicBlock::Create(g.context, "join", irstate->getLLVMFunction());
emitter.getBuilder()->CreateCondBr(is_defined->getValue(), from_local, from_global);
emitter.getBuilder()->CreateCondBr(is_defined_var->getValue(), from_local, from_global);
emitter.getBuilder()->SetInsertPoint(from_local);
CompilerVariable* local = symbol_table[node->id];
......@@ -665,8 +713,11 @@ private:
return new ConcreteCompilerVariable(UNKNOWN, phi, true);
}
emitter.createCall2(exc_info, g.funcs.assertNameDefined, is_defined->getValue(),
emitter.createCall2(exc_info, g.funcs.assertNameDefined, is_defined_var->getValue(),
getStringConstantPtr(node->id + '\0'));
// At this point we know the name must be defined (otherwise the assert would have fired):
_popFake(defined_name);
}
CompilerVariable* rtn = symbol_table[node->id];
......
......@@ -628,14 +628,18 @@ Box* interpretFunction(llvm::Function* f, int nargs, Box* closure, Box* arg1, Bo
r = reinterpret_cast<int64_t (*)(int64_t, double, double)>(f)(args[0].n, args[1].d,
args[2].d);
break;
case 0b100000:
case 0b100000: // 32
r = reinterpret_cast<int64_t (*)(int64_t, int64_t, int64_t, int64_t)>(f)(
args[0].n, args[1].n, args[2].n, args[3].n);
break;
case 0b100001:
case 0b100001: // 33
r = reinterpret_cast<int64_t (*)(int64_t, int64_t, int64_t, double)>(f)(
args[0].n, args[1].n, args[2].n, args[3].d);
break;
case 0b100010: // 34
r = reinterpret_cast<int64_t (*)(int64_t, int64_t, double, int64_t)>(f)(
args[0].n, args[1].n, args[2].d, args[3].n);
break;
case 0b100110:
r = reinterpret_cast<int64_t (*)(int64_t, double, double, int64_t)>(f)(
args[0].n, args[1].d, args[2].d, args[3].n);
......@@ -644,15 +648,15 @@ Box* interpretFunction(llvm::Function* f, int nargs, Box* closure, Box* arg1, Bo
r = reinterpret_cast<int64_t (*)(double, int, double, int64_t)>(f)(args[0].d, args[1].n,
args[2].d, args[3].n);
break;
case 0b1000000:
case 0b1000000: // 64
r = reinterpret_cast<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);
break;
case 0b10000000:
case 0b10000000: // 128
r = reinterpret_cast<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);
break;
case 0b100000000:
case 0b100000000: // 256
r = reinterpret_cast<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);
......
......@@ -1302,6 +1302,9 @@ bool PrintVisitor::visit_langprimitive(AST_LangPrimitive* node) {
case AST_LangPrimitive::LANDINGPAD:
printf("landingpad");
break;
case AST_LangPrimitive::LOCALS:
printf("locals");
break;
default:
RELEASE_ASSERT(0, "%d", node->opcode);
}
......
......@@ -867,6 +867,7 @@ public:
enum Opcodes {
ISINSTANCE,
LANDINGPAD,
LOCALS,
} opcode;
std::vector<AST_expr*> args;
......
......@@ -1702,20 +1702,10 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
if (source->ast->type == AST_TYPE::ClassDef) {
ScopeInfo* scope_info = source->scoping->getScopeInfoForNode(source->ast);
auto written_names = scope_info->getClassDefLocalNames();
AST_Dict* rtn_dict = new AST_Dict();
// Even if the user never explicitly wrote to __module__, there was an
// implicit write:
assert(written_names.count("__module__"));
for (auto s : written_names) {
rtn_dict->keys.push_back(new AST_Str(s));
rtn_dict->values.push_back(makeName(s, AST_TYPE::Load));
}
AST_LangPrimitive* locals = new AST_LangPrimitive(AST_LangPrimitive::LOCALS);
AST_Return* rtn = new AST_Return();
rtn->value = rtn_dict;
rtn->value = locals;
visitor.push_back(rtn);
} else {
// Put a fake "return" statement at the end of every function just to make sure they all have one;
......
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