Skip to content

Commit

Permalink
emit switches in relooper
Browse files Browse the repository at this point in the history
  • Loading branch information
kripken committed Aug 22, 2013
1 parent ab506b3 commit d705c37
Show file tree
Hide file tree
Showing 35 changed files with 2,234 additions and 879 deletions.
13 changes: 10 additions & 3 deletions src/jsifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,13 @@ function JSify(data, functionsOnly, givenFunctions) {
var label = block.labels[i];
var content = getLabelLines(label, '', true);
//printErr(func.ident + ' : ' + label.ident + ' : ' + content + '\n');
blockMap[label.ident] = Relooper.addBlock(content);
var last = label.lines[label.lines.length-1];
if (last.intertype != 'switch') {
blockMap[label.ident] = Relooper.addBlock(content);
} else {
assert(last.signedIdent);
blockMap[label.ident] = Relooper.addBlock(content, last.signedIdent);
}
}
// add branchings
function relevant(x) { return x && x.length > 2 ? x : 0 } // ignores ';' which valueJS and label*JS can be if empty
Expand Down Expand Up @@ -1125,7 +1131,7 @@ function JSify(data, functionsOnly, givenFunctions) {
}
});
makeFuncLineActor('switch', function(item) {
var useIfs = RELOOP || item.switchLabels.length < 1024; // with a huge number of cases, if-else which looks nested to js parsers can cause problems
var useIfs = false;
var phiSets = calcPhiSets(item);
// Consolidate checks that go to the same label. This is important because it makes the relooper simpler and faster.
var targetLabels = {}; // for each target label, the list of values going to it
Expand All @@ -1139,7 +1145,8 @@ function JSify(data, functionsOnly, givenFunctions) {
});
var ret = '';
var first = true;
var signedIdent = makeSignOp(item.ident, item.type, 're'); // we need to standardize for purpose of comparison
signedIdent = makeSignOp(item.ident, item.type, 're'); // we need to standardize for purpose of comparison
if (!useIfs) item.signedIdent = signedIdent;
if (RELOOP) {
item.groupedLabels = [];
}
Expand Down
84 changes: 62 additions & 22 deletions src/relooper/Relooper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,14 @@ void Branch::Render(Block *Target, bool SetLabel) {

int Block::IdCounter = 1; // 0 is reserved for clearings

Block::Block(const char *CodeInit) : Parent(NULL), Id(Block::IdCounter++), IsCheckedMultipleEntry(false) {
Block::Block(const char *CodeInit, const char *BranchVarInit) : Parent(NULL), Id(Block::IdCounter++), IsCheckedMultipleEntry(false) {
Code = strdup(CodeInit);
BranchVar = BranchVarInit ? strdup(BranchVarInit) : NULL;
}

Block::~Block() {
if (Code) free((void*)Code);
if (BranchVar) free((void*)BranchVar);
for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin(); iter != ProcessedBranchesOut.end(); iter++) {
delete iter->second;
}
Expand Down Expand Up @@ -189,8 +191,16 @@ void Block::Render(bool InLoop) {
}
assert(DefaultTarget); // Since each block *must* branch somewhere, this must be set

if (ProcessedBranchesOut.size() > 2) assert(BranchVar); // must have a branch variable if multiple conditions

bool useSwitch = BranchVar != NULL;

if (useSwitch) {
PrintIndented("switch (%s) {\n", BranchVar);
}

ministring RemainingConditions;
bool First = true;
bool First = !useSwitch; // when using a switch, there is no special first
for (BlockBranchMap::iterator iter = ProcessedBranchesOut.begin();; iter++) {
Block *Target;
Branch *Details;
Expand All @@ -208,26 +218,39 @@ void Block::Render(bool InLoop) {
bool HasContent = SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code;
if (iter != ProcessedBranchesOut.end()) {
// If there is nothing to show in this branch, omit the condition
if (HasContent) {
PrintIndented("%sif (%s) {\n", First ? "" : "} else ", Details->Condition);
First = false;
if (useSwitch) {
PrintIndented("%s {\n", Details->Condition);
} else {
if (RemainingConditions.size() > 0) RemainingConditions += " && ";
RemainingConditions += "!(";
RemainingConditions += Details->Condition;
RemainingConditions += ")";
if (HasContent) {
PrintIndented("%sif (%s) {\n", First ? "" : "} else ", Details->Condition);
First = false;
} else {
if (RemainingConditions.size() > 0) RemainingConditions += " && ";
RemainingConditions += "!(";
if (BranchVar) {
RemainingConditions += BranchVar;
RemainingConditions += " == ";
}
RemainingConditions += Details->Condition;
RemainingConditions += ")";
}
}
} else {
if (HasContent) {
if (RemainingConditions.size() > 0) {
if (First) {
PrintIndented("if (%s) {\n", RemainingConditions.c_str());
First = false;
} else {
PrintIndented("} else if (%s) {\n", RemainingConditions.c_str());
// this is the default
if (useSwitch) {
PrintIndented("default: {\n");
} else {
if (HasContent) {
if (RemainingConditions.size() > 0) {
if (First) {
PrintIndented("if (%s) {\n", RemainingConditions.c_str());
First = false;
} else {
PrintIndented("} else if (%s) {\n", RemainingConditions.c_str());
}
} else if (!First) {
PrintIndented("} else {\n");
}
} else if (!First) {
PrintIndented("} else {\n");
}
}
}
Expand All @@ -236,7 +259,13 @@ void Block::Render(bool InLoop) {
if (HasFusedContent) {
Fused->InnerMap.find(Target)->second->Render(InLoop);
}
if (useSwitch && iter != ProcessedBranchesOut.end()) {
PrintIndented("break;\n");
}
if (!First) Indenter::Unindent();
if (useSwitch) {
PrintIndented("}\n");
}
if (iter == ProcessedBranchesOut.end()) break;
}
if (!First) PrintIndented("}\n");
Expand Down Expand Up @@ -392,7 +421,7 @@ void Relooper::Calculate(Block *Entry) {
PrintDebug("Splitting block %d\n", Original->Id);
for (BlockSet::iterator iter = Original->BranchesIn.begin(); iter != Original->BranchesIn.end(); iter++) {
Block *Prior = *iter;
Block *Split = new Block(Original->Code);
Block *Split = new Block(Original->Code, Original->BranchVar);
Parent->Blocks.push_back(Split);
PrintDebug(" to %d\n", Split->Id);
Split->BranchesIn.insert(Prior);
Expand Down Expand Up @@ -975,6 +1004,8 @@ void Relooper::Calculate(Block *Entry) {
Root = Next;
Next = NULL;
SHAPE_SWITCH(Root, {
if (Simple->Inner->BranchVar) LastLoop = NULL; // a switch clears out the loop (TODO: only for breaks, not continue)

// If there is a next block, we already know at Simple creation time to make direct branches,
// and we can do nothing more. If there is no next however, then Natural is where we will
// go to by doing nothing, so we can potentially optimize some branches to direct.
Expand Down Expand Up @@ -1028,6 +1059,11 @@ void Relooper::Calculate(Block *Entry) {
// If we are fusing a Multiple with a loop into this Simple, then visit it now
if (Fused && Fused->NeedLoop) {
LoopStack.push(Fused);
}
if (Simple->Inner->BranchVar) {
LoopStack.push(NULL); // a switch means breaks are now useless, push a dummy
}
if (Fused) {
RECURSE_Multiple(Fused, FindLabeledLoops);
}
for (BlockBranchMap::iterator iter = Simple->Inner->ProcessedBranchesOut.begin(); iter != Simple->Inner->ProcessedBranchesOut.end(); iter++) {
Expand All @@ -1038,14 +1074,18 @@ void Relooper::Calculate(Block *Entry) {
if (Details->Ancestor != LoopStack.top() && Details->Labeled) {
LabeledShape *Labeled = Shape::IsLabeled(Details->Ancestor);
Labeled->Labeled = true;
Details->Labeled = true;
} else {
Details->Labeled = false;
}
}
}
if (Simple->Inner->BranchVar) {
LoopStack.pop();
}
if (Fused && Fused->NeedLoop) {
LoopStack.pop();
}
if (Fused) {
Next = Fused->Next;
} else {
Next = Root->Next;
Expand Down Expand Up @@ -1173,8 +1213,8 @@ void rl_set_asm_js_mode(int on) {
Relooper::SetAsmJSMode(on);
}

void *rl_new_block(const char *text) {
Block *ret = new Block(text);
void *rl_new_block(const char *text, const char *branch_var) {
Block *ret = new Block(text, branch_var);
#if DEBUG
printf(" void *b%d = rl_new_block(\"// code %d\");\n", ret->Id, ret->Id);
__blockDebugMap__[ret] = ret->Id;
Expand Down
5 changes: 3 additions & 2 deletions src/relooper/Relooper.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,10 @@ struct Block {
Shape *Parent; // The shape we are directly inside
int Id; // A unique identifier
const char *Code; // The string representation of the code in this block. Owning pointer (we copy the input)
const char *BranchVar; // If we have more than one branch out, the variable whose value determines where we go
bool IsCheckedMultipleEntry; // If true, we are a multiple entry, so reaching us requires setting the label variable

Block(const char *CodeInit);
Block(const char *CodeInit, const char *BranchVarInit);
~Block();

void AddBranchTo(Block *Target, const char *Condition, const char *Code=NULL);
Expand Down Expand Up @@ -235,7 +236,7 @@ extern "C" {
RELOOPERDLL_API void rl_set_output_buffer(char *buffer, int size);
RELOOPERDLL_API void rl_make_output_buffer(int size);
RELOOPERDLL_API void rl_set_asm_js_mode(int on);
RELOOPERDLL_API void *rl_new_block(const char *text);
RELOOPERDLL_API void *rl_new_block(const char *text, const char *branch_var);
RELOOPERDLL_API void rl_delete_block(void *block);
RELOOPERDLL_API void rl_block_add_branch_to(void *from, void *to, const char *condition, const char *code);
RELOOPERDLL_API void *rl_new_relooper();
Expand Down
11 changes: 9 additions & 2 deletions src/relooper/emscripten/glue.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@
var TBUFFER_SIZE = 10*1024*1024;
var tbuffer = _malloc(TBUFFER_SIZE);

var VBUFFER_SIZE = 256;
var vbuffer = _malloc(VBUFFER_SIZE);

var RelooperGlue = {};
RelooperGlue['init'] = function() {
this.r = _rl_new_relooper();
},
RelooperGlue['addBlock'] = function(text) {
RelooperGlue['addBlock'] = function(text, branchVar) {
assert(this.r);
assert(text.length+1 < TBUFFER_SIZE);
writeStringToMemory(text, tbuffer);
var b = _rl_new_block(tbuffer);
if (branchVar) {
assert(branchVar.length+1 < VBUFFER_SIZE);
writeStringToMemory(branchVar, vbuffer);
}
var b = _rl_new_block(tbuffer, branchVar ? vbuffer : 0);
_rl_relooper_add_block(this.r, b);
return b;
};
Expand Down
29 changes: 19 additions & 10 deletions src/relooper/fuzzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@
pass

# parts
entry = '''print('entry'); var label; var state; var decisions = %s; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }''' % str(decisions)
entry = '''print('entry'); var label; var state; var modded; var decisions = %s; var index = 0; function check() { if (index == decisions.length) throw 'HALT'; return decisions[index++] }''' % str(decisions)

slow = entry + '\n'
for i in range(len(branches[0])):
if i > 0: slow += 'else '
b = branches[0]
slow += 'if (state %% %d == %d) { label = %d; }\n' % (len(b)+1, i, b[i]) # TODO: split range 1-n into these options
slow += 'if (modded == %d) { label = %d; }\n' % (i, b[i]) # TODO: split range 1-n into these options
if len(branches[0]): slow += 'else '
slow += 'label = %d;\n' % defaults[0]

Expand All @@ -51,26 +51,35 @@
'''

for i in range(1, num):
slow += ' case %d: print(%d); state = check(); \n' % (i, i)
slow += ' case %d: print(%d); state = check(); modded = state %% %d\n' % (i, i, len(branches[i])+1)
b = branches[i]
for j in range(len(b)):
slow += ' if (state %% %d == %d) { label = %d; break }\n' % (len(b)+1, j, b[j]) # TODO: split range 1-n into these options
slow += ' if (modded == %d) { label = %d; break }\n' % (j, b[j]) # TODO: split range 1-n into these options
slow += ' label = %d; break\n' % defaults[i]

branch_vars = []
for i in range(num):
branch_var = '"modded"' if len(branches[i]) > 0 and not (len(branches[i]) == 1 and random.random() < 0.5) else 'NULL'
branch_vars.append(branch_var)

if i == 0:
fast += '''
Block *b%d = new Block("%s");
''' % (i, entry)
Block *b%d = new Block("%s", %s);
''' % (i, entry, branch_var)
else:
fast += ''' Block *b%d = new Block("print(%d); state = check();%s");
''' % (i, i, '// ' + ('.' * int(random.expovariate(0.5/num))))
fast += ''' Block *b%d = new Block("print(%d); state = check(); modded = state %% %d; %s", %s);
''' % (i, i, len(branches[i])+1, '// ' + ('.' * int(random.expovariate(0.5/num))), branch_var)

for i in range(num):
branch_var = branch_vars[i]
b = branches[i]
for j in range(len(b)):
fast += ''' b%d->AddBranchTo(b%d, "state %% %d == %d");
''' % (i, b[j], len(b)+1, j)
if branch_var == 'NULL':
fast += ''' b%d->AddBranchTo(b%d, "modded == %d");
''' % (i, b[j], j)
else:
fast += ''' b%d->AddBranchTo(b%d, "case %d:");
''' % (i, b[j], j)
fast += ''' b%d->AddBranchTo(b%d, NULL);
''' % (i, defaults[i])

Expand Down
Loading

0 comments on commit d705c37

Please sign in to comment.