initial work to port registerize

This commit is contained in:
Alon Zakai 2014-11-12 14:47:33 -08:00
Родитель 68b550a8d1
Коммит f418b5f550
2 изменённых файлов: 291 добавлений и 4 удалений

Просмотреть файл

@ -121,6 +121,10 @@ struct IString {
return str[x];
}
bool operator!() { // no string, or empty string
return !str || str[0] == 0;
}
const char *c_str() const { return str; }
bool isNull() { return str == nullptr; }
@ -467,6 +471,16 @@ struct Value {
return -1;
}
Ref map(std::function<Ref (Ref node)> func) {
assert(isArray());
Ref ret = arena.alloc();
ret->setArray();
for (unsigned i = 0; i < arr->size(); i++) {
ret->push_back(func((*arr)[i]));
}
return ret;
}
/*
void forEach(std::function<void (Ref)> func) {
for (unsigned i = 0; i < arr->size(); i++) {

Просмотреть файл

@ -25,6 +25,7 @@ IString TOPLEVEL("toplevel"),
IF("if"),
WHILE("while"),
DO("do"),
FOR("for"),
SEQ("seq"),
SUB("sub"),
CALL("call"),
@ -270,7 +271,7 @@ enum AsmType {
ASM_FLOAT = 2,
ASM_FLOAT32X4 = 3,
ASM_INT32X4 = 4,
ASM_NONE = 5
ASM_NONE = 5 // number of types
};
// forward decls
@ -312,6 +313,9 @@ struct AsmData {
bool isLocal(const IString& name) {
return locals.count(name) > 0;
}
bool isParam(const IString& name) {
return isLocal(name) && locals[name].param;
}
bool isVar(const IString& name) {
return isLocal(name) && !locals[name].param;
}
@ -415,7 +419,7 @@ struct AsmData {
stats[next] = make1(VAR, varDefs);
}
/*
if (inlines.length > 0) {
if (inlines->size() > 0) {
var i = 0;
traverse(func, function(node, type) {
if (type == CALL && node[1][0] == NAME && node[1][1] == 'inlinejs') {
@ -438,6 +442,10 @@ struct AsmData {
//printErr('denormalized \n\n' + astToSrc(func) + '\n\n');
}
void addParam(IString name, AsmType type) {
locals[name] = { type, true };
params.push_back(name);
}
void addVar(IString name, AsmType type) {
locals[name] = { type, false };
vars.push_back(name);
@ -835,6 +843,24 @@ void removeAllEmptySubNodes(Ref ast) {
});
}
Ref unVarify(Ref vars) { // transform var x=1, y=2 etc. into (x=1, y=2), i.e., the same assigns, but without a var definition
Ref ret;
ret->push_back(makeString(STAT));
if (vars->size() == 1) {
ret->push_back(make3(ASSIGN, &(arena.alloc())->setBool(true), makeName(vars[0][0]->getIString()), vars[0][1]));
} else {
ret->push_back(makeArray());
Ref curr = ret[1];
for (int i = 0; i < vars->size()-1; i++) {
curr->push_back(makeString(SEQ));
curr->push_back(make3(ASSIGN, &(arena.alloc())->setBool(true), makeName(vars[i][0]->getIString()), vars[i][1]));
if (i != vars->size()-2) curr = curr[2] = makeArray();
}
curr[2] = make3(ASSIGN, &(arena.alloc())->setBool(true), makeName(vars[vars->size()-1][0]->getIString()), vars[vars->size()-1][1]);
}
return ret;
}
// Calculations
int measureCost(Ref ast) {
@ -898,6 +924,8 @@ StringSet USEFUL_BINARY_OPS("<< >> | & ^"),
COERCION_REQUIRING_BINARIES("* / %"); // binary ops that in asm must be coerced
StringSet ASSOCIATIVE_BINARIES("+ * | & ^"),
CONTROL_FLOW("do while for if switch"),
LOOP("do while for"),
NAME_OR_NUM("name num");
bool isFunctionTable(const char *name) {
@ -970,6 +998,11 @@ public:
HASES
};
class StringTypeMap : public std::unordered_map<IString, AsmType> {
public:
HASES
};
void eliminate(Ref ast, bool memSafe=false) {
// Find variables that have a single use, and if they can be eliminated, do so
traverseFunctions(ast, [&memSafe](Ref func) {
@ -1437,9 +1470,9 @@ void eliminate(Ref ast, bool memSafe=false) {
traversePrePost(func, [&](Ref node) {
// pre
Ref type = node[0];
/*if (type === VAR) {
/*if (type == VAR) {
node[1] = node[1].filter(function(pair) { return !varsToRemove[pair[0]] });
if (node[1].length === 0) {
if (node[1]->size() == 0) {
// wipe out an empty |var;|
node[0] = 'toplevel';
node[1] = [];
@ -2278,6 +2311,245 @@ void optimizeFrounds(Ref ast) {
});
}
// Very simple 'registerization', coalescing of variables into a smaller number.
void registerize(Ref ast) {
traverseFunctions(ast, [](Ref fun) {
AsmData asmData(fun);
// Add parameters as a first (fake) var (with assignment), so they get taken into consideration
// note: params are special, they can never share a register between them (see later)
if (!!fun[2] && fun[2]->size()) {
Ref assign = makeNum(0);
fun[3]->insert(0, make1(VAR, fun[2]->map([&assign](Ref param) {
return &(makeArray()->push_back(param).push_back(assign));
})));
}
// Replace all var definitions with assignments; we will add var definitions at the top after we registerize
StringSet allVars;
traversePre(fun, [&](Ref node) {
Ref type = node[0];
if (type == VAR) {
Ref vars = node[1]; // XXX.filter(function(varr) { return varr[1] });
if (vars->size() > 0) {
safeCopy(node, unVarify(vars));
} else {
safeCopy(node, makeEmpty());
}
} else if (type == NAME) {
allVars.insert(node[1]->getIString());
}
});
removeAllEmptySubNodes(fun); // vacuum?
StringTypeMap regTypes; // reg name -> type
auto getNewRegName = [&](int num, IString name) {
std::string str;
AsmType type = asmData.getType(name);
switch (type) {
case ASM_INT: str = "i"; break;
case ASM_DOUBLE: str = "d"; break;
case ASM_FLOAT: str = "f"; break;
case ASM_FLOAT32X4: str = "F4"; break;
case ASM_INT32X4: str = "I4"; break;
case ASM_NONE: str = "Z"; break;
default: assert(0); // type doesn't have a name yet
}
str += num;
IString ret(strdupe(str.c_str())); // likely interns a new string; leaks the dupe if not
regTypes[ret] = type;
assert(!allVars.has(ret)); // register must not shadow non-local name
return ret;
};
// Find the # of uses of each variable.
// While doing so, check if all a variable's uses are dominated in a simple
// way by a simple assign, if so, then we can assign its register to it
// just for its definition to its last use, and not to the entire toplevel loop,
// we call such variables "optimizable"
StringIntMap varUses;
int level = 1;
std::unordered_map<int, StringSet> levelDominations; // level => set of dominated variables XXX vector?
StringIntMap varLevels;
StringSet possibles;
StringSet unoptimizables;
auto purgeLevel = [&]() {
// Invalidate all dominating on this level, further users make it unoptimizable
for (auto name : levelDominations[level]) {
varLevels[name] = 0;
}
levelDominations[level].clear();
level--;
};
std::function<bool (Ref node)> possibilifier = [&](Ref node) {
Ref type = node[0];
if (type == NAME) {
IString name = node[1]->getIString();
if (asmData.isLocal(name)) {
varUses[name]++;
if (possibles.has(name) && !varLevels[name]) unoptimizables.insert(name); // used outside of simple domination
}
} else if (type == ASSIGN && node[1]->isBool(true)) {
if (!!node[2] && node[2][0] == NAME) {
IString name = node[2][1]->getIString();
// if local and not yet used, this might be optimizable if we dominate
// all other uses
if (asmData.isLocal(name) && !varUses[name] && !varLevels[name]) {
possibles.insert(name);
varLevels[name] = level;
levelDominations[level].insert(name);
}
}
} else if (CONTROL_FLOW.has(type)) {
// recurse children, in the context of a loop
if (type == WHILE || type == DO) {
traversePrePostConditional(node[1], possibilifier, [](Ref node){});
level++;
traversePrePostConditional(node[2], possibilifier, [](Ref node){});
purgeLevel();
} else if (type == FOR) {
traversePrePostConditional(node[1], possibilifier, [](Ref node){});
for (int i = 2; i <= 4; i++) {
level++;
traversePrePostConditional(node[i], possibilifier, [](Ref node){});
purgeLevel();
}
} else if (type == IF) {
traversePrePostConditional(node[1], possibilifier, [](Ref node){});
level++;
traversePrePostConditional(node[2], possibilifier, [](Ref node){});
purgeLevel();
if (!!node[3]) {
level++;
traversePrePostConditional(node[3], possibilifier, [](Ref node){});
purgeLevel();
}
} else if (type == SWITCH) {
traversePrePostConditional(node[1], possibilifier, [](Ref node){});
Ref cases = node[2];
for (int i = 0; i < cases->size(); i++) {
level++;
traversePrePostConditional(cases[i][1], possibilifier, [](Ref node){});
purgeLevel();
}
} else assert(0);;
return false; // prevent recursion into children, which we already did
}
return true;
};
traversePrePostConditional(fun, possibilifier, [](Ref node){});
StringSet optimizables;
for (auto possible : possibles) {
if (!unoptimizables.has(possible)) optimizables.insert(possible);
}
// Go through the function's code, assigning 'registers'.
// The only tricky bit is to keep variables locked on a register through loops,
// since they can potentially be returned to. Optimizable variables lock onto
// loops that they enter, unoptimizable variables lock in a conservative way
// into the topmost loop.
// Note that we cannot lock onto a variable in a loop if it was used and free'd
// before! (then they could overwrite us in the early part of the loop). For now
// we just use a fresh register to make sure we avoid this, but it could be
// optimized to check for safe registers (free, and not used in this loop level).
StringStringMap varRegs; // maps variables to the register they will use all their life
typedef std::vector<IString> StringVec;
std::vector<StringVec> freeRegsClasses;
freeRegsClasses.resize(ASM_NONE);
int nextReg = 1;
StringVec fullNames;
fullNames.push_back(EMPTY); // names start at 1
std::vector<StringVec> loopRegs; // for each loop nesting level, the list of bound variables
int loops = 0; // 0 is toplevel, 1 is first loop, etc
StringSet activeOptimizables;
StringIntMap optimizableLoops;
StringSet paramRegs; // true if the register is used by a parameter (and so needs no def at start of function; also cannot
// be shared with another param, each needs its own)
auto decUse = [&](IString name) {
if (!varUses[name]) return false; // no uses left, or not a relevant variable
if (optimizables.has(name)) activeOptimizables.insert(name);
IString reg = varRegs[name];
assert(asmData.isLocal(name));
StringVec& freeRegs = freeRegsClasses[asmData.getType(name)];
if (!reg) {
// acquire register
if (optimizables.has(name) && freeRegs.size() > 0 &&
!(asmData.isParam(name) && paramRegs.has(freeRegs.back()))) { // do not share registers between parameters
reg = freeRegs.back();
freeRegs.pop_back();
} else {
fullNames[nextReg] = reg = getNewRegName(nextReg, name);
if (asmData.isParam(name)) paramRegs.insert(reg);
nextReg++;
}
varRegs[name] = reg;
}
varUses[name]--;
assert(varUses[name] >= 0);
if (varUses[name] == 0) {
if (optimizables.has(name)) activeOptimizables.erase(name);
// If we are not in a loop, or we are optimizable and not bound to a loop
// (we might have been in one but left it), we can free the register now.
if (loops == 0 || (optimizables.has(name) && !optimizableLoops.has(name))) {
// free register
freeRegs.push_back(reg);
} else {
// when the relevant loop is exited, we will free the register
int relevantLoop = optimizables.has(name) ? (optimizableLoops[name] ? optimizableLoops[name] : 1) : 1;
if (loopRegs.size() <= relevantLoop) loopRegs.resize(relevantLoop);
loopRegs[relevantLoop].push_back(reg);
}
}
return true;
};
traversePrePost(fun, [&](Ref node) { // XXX we rely on traversal order being the same as execution order here
Ref type = node[0];
if (type == NAME) {
IString name = node[1]->getIString();
if (decUse(name)) {
node[1]->setString(varRegs[name]);
}
} else if (LOOP.has(type)) {
loops++;
// Active optimizables lock onto this loop, if not locked onto one that encloses this one
for (auto name : activeOptimizables) {
if (!optimizableLoops[name]) {
optimizableLoops[name] = loops;
}
}
}
}, [&](Ref node) {
Ref type = node[0];
if (LOOP.has(type)) {
// Free registers that were locked to this loop
if (loopRegs[loops].size() > 0) {
for (auto loopReg : loopRegs[loops]) {
freeRegsClasses[regTypes[loopReg]].push_back(loopReg);
}
loopRegs[loops].clear();
}
loops--;
}
});
if (!!fun[2] && fun[2]->size()) {
fun[2]->setSize(0); // clear params, we will fill with registers
fun[3]->splice(0, 1); // remove fake initial var
}
asmData.locals.clear();
asmData.params.clear();
asmData.vars.clear();
for (int i = 1; i < nextReg; i++) {
IString reg = fullNames[i];
AsmType type = regTypes[reg];
if (!paramRegs.has(reg)) {
asmData.addVar(reg, type);
} else {
asmData.addParam(reg, type);
fun[2]->push_back(makeString(reg));
}
}
asmData.denormalize();
});
}
//==================
// Main
//==================
@ -2318,6 +2590,7 @@ int main(int argc, char **argv) {
else if (str == "simplifyExpressions") simplifyExpressions(doc);
else if (str == "optimizeFrounds") optimizeFrounds(doc);
else if (str == "simplifyIfs") simplifyIfs(doc);
else if (str == "registerize") registerize(doc);
else {
fprintf(stderr, "unrecognized argument: %s\n", str.c_str());
assert(0);