Commit 1d7dcaf1 authored by Travis Hance's avatar Travis Hance Committed by Travis Hance

clean-up to the rewriter two-pass stuff

parent 1c1a34e4
...@@ -138,7 +138,8 @@ void RewriterVar::addGuard(uint64_t val) { ...@@ -138,7 +138,8 @@ void RewriterVar::addGuard(uint64_t val) {
void Rewriter::_addGuard(RewriterVar* var, uint64_t val) { void Rewriter::_addGuard(RewriterVar* var, uint64_t val) {
assembler::Register var_reg = var->getInReg(); assembler::Register var_reg = var->getInReg();
if (isLargeConstant(val)) { if (isLargeConstant(val)) {
assembler::Register reg = allocReg(Location::any()); assembler::Register reg = allocReg(Location::any(), /* otherThan */ var_reg);
assert(reg != var_reg);
assembler->mov(assembler::Immediate(val), reg); assembler->mov(assembler::Immediate(val), reg);
assembler->cmp(var_reg, reg); assembler->cmp(var_reg, reg);
} else { } else {
...@@ -158,7 +159,8 @@ void RewriterVar::addGuardNotEq(uint64_t val) { ...@@ -158,7 +159,8 @@ void RewriterVar::addGuardNotEq(uint64_t val) {
void Rewriter::_addGuardNotEq(RewriterVar* var, uint64_t val) { void Rewriter::_addGuardNotEq(RewriterVar* var, uint64_t val) {
assembler::Register var_reg = var->getInReg(); assembler::Register var_reg = var->getInReg();
if (isLargeConstant(val)) { if (isLargeConstant(val)) {
assembler::Register reg = allocReg(Location::any()); assembler::Register reg = allocReg(Location::any(), /* otherThan */ var_reg);
assert(var_reg != reg);
assembler->mov(assembler::Immediate(val), reg); assembler->mov(assembler::Immediate(val), reg);
assembler->cmp(var_reg, reg); assembler->cmp(var_reg, reg);
} else { } else {
...@@ -211,6 +213,7 @@ void Rewriter::_getAttr(RewriterVar* result, RewriterVar* ptr, int offset, Locat ...@@ -211,6 +213,7 @@ void Rewriter::_getAttr(RewriterVar* result, RewriterVar* ptr, int offset, Locat
assembler::Register newvar_reg = result->initializeInReg(dest); assembler::Register newvar_reg = result->initializeInReg(dest);
assembler->mov_generic(assembler::Indirect(ptr_reg, offset), newvar_reg, type); assembler->mov_generic(assembler::Indirect(ptr_reg, offset), newvar_reg, type);
result->releaseIfNoUses();
assertConsistent(); assertConsistent();
} }
...@@ -228,6 +231,7 @@ void Rewriter::_getAttrDouble(RewriterVar* result, RewriterVar* ptr, int offset, ...@@ -228,6 +231,7 @@ void Rewriter::_getAttrDouble(RewriterVar* result, RewriterVar* ptr, int offset,
assembler::XMMRegister newvar_reg = result->initializeInXMMReg(dest); assembler::XMMRegister newvar_reg = result->initializeInXMMReg(dest);
assembler->movsd(assembler::Indirect(ptr_reg, offset), newvar_reg); assembler->movsd(assembler::Indirect(ptr_reg, offset), newvar_reg);
result->releaseIfNoUses();
assertConsistent(); assertConsistent();
} }
...@@ -248,6 +252,7 @@ void Rewriter::_getAttrFloat(RewriterVar* result, RewriterVar* ptr, int offset, ...@@ -248,6 +252,7 @@ void Rewriter::_getAttrFloat(RewriterVar* result, RewriterVar* ptr, int offset,
// cast to double // cast to double
assembler->cvtss2sd(newvar_reg, newvar_reg); assembler->cvtss2sd(newvar_reg, newvar_reg);
result->releaseIfNoUses();
assertConsistent(); assertConsistent();
} }
...@@ -280,6 +285,7 @@ void Rewriter::_cmp(RewriterVar* result, RewriterVar* v1, AST_TYPE::AST_TYPE cmp ...@@ -280,6 +285,7 @@ void Rewriter::_cmp(RewriterVar* result, RewriterVar* v1, AST_TYPE::AST_TYPE cmp
RELEASE_ASSERT(0, "%d", cmp_type); RELEASE_ASSERT(0, "%d", cmp_type);
} }
result->releaseIfNoUses();
assertConsistent(); assertConsistent();
} }
...@@ -300,6 +306,7 @@ void Rewriter::_toBool(RewriterVar* result, RewriterVar* var, Location dest) { ...@@ -300,6 +306,7 @@ void Rewriter::_toBool(RewriterVar* result, RewriterVar* var, Location dest) {
assembler->test(this_reg, this_reg); assembler->test(this_reg, this_reg);
assembler->setnz(result_reg); assembler->setnz(result_reg);
result->releaseIfNoUses();
assertConsistent(); assertConsistent();
} }
...@@ -497,6 +504,7 @@ void Rewriter::_loadConst(RewriterVar* result, int64_t val, Location dest) { ...@@ -497,6 +504,7 @@ void Rewriter::_loadConst(RewriterVar* result, int64_t val, Location dest) {
assembler->mov(assembler::Immediate(val), reg); assembler->mov(assembler::Immediate(val), reg);
result->initializeInReg(reg); result->initializeInReg(reg);
result->releaseIfNoUses();
assertConsistent(); assertConsistent();
} }
...@@ -668,6 +676,8 @@ void Rewriter::_call(RewriterVar* result, bool can_call_into_python, void* func_ ...@@ -668,6 +676,8 @@ void Rewriter::_call(RewriterVar* result, bool can_call_into_python, void* func_
result->initializeInReg(assembler::RAX); result->initializeInReg(assembler::RAX);
assertConsistent(); assertConsistent();
result->releaseIfNoUses();
} }
void Rewriter::abort() { void Rewriter::abort() {
...@@ -680,6 +690,8 @@ void Rewriter::abort() { ...@@ -680,6 +690,8 @@ void Rewriter::abort() {
} }
void RewriterVar::bumpUse() { void RewriterVar::bumpUse() {
rewriter->assertPhaseEmitting();
next_use++; next_use++;
assert(next_use <= uses.size()); assert(next_use <= uses.size());
if (next_use == uses.size()) { if (next_use == uses.size()) {
...@@ -695,8 +707,23 @@ void RewriterVar::bumpUse() { ...@@ -695,8 +707,23 @@ void RewriterVar::bumpUse() {
} }
} }
// Call this on the result at the end of the action in which it's created
// TODO we should have a better way of dealing with variables that have 0 uses
void RewriterVar::releaseIfNoUses() {
rewriter->assertPhaseEmitting();
if (uses.size() == 0) {
assert(next_use == 0);
for (Location loc : locations) {
rewriter->vars_by_location.erase(loc);
}
this->locations.clear();
}
}
void Rewriter::commit() { void Rewriter::commit() {
assert(!finished); assert(!finished);
initPhaseEmitting();
if (assembler->hasFailed()) { if (assembler->hasFailed()) {
static StatCounter rewriter_assemblyfail("rewriter_assemblyfail"); static StatCounter rewriter_assemblyfail("rewriter_assemblyfail");
...@@ -720,10 +747,15 @@ void Rewriter::commit() { ...@@ -720,10 +747,15 @@ void Rewriter::commit() {
// Emit assembly for each action, and set done_guarding when // Emit assembly for each action, and set done_guarding when
// we reach the last guard. // we reach the last guard.
// Note: If an arg finishes its uses before we're done gurading, we don't release it at that point;
// instead, we release it here, at the point when we set done_guarding.
// An alternate, maybe cleaner, way to accomplish this would be to add a use for each arg
// at each guard in the var's `uses` list.
// First: check if we're done guarding before we even begin emitting.
if (last_guard_action == -1) { if (last_guard_action == -1) {
done_guarding = true; done_guarding = true;
// maybe we should track this like we do the other uses
for (RewriterVar* arg : args) { for (RewriterVar* arg : args) {
if (arg->next_use == arg->uses.size()) { if (arg->next_use == arg->uses.size()) {
for (Location loc : arg->locations) { for (Location loc : arg->locations) {
...@@ -736,6 +768,7 @@ void Rewriter::commit() { ...@@ -736,6 +768,7 @@ void Rewriter::commit() {
assertConsistent(); assertConsistent();
// Now, start emitting assembly; check if we're dong guarding after each.
for (int i = 0; i < actions.size(); i++) { for (int i = 0; i < actions.size(); i++) {
actions[i].action(); actions[i].action();
...@@ -846,17 +879,6 @@ void Rewriter::commit() { ...@@ -846,17 +879,6 @@ void Rewriter::commit() {
assert(var->isInLocation(ru)); assert(var->isInLocation(ru));
} }
// To make this check work, we need to kill vars which have 0 uses
// TODO we should not emit any code for these variables...
for (RewriterVar* var : vars) {
if (var->uses.size() == 0) {
for (Location l : var->locations) {
vars_by_location.erase(l);
}
var->locations.clear();
}
}
for (RewriterVar* live_out : live_outs) { for (RewriterVar* live_out : live_outs) {
live_out->bumpUse(); live_out->bumpUse();
} }
...@@ -898,6 +920,8 @@ void Rewriter::addDependenceOn(ICInvalidator& invalidator) { ...@@ -898,6 +920,8 @@ void Rewriter::addDependenceOn(ICInvalidator& invalidator) {
} }
Location Rewriter::allocScratch() { Location Rewriter::allocScratch() {
assertPhaseEmitting();
int scratch_bytes = rewrite->getScratchBytes(); int scratch_bytes = rewrite->getScratchBytes();
for (int i = 0; i < scratch_bytes; i += 8) { for (int i = 0; i < scratch_bytes; i += 8) {
Location l(Location::Scratch, i); Location l(Location::Scratch, i);
...@@ -932,6 +956,7 @@ void Rewriter::_add(RewriterVar* result, RewriterVar* a, int64_t b, Location des ...@@ -932,6 +956,7 @@ void Rewriter::_add(RewriterVar* result, RewriterVar* a, int64_t b, Location des
a->bumpUse(); a->bumpUse();
result->releaseIfNoUses();
assertConsistent(); assertConsistent();
} }
...@@ -969,6 +994,7 @@ int Rewriter::_allocate(RewriterVar* result, int n) { ...@@ -969,6 +994,7 @@ int Rewriter::_allocate(RewriterVar* result, int n) {
} }
assertConsistent(); assertConsistent();
result->releaseIfNoUses();
return a; return a;
} }
} else { } else {
...@@ -1000,6 +1026,7 @@ void Rewriter::_allocateAndCopy(RewriterVar* result, RewriterVar* array_ptr, int ...@@ -1000,6 +1026,7 @@ void Rewriter::_allocateAndCopy(RewriterVar* result, RewriterVar* array_ptr, int
array_ptr->bumpUse(); array_ptr->bumpUse();
result->releaseIfNoUses();
assertConsistent(); assertConsistent();
} }
...@@ -1038,6 +1065,7 @@ void Rewriter::_allocateAndCopyPlus1(RewriterVar* result, RewriterVar* first_ele ...@@ -1038,6 +1065,7 @@ void Rewriter::_allocateAndCopyPlus1(RewriterVar* result, RewriterVar* first_ele
first_elem->bumpUse(); first_elem->bumpUse();
result->releaseIfNoUses();
assertConsistent(); assertConsistent();
} }
...@@ -1092,6 +1120,8 @@ void Rewriter::spillRegister(assembler::Register reg, Location preserve) { ...@@ -1092,6 +1120,8 @@ void Rewriter::spillRegister(assembler::Register reg, Location preserve) {
} }
void Rewriter::spillRegister(assembler::XMMRegister reg) { void Rewriter::spillRegister(assembler::XMMRegister reg) {
assertPhaseEmitting();
if (!done_guarding) { if (!done_guarding) {
for (int i = 0; i < args.size(); i++) { for (int i = 0; i < args.size(); i++) {
assert(!args[i]->isInLocation(Location(reg))); assert(!args[i]->isInLocation(Location(reg)));
...@@ -1111,6 +1141,8 @@ void Rewriter::spillRegister(assembler::XMMRegister reg) { ...@@ -1111,6 +1141,8 @@ void Rewriter::spillRegister(assembler::XMMRegister reg) {
} }
assembler::Register Rewriter::allocReg(Location dest, Location otherThan) { assembler::Register Rewriter::allocReg(Location dest, Location otherThan) {
assertPhaseEmitting();
if (dest.type == Location::AnyReg) { if (dest.type == Location::AnyReg) {
int best = -1; int best = -1;
bool found = false; bool found = false;
...@@ -1124,9 +1156,9 @@ assembler::Register Rewriter::allocReg(Location dest, Location otherThan) { ...@@ -1124,9 +1156,9 @@ assembler::Register Rewriter::allocReg(Location dest, Location otherThan) {
if (!done_guarding && var->is_arg && var->arg_loc == Location(reg)) { if (!done_guarding && var->is_arg && var->arg_loc == Location(reg)) {
continue; continue;
} }
if (var->next_use > best) { if (var->uses[var->next_use] > best) {
found = true; found = true;
best = var->next_use; best = var->uses[var->next_use];
best_reg = reg; best_reg = reg;
} }
} }
...@@ -1152,6 +1184,8 @@ assembler::Register Rewriter::allocReg(Location dest, Location otherThan) { ...@@ -1152,6 +1184,8 @@ assembler::Register Rewriter::allocReg(Location dest, Location otherThan) {
} }
assembler::XMMRegister Rewriter::allocXMMReg(Location dest, Location otherThan) { assembler::XMMRegister Rewriter::allocXMMReg(Location dest, Location otherThan) {
assertPhaseEmitting();
if (dest.type == Location::AnyReg) { if (dest.type == Location::AnyReg) {
for (assembler::XMMRegister reg : allocatable_xmm_regs) { for (assembler::XMMRegister reg : allocatable_xmm_regs) {
if (Location(reg) != otherThan && vars_by_location.count(reg) == 0) { if (Location(reg) != otherThan && vars_by_location.count(reg) == 0) {
...@@ -1205,12 +1239,16 @@ void Rewriter::removeLocationFromVar(RewriterVar* var, Location l) { ...@@ -1205,12 +1239,16 @@ void Rewriter::removeLocationFromVar(RewriterVar* var, Location l) {
} }
RewriterVar* Rewriter::createNewVar() { RewriterVar* Rewriter::createNewVar() {
assertPhaseCollecting();
RewriterVar* var = new RewriterVar(this); RewriterVar* var = new RewriterVar(this);
vars.push_back(var); vars.push_back(var);
return var; return var;
} }
assembler::Register RewriterVar::initializeInReg(Location l) { assembler::Register RewriterVar::initializeInReg(Location l) {
rewriter->assertPhaseEmitting();
// TODO um should we check this in more places, or what? // TODO um should we check this in more places, or what?
// The thing is: if we aren't done guarding, and the register we want to use // The thing is: if we aren't done guarding, and the register we want to use
// is taken by an arg, we can't spill it, so we shouldn't ask to alloc it. // is taken by an arg, we can't spill it, so we shouldn't ask to alloc it.
...@@ -1234,6 +1272,8 @@ assembler::Register RewriterVar::initializeInReg(Location l) { ...@@ -1234,6 +1272,8 @@ assembler::Register RewriterVar::initializeInReg(Location l) {
} }
assembler::XMMRegister RewriterVar::initializeInXMMReg(Location l) { assembler::XMMRegister RewriterVar::initializeInXMMReg(Location l) {
rewriter->assertPhaseEmitting();
assembler::XMMRegister reg = rewriter->allocXMMReg(l); assembler::XMMRegister reg = rewriter->allocXMMReg(l);
l = Location(reg); l = Location(reg);
...@@ -1255,6 +1295,8 @@ TypeRecorder* Rewriter::getTypeRecorder() { ...@@ -1255,6 +1295,8 @@ TypeRecorder* Rewriter::getTypeRecorder() {
Rewriter::Rewriter(ICSlotRewrite* rewrite, int num_args, const std::vector<int>& live_outs) Rewriter::Rewriter(ICSlotRewrite* rewrite, int num_args, const std::vector<int>& live_outs)
: rewrite(rewrite), assembler(rewrite->getAssembler()), return_location(rewrite->returnRegister()), : rewrite(rewrite), assembler(rewrite->getAssembler()), return_location(rewrite->returnRegister()),
added_changing_action(false), last_guard_action(-1), done_guarding(false), ndecisions(0), decision_path(1) { added_changing_action(false), last_guard_action(-1), done_guarding(false), ndecisions(0), decision_path(1) {
initPhaseCollecting();
#ifndef NDEBUG #ifndef NDEBUG
start_vars = RewriterVar::nvars; start_vars = RewriterVar::nvars;
#endif #endif
......
...@@ -229,9 +229,18 @@ private: ...@@ -229,9 +229,18 @@ private:
std::unordered_set<Location> locations; std::unordered_set<Location> locations;
bool isInLocation(Location l); bool isInLocation(Location l);
// uses is a vector of the indices into the Rewriter::actions vector
// indicated the actions that use this variable.
// During the assembly-emitting phase, next_use is used to keep track of the next
// use (so next_use is an index into uses).
// Every action that uses a variable should call bumpUse on it when it's "done" with it
// Here "done" means that it would be okay to release all of the var's locations and
// thus allocate new variables in that same location. To be safe, you can always just
// only call bumpUse at the end, but in some cases it may be possible earlier.
std::vector<int> uses; std::vector<int> uses;
int next_use; int next_use;
void bumpUse(); void bumpUse();
void releaseIfNoUses();
bool isDoneUsing() { return next_use == uses.size(); } bool isDoneUsing() { return next_use == uses.size(); }
// Indicates if this variable is an arg, and if so, what location the arg is from. // Indicates if this variable is an arg, and if so, what location the arg is from.
...@@ -299,6 +308,17 @@ private: ...@@ -299,6 +308,17 @@ private:
bool finished; // committed or aborted bool finished; // committed or aborted
#ifndef NDEBUG #ifndef NDEBUG
int start_vars; int start_vars;
bool phase_emitting;
void initPhaseCollecting() { phase_emitting = false; }
void initPhaseEmitting() { phase_emitting = true; }
void assertPhaseCollecting() { assert(!phase_emitting && "you should only call this in the collecting phase"); }
void assertPhaseEmitting() { assert(phase_emitting && "you should only call this in the assembly-emitting phase"); }
#else
void initPhaseCollecting() {}
void initPhaseEmitting() {}
void assertPhaseCollecting() {}
void assertPhaseEmitting() {}
#endif #endif
std::vector<int> live_out_regs; std::vector<int> live_out_regs;
...@@ -311,6 +331,7 @@ private: ...@@ -311,6 +331,7 @@ private:
std::vector<RewriterAction> actions; std::vector<RewriterAction> actions;
void addAction(const std::function<void()>& action, std::vector<RewriterVar*> const& vars, ActionType type) { void addAction(const std::function<void()>& action, std::vector<RewriterVar*> const& vars, ActionType type) {
assertPhaseCollecting();
for (RewriterVar* var : vars) { for (RewriterVar* var : vars) {
assert(var != NULL); assert(var != NULL);
var->uses.push_back(actions.size()); var->uses.push_back(actions.size());
...@@ -327,7 +348,10 @@ private: ...@@ -327,7 +348,10 @@ private:
int last_guard_action; int last_guard_action;
bool done_guarding; bool done_guarding;
bool isDoneGuarding() { return done_guarding; } bool isDoneGuarding() {
assertPhaseEmitting();
return done_guarding;
}
// Allocates a register. dest must be of type Register or AnyReg // Allocates a register. dest must be of type Register or AnyReg
// If otherThan is a register, guaranteed to not use that register. // If otherThan is a register, guaranteed to not use that register.
......
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