Commit 0999d34a authored by Kevin Modzelewski's avatar Kevin Modzelewski Committed by Kevin Modzelewski

Move is_defined names out of the symbol table

parent 061e994b
......@@ -51,12 +51,30 @@ public:
return v[vreg];
}
void clear() {
int n = v.size();
for (int i = 0; i < n; i++) {
v[i] = T();
}
}
int numSet() {
int n = v.size();
int r = 0;
for (int i = 0; i < n; i++) {
if (v[i] != T())
r++;
}
return r;
}
class iterator {
public:
const VRegMap<T>& map;
int i;
iterator(const VRegMap<T>& map, int i) : map(map), i(i) {}
// TODO: make this skip unset values?
iterator& operator++() {
i++;
return *this;
......@@ -66,6 +84,8 @@ public:
bool operator!=(const iterator& rhs) const { return !(*this == rhs); }
std::pair<int, const T&> operator*() { return std::pair<int, const T&>(i, map[i]); }
int first() const { return i; }
const T& second() const { return map[i]; }
};
int numVregs() const { return v.size(); }
......
......@@ -2429,8 +2429,11 @@ public:
}
};
ConcreteCompilerType* BOOL = new BoolType();
llvm::Value* makeLLVMBool(bool b) {
return llvm::ConstantInt::get(BOOL->llvmType(), b, false);
}
ConcreteCompilerVariable* makeBool(bool b) {
return new ConcreteCompilerVariable(BOOL, llvm::ConstantInt::get(BOOL->llvmType(), b, false));
return new ConcreteCompilerVariable(BOOL, makeLLVMBool(b));
}
ConcreteCompilerVariable* doIs(IREmitter& emitter, CompilerVariable* lhs, CompilerVariable* rhs, bool negate) {
......@@ -2858,6 +2861,17 @@ llvm::Value* i1FromBool(IREmitter& emitter, ConcreteCompilerVariable* v) {
}
}
llvm::Value* i1FromLLVMBool(IREmitter& emitter, llvm::Value* v) {
if (BOOLS_AS_I64) {
assert(v->getType() == BOOL->llvmType());
assert(BOOL->llvmType() == g.i64);
llvm::Value* v2 = emitter.getBuilder()->CreateTrunc(v, g.i1);
return v2;
} else {
return v;
}
}
ConcreteCompilerType* LIST, *SLICE, *MODULE, *DICT, *SET, *FROZENSET, *LONG, *BOXED_COMPLEX;
......
......@@ -346,6 +346,7 @@ CompilerVariable* makeFloat(double);
CompilerVariable* makeUnboxedFloat(IREmitter&, ConcreteCompilerVariable*);
CompilerVariable* makeUnboxedFloat(IREmitter&, llvm::Value*);
llvm::Value* makeLLVMBool(bool b);
ConcreteCompilerVariable* makeBool(bool);
ConcreteCompilerVariable* makeLong(IREmitter&, Box*);
ConcreteCompilerVariable* makePureImaginary(IREmitter&, Box*);
......@@ -372,6 +373,7 @@ CompilerType* makeFuncType(ConcreteCompilerType* rtn_type, const std::vector<Con
ConcreteCompilerVariable* boolFromI1(IREmitter&, llvm::Value*);
llvm::Value* i1FromBool(IREmitter&, ConcreteCompilerVariable*);
llvm::Value* i1FromLLVMBool(IREmitter&, llvm::Value*);
template <typename V>
CompilerVariable* _ValuedCompilerType<V>::getPystonIter(IREmitter& emitter, const OpInfo& info, VAR* var) {
......
......@@ -297,13 +297,6 @@ static ConcreteCompilerType* getTypeAtBlockStart(TypeAnalysis* types, InternedSt
}
}
static bool shouldPhisOwnThisSym(llvm::StringRef name) {
// generating unnecessary increfs to the passed generator would introduce cycles inside the generator
if (name == PASSED_GENERATOR_NAME)
assert(0);
return true;
}
llvm::Value* handlePotentiallyUndefined(ConcreteCompilerVariable* is_defined_var, llvm::Type* rtn_type,
llvm::BasicBlock*& cur_block, IREmitter& emitter, bool speculate_undefined,
std::function<llvm::Value*(IREmitter&)> when_defined,
......@@ -361,6 +354,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
CFG* cfg = source->cfg;
auto&& vreg_info = cfg->getVRegInfo();
int num_vregs = vreg_info.getTotalNumOfVRegs();
if (entry_descriptor != NULL)
assert(blocks.count(cfg->getStartingBlock()) == 0);
......@@ -383,7 +377,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
// the function entry block, where we add the type guards [no guards anymore]
llvm::BasicBlock* osr_entry_block = NULL;
llvm::BasicBlock* osr_unbox_block_end = NULL; // the block after type guards where we up/down-convert things
ConcreteSymbolTable* osr_syms = NULL; // syms after conversion
std::unordered_map<InternedString, ConcreteCompilerVariable*>* osr_syms = NULL; // syms after conversion
if (entry_descriptor != NULL) {
llvm::BasicBlock* osr_unbox_block = llvm::BasicBlock::Create(g.context, "osr_unbox", irstate->getLLVMFunction(),
&irstate->getLLVMFunction()->getEntryBlock());
......@@ -391,8 +385,8 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
&irstate->getLLVMFunction()->getEntryBlock());
assert(&irstate->getLLVMFunction()->getEntryBlock() == osr_entry_block);
osr_syms = new ConcreteSymbolTable();
SymbolTable* initial_syms = new SymbolTable();
osr_syms = new std::unordered_map<InternedString, ConcreteCompilerVariable*>();
auto initial_syms = new std::unordered_map<InternedString, CompilerVariable*>();
// llvm::BranchInst::Create(llvm_entry_blocks[entry_descriptor->backedge->target->idx], entry_block);
llvm::BasicBlock* osr_entry_block_end = osr_entry_block;
......@@ -499,10 +493,14 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
////
// Main ir generation: go through each basic block in the CFG and emit the code
// TODO: switch these to unique_ptr
std::unordered_map<CFGBlock*, SymbolTable*> ending_symbol_tables;
std::unordered_map<CFGBlock*, ConcreteSymbolTable*> phi_ending_symbol_tables;
typedef std::map<InternedString, std::pair<ConcreteCompilerType*, llvm::PHINode*>> PHITable;
std::unordered_map<CFGBlock*, DefinednessTable*> definedness_tables;
typedef VRegMap<std::pair<ConcreteCompilerType*, llvm::PHINode*>> PHITable;
typedef VRegMap<llvm::PHINode*> DefinednessPHITable;
std::unordered_map<CFGBlock*, PHITable*> created_phis;
std::unordered_map<CFGBlock*, DefinednessPHITable*> created_definedness_phis;
std::unordered_map<CFGBlock*, llvm::SmallVector<IRGenerator::ExceptionState, 2>> incoming_exception_state;
CFGBlock* initial_block = NULL;
......@@ -536,8 +534,10 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
llvm::BasicBlock* entry_block_end = llvm_entry_blocks[block];
std::unique_ptr<IREmitter> emitter(createIREmitter(irstate, entry_block_end));
PHITable* phis = new PHITable();
PHITable* phis = new PHITable(num_vregs);
created_phis[block] = phis;
DefinednessPHITable* definedness_phis = new DefinednessPHITable(num_vregs);
created_definedness_phis[block] = definedness_phis;
// Set initial symbol table:
// If we're in the starting block, no phis or symbol table changes for us.
......@@ -612,25 +612,40 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
irstate->setupFrameInfoVarOSR(osr_frame_info_arg);
for (const auto& p : entry_descriptor->args) {
// Don't add the frame info to the symbol table since we will store it separately
// (we manually added it during the calculation of osr_syms):
if (p.first.s() == FRAME_INFO_PTR_NAME)
continue;
ConcreteCompilerType* analyzed_type = getTypeAtBlockStart(types, p.first, vreg_info, block);
// printf("For %s, given %s, analyzed for %s\n", p.first.c_str(), p.second->debugName().c_str(),
// analyzed_type->debugName().c_str());
assert(p.first.s() != FRAME_INFO_PTR_NAME);
assert(p.first.s() != PASSED_CLOSURE_NAME);
assert(p.first.s() != CREATED_CLOSURE_NAME);
assert(p.first.s() != PASSED_GENERATOR_NAME);
if (p.first.s()[0] == '!') {
assert(startswith(p.first.s(), "!is_defined_"));
assert(p.second == BOOL);
auto base_name = source->getInternedStrings().get(p.first.s().substr(12));
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(BOOL->llvmType(),
block->predecessors.size() + 1, p.first.s());
int vreg = vreg_info.getVReg(base_name);
generator->giveDefinednessVar(vreg, phi);
(*definedness_phis)[vreg] = phi;
} else {
int vreg = vreg_info.getVReg(p.first);
ConcreteCompilerType* analyzed_type = getTypeAtBlockStart(types, p.first, vreg_info, block);
// printf("For %s, given %s, analyzed for %s\n", p.first.c_str(), p.second->debugName().c_str(),
// analyzed_type->debugName().c_str());
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(analyzed_type->llvmType(),
block->predecessors.size() + 1, p.first.s());
if (analyzed_type->getBoxType() == analyzed_type) {
irstate->getRefcounts()->setType(phi, RefType::OWNED);
}
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(analyzed_type->llvmType(),
block->predecessors.size() + 1, p.first.s());
if (analyzed_type->getBoxType() == analyzed_type) {
RefType type = shouldPhisOwnThisSym(p.first.s()) ? RefType::OWNED : RefType::BORROWED;
irstate->getRefcounts()->setType(phi, type);
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(analyzed_type, phi);
generator->giveLocalSymbol(vreg, var);
(*phis)[vreg] = std::make_pair(analyzed_type, phi);
}
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(analyzed_type, phi);
generator->giveLocalSymbol(p.first, var);
(*phis)[p.first] = std::make_pair(analyzed_type, phi);
}
} else if (pred == NULL) {
assert(traversal_order.size() < cfg->blocks.size());
......@@ -643,11 +658,15 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
}
RELEASE_ASSERT(0, "Rotted code");
#if 0
std::set<InternedString> names;
for (const int vreg : phi_analysis->getAllRequiredFor(block)) {
auto s = cfg->getVRegInfo().getName(vreg);
names.insert(s);
if (phi_analysis->isPotentiallyUndefinedAfter(vreg, block->predecessors[0])) {
assert(0 && "this should go in definedness_names or some such");
names.insert(getIsDefinedName(s, source->getInternedStrings()));
}
}
......@@ -664,8 +683,11 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(type, phi);
generator->giveLocalSymbol(s, var);
assert(s.s()[0] != '!' && "implement me");
(*phis)[s] = std::make_pair(type, phi);
}
#endif
} else {
assert(pred);
assert(blocks.count(pred));
......@@ -673,7 +695,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
if (block->predecessors.size() == 1) {
// If this block has only one predecessor, it by definition doesn't need any phi nodes.
// Assert that the phi_st is empty, and just create the symbol table from the non-phi st:
ASSERT(phi_ending_symbol_tables[pred]->size() == 0, "%d %d", block->idx, pred->idx);
ASSERT(phi_ending_symbol_tables[pred]->numSet() == 0, "%d %d", block->idx, pred->idx);
assert(ending_symbol_tables.count(pred));
// Filter out any names set by an invoke statement at the end of the previous block, if we're in the
......@@ -716,6 +738,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
assert(asgn->targets.size() == 1);
if (asgn->targets[0]->type == AST_TYPE::Name) {
InternedString name = ast_cast<AST_Name>(asgn->targets[0])->id;
int vreg = ast_cast<AST_Name>(asgn->targets[0])->vreg;
assert(name.c_str()[0] == '#'); // it must be a temporary
// You might think I need to check whether `name' is being assigned globally or locally,
// since a global assign doesn't affect the symbol table. However, the CFG pass only
......@@ -724,14 +747,19 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
// TODO: inefficient
sym_table = new SymbolTable(*sym_table);
ASSERT(sym_table->count(name), "%d %s\n", block->idx, name.c_str());
sym_table->erase(name);
ASSERT((*sym_table)[vreg] != NULL, "%d %s\n", block->idx, name.c_str());
(*sym_table)[vreg] = NULL;
created_new_sym_table = true;
}
}
}
generator->copySymbolsFrom(sym_table);
for (auto&& p : *definedness_tables[pred]) {
if (!p.second)
continue;
generator->giveDefinednessVar(p.first, p.second);
}
if (created_new_sym_table)
delete sym_table;
} else {
......@@ -747,28 +775,34 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
// And go through and add phi nodes:
ConcreteSymbolTable* pred_st = phi_ending_symbol_tables[pred];
// We have to sort the phi table by name in order to a get a deterministic ordering for the JIT object
// cache.
typedef std::pair<InternedString, ConcreteCompilerVariable*> Entry;
std::vector<Entry> sorted_pred_st(pred_st->begin(), pred_st->end());
std::sort(sorted_pred_st.begin(), sorted_pred_st.end(),
[](const Entry& lhs, const Entry& rhs) { return lhs.first < rhs.first; });
for (auto& entry : sorted_pred_st) {
InternedString name = entry.first;
ConcreteCompilerVariable* cv = entry.second; // incoming CCV from predecessor block
// printf("block %d: adding phi for %s from pred %d\n", block->idx, name.c_str(), pred->idx);
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(cv->getType()->llvmType(),
block->predecessors.size(), name.s());
for (int vreg = 0; vreg < num_vregs; vreg++) {
ConcreteCompilerVariable* cv = (*pred_st)[vreg];
if (!cv)
continue;
llvm::PHINode* phi
= emitter->getBuilder()->CreatePHI(cv->getType()->llvmType(), block->predecessors.size());
if (VERBOSITY("irgen"))
phi->setName(vreg_info.getName(vreg).s());
if (cv->getType()->getBoxType() == cv->getType()) {
RefType type = shouldPhisOwnThisSym(name.s()) ? RefType::OWNED : RefType::BORROWED;
irstate->getRefcounts()->setType(phi, type);
irstate->getRefcounts()->setType(phi, RefType::OWNED);
}
// emitter->getBuilder()->CreateCall(g.funcs.dump, phi);
ConcreteCompilerVariable* var = new ConcreteCompilerVariable(cv->getType(), phi);
generator->giveLocalSymbol(name, var);
generator->giveLocalSymbol(vreg, var);
(*phis)[vreg] = std::make_pair(cv->getType(), phi);
}
(*phis)[name] = std::make_pair(cv->getType(), phi);
for (auto&& p : *definedness_tables[pred]) {
if (!p.second)
continue;
llvm::PHINode* phi = emitter->getBuilder()->CreatePHI(BOOL->llvmType(), block->predecessors.size());
if (VERBOSITY("irgen"))
phi->setName("!is_defined_" + vreg_info.getName(p.first).s());
generator->giveDefinednessVar(p.first, phi);
(*definedness_phis)[p.first] = phi;
}
}
}
......@@ -794,6 +828,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
const IRGenerator::EndingState& ending_st = generator->getEndingSymbolTable();
ending_symbol_tables[block] = ending_st.symbol_table;
phi_ending_symbol_tables[block] = ending_st.phi_symbol_table;
definedness_tables[block] = ending_st.definedness_vars;
llvm_exit_blocks[block] = ending_st.ending_block;
if (ending_st.exception_state.size()) {
......@@ -806,7 +841,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
}
if (into_hax.count(block))
ASSERT(ending_st.symbol_table->size() == 0, "%d", block->idx);
ASSERT(ending_st.symbol_table->numSet() == 0, "%d", block->idx);
}
////
......@@ -836,22 +871,20 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
// printf("(%d %ld) -> (%d %ld)\n", bpred->idx, phi_ending_symbol_tables[bpred]->size(), b->idx,
// phis->size());
ASSERT(sameKeyset(phi_ending_symbol_tables[bpred], phis), "%d->%d", bpred->idx, b->idx);
assert(phi_ending_symbol_tables[bpred]->size() == phis->size());
assert(phi_ending_symbol_tables[bpred]->numSet() == phis->numSet());
}
if (this_is_osr_entry) {
assert(sameKeyset(osr_syms, phis));
int nondefined_syms = 0;
for (auto&& p : *osr_syms) {
if (p.first.s()[0] != '!')
nondefined_syms++;
}
assert(nondefined_syms == phis->numSet());
}
#endif // end checking phi agreement.
// Can't always add the phi incoming value right away, since we may have to create more
// basic blocks as part of type coercion.
// Instead, just make a record of the phi node, value, and the location of the from-BB,
// which we won't read until after all new BBs have been added.
std::vector<std::tuple<llvm::PHINode*, llvm::Value*, llvm::BasicBlock*&>> phi_args;
for (auto it = phis->begin(); it != phis->end(); ++it) {
llvm::PHINode* llvm_phi = it->second.second;
auto handle_phi = [&](llvm::PHINode* llvm_phi, int vreg, CompilerType* phi_type, bool is_defined_name) {
for (int j = 0; j < b->predecessors.size(); j++) {
CFGBlock* bpred = b->predecessors[j];
if (blocks.count(bpred) == 0)
......@@ -861,34 +894,59 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
if (llvm::isa<llvm::UnreachableInst>(terminator))
continue;
ConcreteCompilerVariable* v = (*phi_ending_symbol_tables[bpred])[it->first];
assert(v);
// Make sure they all prepared for the same type:
ASSERT(it->second.first == v->getType(), "%d %d: %s %s %s", b->idx, bpred->idx, it->first.c_str(),
it->second.first->debugName().c_str(), v->getType()->debugName().c_str());
llvm::Value* val = v->getValue();
llvm_phi->addIncoming(v->getValue(), llvm_exit_blocks[b->predecessors[j]]);
llvm::Value* val;
CompilerType* this_type;
if (!is_defined_name) {
ConcreteCompilerVariable* v = (*phi_ending_symbol_tables[bpred])[vreg];
assert(v);
// Make sure they all prepared for the same type:
ASSERT(phi_type == v->getType(), "%d %d: %d %s %s", b->idx, bpred->idx, vreg,
phi_type->debugName().c_str(), v->getType()->debugName().c_str());
val = v->getValue();
this_type = v->getType();
} else {
val = (*definedness_tables[bpred])[vreg];
this_type = BOOL;
}
assert(val);
llvm_phi->addIncoming(val, llvm_exit_blocks[b->predecessors[j]]);
if (v->getType()->getBoxType() == v->getType() && shouldPhisOwnThisSym(it->first.s())) {
if (this_type->getBoxType() == this_type) {
// llvm::outs() << *v->getValue() << " is getting consumed by phi " << *llvm_phi << '\n';
assert(llvm::isa<llvm::BranchInst>(terminator));
irstate->getRefcounts()->refConsumed(v->getValue(), terminator);
irstate->getRefcounts()->refConsumed(val, terminator);
}
}
if (this_is_osr_entry) {
ConcreteCompilerVariable* v = (*osr_syms)[it->first];
ConcreteCompilerVariable* v;
if (!is_defined_name)
v = (*osr_syms)[vreg_info.getName(vreg)];
else
v = (*osr_syms)[getIsDefinedName(vreg_info.getName(vreg), source->getInternedStrings())];
assert(v);
ASSERT(it->second.first == v->getType(), "");
ASSERT(phi_type == phi_type, "");
llvm_phi->addIncoming(v->getValue(), osr_unbox_block_end);
}
};
for (auto it = phis->begin(); it != phis->end(); ++it) {
llvm::PHINode* llvm_phi = it.second().second;
if (!llvm_phi)
continue;
handle_phi(llvm_phi, it.first(), it.second().first, false);
}
for (auto t : phi_args) {
RELEASE_ASSERT(0, "this hasn't been hit in a very long time -- check refcounting");
std::get<0>(t)->addIncoming(std::get<1>(t), std::get<2>(t));
auto definedness_phis = created_definedness_phis[b];
for (auto it = definedness_phis->begin(); it != definedness_phis->end(); ++it) {
llvm::PHINode* llvm_phi = it.second();
if (!llvm_phi)
continue;
handle_phi(llvm_phi, it.first(), BOOL, true);
}
}
......@@ -898,7 +956,9 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
continue;
delete ending_symbol_tables[b];
delete phi_ending_symbol_tables[b];
delete created_phis[b];
delete definedness_tables[b];
}
if (entry_descriptor) {
......@@ -1101,8 +1161,10 @@ CompiledFunction* doCompile(FunctionMetadata* md, SourceInfo* source, ParamNames
IRGenState irstate(md, cf, source, std::move(phis), param_names, getGCBuilder(), dbg_funcinfo, &refcounter);
emitBBs(&irstate, types, entry_descriptor, blocks);
assert(!llvm::verifyFunction(*f, &llvm::errs()));
RefcountTracker::addRefcounts(&irstate);
assert(!llvm::verifyFunction(*f, &llvm::errs()));
int num_instructions = std::distance(llvm::inst_begin(f), llvm::inst_end(f));
static StatCounter num_llvm_insts("num_llvm_insts");
......
......@@ -797,6 +797,8 @@ private:
IREmitterImpl emitter;
// symbol_table tracks which (non-global) python variables are bound to which CompilerVariables
SymbolTable symbol_table;
DefinednessTable definedness_vars;
std::unordered_map<CFGBlock*, llvm::BasicBlock*>& entry_blocks;
CFGBlock* myblock;
TypeAnalysis* types;
......@@ -825,6 +827,8 @@ public:
: irstate(irstate),
curblock(entry_blocks[myblock]),
emitter(irstate, curblock, this),
symbol_table(myblock->cfg->getVRegInfo().getTotalNumOfVRegs()),
definedness_vars(myblock->cfg->getVRegInfo().getTotalNumOfVRegs()),
entry_blocks(entry_blocks),
myblock(myblock),
types(types),
......@@ -889,9 +893,9 @@ private:
return irstate->getSourceInfo()->getInternedStrings().get(std::forward<T>(s));
}
InternedString getIsDefinedName(InternedString name) {
return pyston::getIsDefinedName(name, irstate->getSourceInfo()->getInternedStrings());
}
// InternedString getIsDefinedName(InternedString name) {
// return pyston::getIsDefinedName(name, irstate->getSourceInfo()->getInternedStrings());
//}
CompilerVariable* evalAttribute(AST_Attribute* node, const UnwindInfo& unw_info) {
CompilerVariable* value = evalExpr(node->value, unw_info);
......@@ -1417,7 +1421,7 @@ private:
return new ConcreteCompilerVariable(UNKNOWN, r);
} else {
// vst is one of {FAST, CLOSURE}
if (symbol_table.find(node->id) == symbol_table.end()) {
if (!symbol_table[node->vreg]) {
// TODO should mark as DEAD here, though we won't end up setting all the names appropriately
// state = DEAD;
llvm::CallSite call = emitter.createCall(
......@@ -1429,24 +1433,22 @@ private:
return undefVariable();
}
InternedString defined_name = getIsDefinedName(node->id);
ConcreteCompilerVariable* is_defined_var
= static_cast<ConcreteCompilerVariable*>(_getFake(defined_name, true));
auto is_defined_var = getDefinedVar(node->vreg, true);
if (is_defined_var) {
emitter.createCall(
unw_info, g.funcs.assertNameDefined,
{ i1FromBool(emitter, is_defined_var), embedRelocatablePtr(node->id.c_str(), g.i8_ptr),
{ i1FromLLVMBool(emitter, is_defined_var), embedRelocatablePtr(node->id.c_str(), g.i8_ptr),
emitter.setType(embedRelocatablePtr(UnboundLocalError, g.llvm_class_type_ptr), RefType::BORROWED),
getConstantInt(true, g.i1) });
// At this point we know the name must be defined (otherwise the assert would have fired):
_popFake(defined_name);
popDefinedVar(node->vreg);
}
CompilerVariable* rtn = symbol_table[node->id];
CompilerVariable* rtn = symbol_table[node->vreg];
if (is_kill)
symbol_table.erase(node->id);
symbol_table[node->vreg] = NULL;
return rtn;
}
}
......@@ -1893,32 +1895,24 @@ private:
return evalSliceExprPost(node, unw_info, rtn);
}
void _setFake(InternedString name, CompilerVariable* val) {
assert(name.s()[0] == '!');
CompilerVariable*& cur = symbol_table[name];
void setDefinedVar(int vreg, llvm::Value* val) {
// printf("Setting definedness var for %s\n", name.c_str());
assert(val->getType() == BOOL->llvmType());
llvm::Value*& cur = definedness_vars[vreg];
assert(cur == NULL);
cur = val;
}
// whether a Python variable FOO might be undefined or not is determined by whether the corresponding is_defined_FOO
// variable is present in our symbol table. If it is, then it *might* be undefined. If it isn't, then it either is
// definitely defined, or definitely isn't.
//
// to check whether a variable is in our symbol table, call _getFake with allow_missing = true and check whether the
// result is NULL.
CompilerVariable* _getFake(InternedString name, bool allow_missing = false) {
assert(name.s()[0] == '!');
auto it = symbol_table.find(name);
if (it == symbol_table.end()) {
llvm::Value* getDefinedVar(int vreg, bool allow_missing = false) {
auto r = definedness_vars[vreg];
if (!r)
assert(allow_missing);
return NULL;
}
return it->second;
return r;
}
CompilerVariable* _popFake(InternedString name, bool allow_missing = false) {
CompilerVariable* rtn = _getFake(name, allow_missing);
symbol_table.erase(name);
llvm::Value* popDefinedVar(int vreg, bool allow_missing = false) {
llvm::Value* rtn = getDefinedVar(vreg, allow_missing);
definedness_vars[vreg] = NULL;
if (!allow_missing)
assert(rtn != NULL);
return rtn;
......@@ -1979,13 +1973,12 @@ private:
} else {
// FAST or CLOSURE
CompilerVariable* prev = symbol_table[name];
symbol_table[name] = val;
CompilerVariable* prev = symbol_table[vreg];
symbol_table[vreg] = val;
// Clear out the is_defined name since it is now definitely defined:
assert(!isIsDefinedName(name.s()));
InternedString defined_name = getIsDefinedName(name);
bool maybe_was_undefined = _popFake(defined_name, true);
bool maybe_was_undefined = popDefinedVar(vreg, true);
if (vst == ScopeInfo::VarScopeType::CLOSURE) {
size_t offset = irstate->getScopeInfo()->getClosureOffset(name);
......@@ -2205,15 +2198,14 @@ private:
// SyntaxError: can not delete variable 'x' referenced in nested scope
assert(vst == ScopeInfo::VarScopeType::FAST);
CompilerVariable* prev = symbol_table.count(target->id) ? symbol_table[target->id] : NULL;
CompilerVariable* prev = symbol_table[target->vreg];
InternedString defined_name = getIsDefinedName(target->id);
ConcreteCompilerVariable* is_defined_var = static_cast<ConcreteCompilerVariable*>(_getFake(defined_name, true));
llvm::Value* is_defined_var = getDefinedVar(target->vreg, true);
_setVRegIfUserVisible(target->vreg, []() { return getNullPtr(g.llvm_value_type_ptr); }, prev,
(bool)is_defined_var);
if (symbol_table.count(target->id) == 0) {
if (!symbol_table[target->vreg]) {
llvm::CallSite call = emitter.createCall(
unw_info, g.funcs.assertNameDefined,
{ getConstantInt(0, g.i1), embedConstantPtr(target->id.c_str(), g.i8_ptr),
......@@ -2226,13 +2218,13 @@ private:
if (is_defined_var) {
emitter.createCall(
unw_info, g.funcs.assertNameDefined,
{ i1FromBool(emitter, is_defined_var), embedConstantPtr(target->id.c_str(), g.i8_ptr),
{ i1FromLLVMBool(emitter, is_defined_var), embedConstantPtr(target->id.c_str(), g.i8_ptr),
emitter.setType(embedRelocatablePtr(NameError, g.llvm_class_type_ptr), RefType::BORROWED),
getConstantInt(true /*local_error_msg*/, g.i1) });
_popFake(defined_name);
popDefinedVar(target->vreg);
}
symbol_table.erase(target->id);
symbol_table[target->vreg] = NULL;
}
void doExec(AST_Exec* node, const UnwindInfo& unw_info) {
......@@ -2664,7 +2656,7 @@ private:
_doSet(name, var, unw_info);
}
bool allowableFakeEndingSymbol(InternedString name) { return isIsDefinedName(name.s()); }
// bool allowableFakeEndingSymbol(InternedString name) { return isIsDefinedName(name.s()); }
void endBlock(State new_state) {
assert(state == RUNNING);
......@@ -2675,40 +2667,37 @@ private:
auto cfg = source->cfg;
ScopeInfo* scope_info = irstate->getScopeInfo();
// Sort the names here to make the process deterministic:
std::map<InternedString, CompilerVariable*> sorted_symbol_table(symbol_table.begin(), symbol_table.end());
for (const auto& p : sorted_symbol_table) {
assert(p.first.s() != FRAME_INFO_PTR_NAME);
assert(p.second->getType()->isUsable());
if (allowableFakeEndingSymbol(p.first))
int num_vregs = symbol_table.numVregs();
for (int vreg = 0; vreg < num_vregs; vreg++) {
if (!symbol_table[vreg])
continue;
// ASSERT(p.first[0] != '!' || isIsDefinedName(p.first), "left a fake variable in the real
// symbol table? '%s'", p.first.c_str());
auto val = symbol_table[vreg];
assert(val->getType()->isUsable());
int vreg = cfg->getVRegInfo().getVReg(p.first);
if (!irstate->getLiveness()->isLiveAtEnd(vreg, myblock)) {
symbol_table.erase(getIsDefinedName(p.first));
symbol_table.erase(p.first);
popDefinedVar(vreg, true);
symbol_table[vreg] = NULL;
} else if (irstate->getPhis()->isRequiredAfter(vreg, myblock)) {
assert(scope_info->getScopeTypeOfName(p.first) != ScopeInfo::VarScopeType::GLOBAL);
assert(scope_info->getScopeTypeOfName(cfg->getVRegInfo().getName(vreg))
!= ScopeInfo::VarScopeType::GLOBAL);
ConcreteCompilerType* phi_type = types->getTypeAtBlockEnd(vreg, myblock);
assert(phi_type->isUsable());
// printf("Converting %s from %s to %s\n", p.first.c_str(),
// p.second->getType()->debugName().c_str(), phi_type->debugName().c_str());
// printf("have to convert %s from %s to %s\n", p.first.c_str(),
// p.second->getType()->debugName().c_str(), phi_type->debugName().c_str());
ConcreteCompilerVariable* v = p.second->makeConverted(emitter, phi_type);
symbol_table[p.first] = v;
ConcreteCompilerVariable* v = val->makeConverted(emitter, phi_type);
symbol_table[vreg] = v;
} else {
if (myblock->successors.size()) {
// TODO getTypeAtBlockEnd will automatically convert up to the concrete type, which we don't
// want
// here, but this is just for debugging so I guess let it happen for now:
ConcreteCompilerType* ending_type = types->getTypeAtBlockEnd(vreg, myblock);
RELEASE_ASSERT(p.second->canConvertTo(ending_type), "%s is supposed to be %s, but somehow is %s",
p.first.c_str(), ending_type->debugName().c_str(),
p.second->getType()->debugName().c_str());
RELEASE_ASSERT(val->canConvertTo(ending_type), "%s is supposed to be %s, but somehow is %s",
cfg->getVRegInfo().getName(vreg).c_str(), ending_type->debugName().c_str(),
val->getType()->debugName().c_str());
}
}
}
......@@ -2718,22 +2707,19 @@ private:
if (VERBOSITY() >= 3)
printf("phi will be required for %s\n", name.c_str());
assert(scope_info->getScopeTypeOfName(name) != ScopeInfo::VarScopeType::GLOBAL);
CompilerVariable*& cur = symbol_table[name];
InternedString defined_name = getIsDefinedName(name);
CompilerVariable*& cur = symbol_table[vreg];
if (cur != NULL) {
// printf("defined on this path; ");
ConcreteCompilerVariable* is_defined
= static_cast<ConcreteCompilerVariable*>(_popFake(defined_name, true));
llvm::Value* is_defined = popDefinedVar(vreg, true);
if (irstate->getPhis()->isPotentiallyUndefinedAfter(vreg, myblock)) {
// printf("is potentially undefined later, so marking it defined\n");
if (is_defined) {
_setFake(defined_name, is_defined);
setDefinedVar(vreg, is_defined);
} else {
_setFake(defined_name, makeBool(1));
setDefinedVar(vreg, makeLLVMBool(1));
}
} else {
// printf("is defined in all later paths, so not marking\n");
......@@ -2755,7 +2741,7 @@ private:
}
cur = new ConcreteCompilerVariable(phi_type, v);
_setFake(defined_name, makeBool(0));
setDefinedVar(vreg, makeLLVMBool(0));
}
}
......@@ -2766,12 +2752,18 @@ public:
void addFrameStackmapArgs(PatchpointInfo* pp, std::vector<llvm::Value*>& stackmap_args) override {
int initial_args = stackmap_args.size();
auto&& vregs = irstate->getSourceInfo()->cfg->getVRegInfo();
// For deopts we need to add the compiler created names to the stackmap
if (ENABLE_FRAME_INTROSPECTION && pp->isDeopt()) {
// TODO: don't need to use a sorted symbol table if we're explicitly recording the names!
// nice for debugging though.
typedef std::pair<InternedString, CompilerVariable*> Entry;
std::vector<Entry> sorted_symbol_table(symbol_table.begin(), symbol_table.end());
std::vector<Entry> sorted_symbol_table;
for (auto&& p : symbol_table) {
if (p.second)
sorted_symbol_table.push_back(Entry(vregs.getName(p.first), p.second));
}
// TODO: at some point it would be nice to pass these separately
auto source = irstate->getSourceInfo();
......@@ -2814,7 +2806,8 @@ public:
SourceInfo* source = irstate->getSourceInfo();
SymbolTable* st = new SymbolTable(symbol_table);
ConcreteSymbolTable* phi_st = new ConcreteSymbolTable();
ConcreteSymbolTable* phi_st = new ConcreteSymbolTable(symbol_table.numVregs());
DefinednessTable* def_vars = new DefinednessTable(definedness_vars);
auto cfg = source->cfg;
auto&& vreg_info = cfg->getVRegInfo();
......@@ -2823,17 +2816,19 @@ public:
assert(incoming_exc_state.empty());
for (auto&& p : symbol_table) {
ASSERT(p.second->getType()->isUsable(), "%s", p.first.c_str());
if (!p.second)
continue;
ASSERT(p.second->getType()->isUsable(), "%d", p.first);
}
if (myblock->successors.size() == 0) {
st->clear();
symbol_table.clear();
return EndingState(st, phi_st, curblock, outgoing_exc_state);
return EndingState(st, phi_st, def_vars, curblock, outgoing_exc_state);
} else if (myblock->successors.size() > 1) {
// Since there are no critical edges, all successors come directly from this node,
// so there won't be any required phis.
return EndingState(st, phi_st, curblock, outgoing_exc_state);
return EndingState(st, phi_st, def_vars, curblock, outgoing_exc_state);
}
assert(myblock->successors.size() == 1); // other cases should have been handled
......@@ -2843,39 +2838,29 @@ public:
// If the next block has a single predecessor, don't have to
// emit any phis.
// Should probably not emit no-op jumps like this though.
return EndingState(st, phi_st, curblock, outgoing_exc_state);
return EndingState(st, phi_st, def_vars, curblock, outgoing_exc_state);
}
// We have one successor, but they have more than one predecessor.
// We're going to sort out which symbols need to go in phi_st and which belong inst.
for (SymbolTable::iterator it = st->begin(); it != st->end();) {
if (allowableFakeEndingSymbol(it->first)
|| irstate->getPhis()->isRequiredAfter(cfg->getVRegInfo().getVReg(it->first), myblock)) {
// this conversion should have already happened... should refactor this.
ConcreteCompilerType* ending_type;
if (isIsDefinedName(it->first.s())) {
assert(it->second->getType() == BOOL);
ending_type = BOOL;
} else if (it->first.s() == PASSED_CLOSURE_NAME) {
RELEASE_ASSERT(0, "");
} else if (it->first.s() == CREATED_CLOSURE_NAME) {
RELEASE_ASSERT(0, "");
} else if (it->first.s() == PASSED_GENERATOR_NAME) {
RELEASE_ASSERT(0, "");
} else if (it->first.s() == FRAME_INFO_PTR_NAME) {
RELEASE_ASSERT(0, "");
} else {
ending_type = types->getTypeAtBlockEnd(vreg_info.getVReg(it->first), myblock);
}
for (auto&& p : *st) {
if (!p.second)
continue;
if (/*allowableFakeEndingSymbol(it->first)
||*/ irstate->getPhis()->isRequiredAfter(p.first, myblock)) {
ConcreteCompilerType* ending_type = types->getTypeAtBlockEnd(p.first, myblock);
assert(ending_type->isUsable());
//(*phi_st)[it->first] = it->second->makeConverted(emitter, it->second->getConcreteType());
(*phi_st)[it->first] = it->second->makeConverted(emitter, ending_type);
it = st->erase(it);
} else {
++it;
//(*phi_st)[p->first] = p->second->makeConverted(emp, p->second->getConcreteType());
(*phi_st)[p.first] = p.second->makeConverted(emitter, ending_type);
(*st)[p.first] = NULL;
}
}
return EndingState(st, phi_st, curblock, outgoing_exc_state);
return EndingState(st, phi_st, def_vars, curblock, outgoing_exc_state);
}
void giveDefinednessVar(int vreg, llvm::Value* val) override {
// printf("Giving definedness var %s\n", irstate->getSourceInfo()->cfg->getVRegInfo().getName(vreg).c_str());
setDefinedVar(vreg, val);
}
void giveLocalSymbol(InternedString name, CompilerVariable* var) override {
......@@ -2884,12 +2869,18 @@ public:
assert(name.s() != CREATED_CLOSURE_NAME);
assert(name.s() != PASSED_CLOSURE_NAME);
assert(name.s() != PASSED_GENERATOR_NAME);
assert(name.s()[0] != '!');
ASSERT(irstate->getScopeInfo()->getScopeTypeOfName(name) != ScopeInfo::VarScopeType::GLOBAL, "%s",
name.c_str());
ASSERT(var->getType()->isUsable(), "%s", name.c_str());
int vreg = irstate->getSourceInfo()->cfg->getVRegInfo().getVReg(name);
giveLocalSymbol(vreg, var);
}
void giveLocalSymbol(int vreg, CompilerVariable* var) override {
assert(var->getType()->isUsable());
CompilerVariable*& cur = symbol_table[name];
CompilerVariable*& cur = symbol_table[vreg];
assert(cur == NULL);
cur = var;
}
......@@ -2898,9 +2889,11 @@ public:
assert(st);
DupCache cache;
for (SymbolTable::iterator it = st->begin(); it != st->end(); ++it) {
if (!it.second())
continue;
// printf("Copying in %s, a %s\n", it->first.c_str(), it->second->getType()->debugName().c_str());
symbol_table[it->first] = it->second->dup(cache);
assert(symbol_table[it->first]->getType()->isUsable());
symbol_table[it.first()] = it.second()->dup(cache);
assert(symbol_table[it.first()]->getType()->isUsable());
}
}
......@@ -3028,10 +3021,11 @@ public:
}
void run(const CFGBlock* block) override {
auto&& vregs = block->cfg->getVRegInfo();
if (VERBOSITY("irgenerator") >= 2) { // print starting symbol table
printf(" %d init:", block->idx);
for (auto it = symbol_table.begin(); it != symbol_table.end(); ++it)
printf(" %s", it->first.c_str());
printf(" %s", vregs.getName(it.first()).c_str());
printf("\n");
}
for (int i = 0; i < block->body.size(); i++) {
......@@ -3050,7 +3044,7 @@ public:
if (VERBOSITY("irgenerator") >= 2) { // print ending symbol table
printf(" %d fini:", block->idx);
for (auto it = symbol_table.begin(); it != symbol_table.end(); ++it)
printf(" %s", it->first.c_str());
printf(" %s", vregs.getName(it.first()).c_str());
printf("\n");
}
}
......
......@@ -21,6 +21,7 @@
#include "llvm/ADT/iterator_range.h"
#include "llvm/IR/Instructions.h"
#include "analysis/function_analysis.h"
#include "core/stringpool.h"
#include "core/types.h"
......@@ -43,9 +44,10 @@ class TypeAnalysis;
class RefcountTracker;
class UnwindInfo;
typedef std::unordered_map<InternedString, CompilerVariable*> SymbolTable;
typedef std::map<InternedString, CompilerVariable*> SortedSymbolTable;
typedef std::unordered_map<InternedString, ConcreteCompilerVariable*> ConcreteSymbolTable;
typedef VRegMap<CompilerVariable*> SymbolTable;
typedef VRegMap<llvm::Value*> DefinednessTable;
typedef VRegMap<CompilerVariable*> SortedSymbolTable;
typedef VRegMap<ConcreteCompilerVariable*> ConcreteSymbolTable;
extern const std::string CREATED_CLOSURE_NAME;
extern const std::string PASSED_CLOSURE_NAME;
......@@ -153,15 +155,20 @@ public:
// symbol_table records which Python variables are bound to what CompilerVariables at the end of this block.
// phi_symbol_table records the ones that will need to be `phi'd.
// both only record non-globals.
// TODO: switch these to unique_ptr's
SymbolTable* symbol_table;
ConcreteSymbolTable* phi_symbol_table;
DefinednessTable* definedness_vars;
llvm::BasicBlock* ending_block;
llvm::SmallVector<ExceptionState, 2> exception_state;
EndingState(SymbolTable* symbol_table, ConcreteSymbolTable* phi_symbol_table, llvm::BasicBlock* ending_block,
EndingState(SymbolTable* symbol_table, ConcreteSymbolTable* phi_symbol_table,
DefinednessTable* definedness_vars, llvm::BasicBlock* ending_block,
llvm::ArrayRef<ExceptionState> exception_state)
: symbol_table(symbol_table),
phi_symbol_table(phi_symbol_table),
definedness_vars(definedness_vars),
ending_block(ending_block),
exception_state(exception_state.begin(), exception_state.end()) {}
};
......@@ -172,6 +179,8 @@ public:
= 0;
virtual void giveLocalSymbol(InternedString name, CompilerVariable* var) = 0;
virtual void giveLocalSymbol(int vreg, CompilerVariable* var) = 0;
virtual void giveDefinednessVar(int vreg, llvm::Value* val) = 0;
virtual void copySymbolsFrom(SymbolTable* st) = 0;
virtual void run(const CFGBlock* block) = 0; // primary entry point
virtual EndingState getEndingSymbolTable() = 0;
......
......@@ -95,19 +95,19 @@ void removeDirectoryIfExists(const std::string& path);
// Checks that lhs and rhs, which are iterables of InternedStrings, have the
// same set of names in them.
template <class T1, class T2> bool sameKeyset(T1* lhs, T2* rhs) {
std::vector<InternedString> lv, rv;
for (typename T1::iterator it = lhs->begin(); it != lhs->end(); it++) {
lv.push_back(it->first);
std::vector<int> lv, rv;
for (typename T1::iterator it = lhs->begin(); it != lhs->end(); ++it) {
lv.push_back((*it).first);
}
for (typename T2::iterator it = rhs->begin(); it != rhs->end(); it++) {
rv.push_back(it->first);
for (typename T2::iterator it = rhs->begin(); it != rhs->end(); ++it) {
rv.push_back((*it).first);
}
std::sort(lv.begin(), lv.end());
std::sort(rv.begin(), rv.end());
std::vector<InternedString> lextra(lv.size());
std::vector<InternedString>::iterator diffend
std::vector<int> lextra(lv.size());
std::vector<int>::iterator diffend
= std::set_difference(lv.begin(), lv.end(), rv.begin(), rv.end(), lextra.begin());
lextra.resize(diffend - lextra.begin());
......@@ -115,19 +115,19 @@ template <class T1, class T2> bool sameKeyset(T1* lhs, T2* rhs) {
if (lextra.size()) {
printf("Only in lhs:\n");
for (int i = 0; i < lextra.size(); i++) {
printf("%s\n", lextra[i].c_str());
printf("%d\n", lextra[i]);
}
good = false;
}
std::vector<InternedString> rextra(rv.size());
std::vector<int> rextra(rv.size());
diffend = std::set_difference(rv.begin(), rv.end(), lv.begin(), lv.end(), rextra.begin());
rextra.resize(diffend - rextra.begin());
if (rextra.size()) {
printf("Only in rhs:\n");
for (int i = 0; i < rextra.size(); i++) {
printf("%s\n", rextra[i].c_str());
printf("%d\n", rextra[i]);
}
good = false;
}
......
# I think we have some other similar tests to this, but it's hard to find them.
def f(x):
if x:
y = 1
if '':
pass
print y
for i in xrange(10000):
f(1)
try:
f(0)
assert 0
except UnboundLocalError:
pass
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