aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyzer.js12
-rw-r--r--src/compiler.js2
-rw-r--r--src/jsifier.js16
-rw-r--r--src/library.js3
-rw-r--r--src/library_gl.js76
-rw-r--r--src/library_sdl.js35
-rw-r--r--src/parseTools.js35
-rw-r--r--src/preamble.js15
-rw-r--r--src/relooper/Relooper.cpp50
-rw-r--r--src/relooper/Relooper.h34
-rw-r--r--src/relooper/fuzzer.py6
-rw-r--r--src/relooper/test.cpp28
-rw-r--r--src/relooper/test.txt377
-rw-r--r--src/relooper/test2.txt40
-rw-r--r--src/relooper/test3.txt82
-rw-r--r--src/relooper/test4.txt52
-rw-r--r--src/relooper/test5.txt104
-rw-r--r--src/relooper/test6.txt38
-rw-r--r--src/relooper/test_dead.txt2
-rw-r--r--src/relooper/test_debug.txt62
-rw-r--r--src/relooper/test_fuzz1.txt100
-rw-r--r--src/relooper/test_fuzz2.txt42
-rw-r--r--src/relooper/test_fuzz3.txt36
-rw-r--r--src/relooper/test_fuzz4.txt56
-rw-r--r--src/relooper/test_fuzz5.txt122
-rw-r--r--src/relooper/test_fuzz6.txt358
-rw-r--r--src/relooper/test_inf.txt1606
-rwxr-xr-xsrc/relooper/testit.sh30
-rw-r--r--src/runtime.js2
29 files changed, 1816 insertions, 1605 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 17582ea3..e8ca6cf6 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -1570,7 +1570,17 @@ function analyzer(data, sidePass) {
for (var j = 0; j < label.lines.length; j++) {
var line = label.lines[j];
if ((line.intertype == 'call' || line.intertype == 'invoke') && line.ident == setjmp) {
- // Add a new label
+ if (line.intertype == 'invoke') {
+ // setjmp cannot trigger unwinding, so just reduce the invoke to a call + branch
+ line.intertype = 'call';
+ label.lines.push({
+ intertype: 'branch',
+ label: line.toLabel,
+ lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this
+ });
+ line.toLabel = line.unwindLabel = -2;
+ }
+ // split this label into up to the setjmp (including), then a new label for the rest. longjmp will reach the rest
var oldLabel = label.ident;
var newLabel = func.labelIdCounter++;
if (!func.setjmpTable) func.setjmpTable = [];
diff --git a/src/compiler.js b/src/compiler.js
index 7d768c3d..e4ce1c88 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -134,7 +134,7 @@ load('settings.js');
var settings_file = arguments_[0];
var ll_file = arguments_[1];
phase = arguments_[2];
-if (phase == 'pre') {
+if (phase == 'pre' || phase == 'glue') {
additionalLibraries = Array.prototype.slice.call(arguments_, 3);
} else {
var forwardedDataFile = arguments_[3];
diff --git a/src/jsifier.js b/src/jsifier.js
index 6b831b04..58dc4653 100644
--- a/src/jsifier.js
+++ b/src/jsifier.js
@@ -6,7 +6,6 @@
// Handy sets
var STRUCT_LIST = set('struct', 'list');
-var UNDERSCORE_OPENPARENS = set('_', '(');
var RELOOP_IGNORED_LASTS = set('return', 'unreachable', 'resume');
var addedLibraryItems = {};
@@ -96,19 +95,6 @@ function JSify(data, functionsOnly, givenFunctions) {
// Functions
- Functions.currExternalFunctions = !mainPass ? givenFunctions.currExternalFunctions : {};
-
- data.functionStubs.forEach(function(func) {
- // Don't overwrite stubs that have more info.
- if (!Functions.currExternalFunctions.hasOwnProperty(func.ident) ||
- !Functions.currExternalFunctions[func.ident].numParams === undefined) {
- Functions.currExternalFunctions[func.ident] = {
- hasVarArgs: func.hasVarArgs,
- numParams: func.params && func.params.length
- };
- }
- });
-
if (phase == 'funcs') { // || phase == 'pre') { // pre has function shells, just to defined implementedFunctions
var MAX_BATCH_FUNC_LINES = 1000;
while (data.unparsedFunctions.length > 0) {
@@ -1824,7 +1810,7 @@ function JSify(data, functionsOnly, givenFunctions) {
print('staticSealed = true; // seal the static portion of memory\n');
print('STACK_MAX = STACK_BASE + ' + TOTAL_STACK + ';\n');
print('DYNAMIC_BASE = DYNAMICTOP = Runtime.alignMemory(STACK_MAX);\n');
- print('assert(DYNAMIC_BASE < TOTAL_MEMORY); // Stack must fit in TOTAL_MEMORY; allocations from here on may enlarge TOTAL_MEMORY\n');
+ print('assert(DYNAMIC_BASE < TOTAL_MEMORY, "TOTAL_MEMORY not big enough for stack");\n');
}
if (asmLibraryFunctions.length > 0) {
diff --git a/src/library.js b/src/library.js
index fc731e01..354e5549 100644
--- a/src/library.js
+++ b/src/library.js
@@ -8858,7 +8858,8 @@ LibraryManager.library = {
}
}
str += ')';
- args = args.callee.caller.arguments;
+ var caller = args.callee.caller;
+ args = caller ? caller.arguments : [];
if (first)
str = '';
return [args, funcname, str];
diff --git a/src/library_gl.js b/src/library_gl.js
index 29f78c8a..075d7cb5 100644
--- a/src/library_gl.js
+++ b/src/library_gl.js
@@ -268,11 +268,27 @@ var LibraryGL = {
return;
case "object":
if (result === null) {
- GL.recordError(0x0500); // GL_INVALID_ENUM
+ // null is a valid result for some (e.g., which buffer is bound - perhaps nothing is bound), but otherwise
+ // can mean an invalid name_, which we need to report as an error
+ switch(name_) {
+ case 0x8894: // ARRAY_BUFFER_BINDING
+ case 0x8B8D: // CURRENT_PROGRAM
+ case 0x8895: // ELEMENT_ARRAY_BUFFER_BINDING
+ case 0x8CA6: // FRAMEBUFFER_BINDING
+ case 0x8CA7: // RENDERBUFFER_BINDING
+ case 0x8069: // TEXTURE_BINDING_2D
+ case 0x8514: { // TEXTURE_BINDING_CUBE_MAP
+ ret = 0;
+ break;
+ }
+ default: {
+ GL.recordError(0x0500); // GL_INVALID_ENUM
#if GL_ASSERTIONS
- Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v(' + name_ + ') and it returns null!');
+ Module.printErr('GL_INVALID_ENUM in glGet' + type + 'v(' + name_ + ') and it returns null!');
#endif
- return;
+ return;
+ }
+ }
} else if (result instanceof Float32Array ||
result instanceof Uint32Array ||
result instanceof Int32Array ||
@@ -547,6 +563,9 @@ var LibraryGL = {
Module.ctx.getExtension('WEBKIT_EXT_texture_filter_anisotropic');
GL.floatExt = Module.ctx.getExtension('OES_texture_float');
+
+ // Extension available from Firefox 26 and Google Chrome 30
+ GL.instancedArraysExt = Module.ctx.getExtension('ANGLE_instanced_arrays');
// These are the 'safe' feature-enabling extensions that don't add any performance impact related to e.g. debugging, and
// should be enabled by default so that client GLES2/GL code will not need to go through extra hoops to get its stuff working.
@@ -1685,7 +1704,7 @@ var LibraryGL = {
glGetFramebufferAttachmentParameteriv__sig: 'viiii',
glGetFramebufferAttachmentParameteriv: function(target, attachment, pname, params) {
var result = Module.ctx.getFramebufferAttachmentParameter(target, attachment, pname);
- {{{ makeSetValue('params', '0', 'params', 'i32') }}};
+ {{{ makeSetValue('params', '0', 'result', 'i32') }}};
},
glIsFramebuffer__sig: 'ii',
@@ -1916,6 +1935,13 @@ var LibraryGL = {
return id;
};
+ function ensurePrecision(source) {
+ if (!/precision +(low|medium|high)p +float *;/.test(source)) {
+ source = 'precision mediump float;\n' + source;
+ }
+ return source;
+ }
+
var glShaderSource = _glShaderSource;
_glShaderSource = function _glShaderSource(shader, count, string, length) {
var source = GL.getSource(shader, count, string, length);
@@ -1989,6 +2015,7 @@ var LibraryGL = {
source = 'varying float v_fogFragCoord; \n' +
source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord');
}
+ source = ensurePrecision(source);
} else { // Fragment shader
for (var i = 0; i < GL.immediate.MAX_TEXTURES; i++) {
var old = source;
@@ -2020,7 +2047,7 @@ var LibraryGL = {
source = 'varying float v_fogFragCoord; \n' +
source.replace(/gl_FogFragCoord/g, 'v_fogFragCoord');
}
- source = 'precision mediump float;\n' + source;
+ source = ensurePrecision(source);
}
#if GL_DEBUG
GL.shaderSources[shader] = source;
@@ -4959,6 +4986,45 @@ var LibraryGL = {
return Module.ctx.getError();
}
},
+
+ // ANGLE_instanced_arrays WebGL extension related functions
+
+ glVertexAttribDivisor__sig: 'vii',
+ glVertexAttribDivisor: function(index, divisor) {
+#if GL_ASSERTIONS
+ assert(GL.instancedArraysExt, 'Must have ANGLE_instanced_arrays extension to use WebGL instancing');
+#endif
+ GL.instancedArraysExt.vertexAttribDivisorANGLE(index, divisor);
+ },
+
+ glDrawArraysInstanced__sig: 'viiii',
+ glDrawArraysInstanced: function(mode, first, count, primcount) {
+#if GL_ASSERTIONS
+ assert(GL.instancedArraysExt, 'Must have ANGLE_instanced_arrays extension to use WebGL instancing');
+#endif
+ GL.instancedArraysExt.drawArraysInstancedANGLE(mode, first, count, primcount);
+ },
+
+ glDrawElementsInstanced__sig: 'viiiii',
+ glDrawElementsInstanced: function(mode, count, type, indices, primcount) {
+#if GL_ASSERTIONS
+ assert(GL.instancedArraysExt, 'Must have ANGLE_instanced_arrays extension to use WebGL instancing');
+#endif
+ GL.instancedArraysExt.drawElementsInstancedANGLE(mode, count, type, indices, primcount);
+ },
+
+ // OpenGL Desktop/ES 2.0 instancing extensions compatibility
+
+ glVertexAttribDivisorNV: 'glVertexAttribDivisor',
+ glDrawArraysInstancedNV: 'glDrawArraysInstanced',
+ glDrawElementsInstancedNV: 'glDrawElementsInstanced',
+ glVertexAttribDivisorEXT: 'glVertexAttribDivisor',
+ glDrawArraysInstancedEXT: 'glDrawArraysInstanced',
+ glDrawElementsInstancedEXT: 'glDrawElementsInstanced',
+ glVertexAttribDivisorARB: 'glVertexAttribDivisor',
+ glDrawArraysInstancedARB: 'glDrawArraysInstanced',
+ glDrawElementsInstancedARB: 'glDrawElementsInstanced',
+
// signatures of simple pass-through functions, see later
glActiveTexture__sig: 'vi',
diff --git a/src/library_sdl.js b/src/library_sdl.js
index 2efc1271..fc38dd1c 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -1059,11 +1059,35 @@ var LibrarySDL = {
var src = buffer >> 2;
var dst = 0;
var isScreen = surf == SDL.screen;
- var data32 = new Uint32Array(data.buffer);
- var num = data32.length;
- while (dst < num) {
- // HEAP32[src++] is an optimization. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
- data32[dst++] = HEAP32[src++] | (isScreen ? 0xff000000 : 0);
+ var num;
+ if (typeof CanvasPixelArray !== 'undefined' && data instanceof CanvasPixelArray) {
+ // IE10/IE11: ImageData objects are backed by the deprecated CanvasPixelArray,
+ // not UInt8ClampedArray. These don't have buffers, so we need to revert
+ // to copying a byte at a time. We do the undefined check because modern
+ // browsers do not define CanvasPixelArray anymore.
+ num = data.length;
+ while (dst < num) {
+ var val = HEAP32[src]; // This is optimized. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
+ data[dst ] = val & 0xff;
+ data[dst+1] = (val >> 8) & 0xff;
+ data[dst+2] = (val >> 16) & 0xff;
+ data[dst+3] = isScreen ? 0xff : ((val >> 24) & 0xff);
+ src++;
+ dst += 4;
+ }
+ } else {
+ var data32 = new Uint32Array(data.buffer);
+ num = data32.length;
+ if (isScreen) {
+ while (dst < num) {
+ // HEAP32[src++] is an optimization. Instead, we could do {{{ makeGetValue('buffer', 'dst', 'i32') }}};
+ data32[dst++] = HEAP32[src++] | 0xff000000;
+ }
+ } else {
+ while (dst < num) {
+ data32[dst++] = HEAP32[src++];
+ }
+ }
}
#else
var num = surfData.image.data.length;
@@ -1220,6 +1244,7 @@ var LibrarySDL = {
if (surf) SDL.freeSurface(surf);
},
+ SDL_UpperBlit__deps: ['SDL_LockSurface'],
SDL_UpperBlit: function(src, srcrect, dst, dstrect) {
var srcData = SDL.surfaces[src];
var dstData = SDL.surfaces[dst];
diff --git a/src/parseTools.js b/src/parseTools.js
index ff981264..874514b1 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -16,6 +16,7 @@ function processMacros(text) {
// Simple #if/else/endif preprocessing for a file. Checks if the
// ident checked is true in our global.
+// Also handles #include x.js (similar to C #include <file>)
function preprocess(text) {
var lines = text.split('\n');
var ret = '';
@@ -30,25 +31,29 @@ function preprocess(text) {
ret += line + '\n';
}
} else {
- if (line[1] && line[1] == 'i') { // if
- var parts = line.split(' ');
- var ident = parts[1];
- var op = parts[2];
- var value = parts[3];
- if (op) {
- if (op === '==') {
- showStack.push(ident in this && this[ident] == value);
- } else if (op === '!=') {
- showStack.push(!(ident in this && this[ident] == value));
+ if (line[1] == 'i') {
+ if (line[2] == 'f') { // if
+ var parts = line.split(' ');
+ var ident = parts[1];
+ var op = parts[2];
+ var value = parts[3];
+ if (op) {
+ if (op === '==') {
+ showStack.push(ident in this && this[ident] == value);
+ } else if (op === '!=') {
+ showStack.push(!(ident in this && this[ident] == value));
+ } else {
+ error('unsupported preprecessor op ' + op);
+ }
} else {
- error('unsupported preprecessor op ' + op);
+ showStack.push(ident in this && this[ident] > 0);
}
- } else {
- showStack.push(ident in this && this[ident] > 0);
+ } else if (line[2] == 'n') { // include
+ ret += '\n' + read(line.substr(line.indexOf(' ')+1)) + '\n'
}
- } else if (line[2] && line[2] == 'l') { // else
+ } else if (line[2] == 'l') { // else
showStack.push(!showStack.pop());
- } else if (line[2] && line[2] == 'n') { // endif
+ } else if (line[2] == 'n') { // endif
showStack.pop();
} else {
throw "Unclear preprocessor command: " + line;
diff --git a/src/preamble.js b/src/preamble.js
index 832ec2c3..f9fccdf6 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -866,6 +866,21 @@ var TOTAL_STACK = Module['TOTAL_STACK'] || {{{ TOTAL_STACK }}};
var TOTAL_MEMORY = Module['TOTAL_MEMORY'] || {{{ TOTAL_MEMORY }}};
var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}};
+#if ASM_JS
+var totalMemory = 4096;
+while (totalMemory < TOTAL_MEMORY || totalMemory < 2*TOTAL_STACK) {
+ if (totalMemory < 16*1024*1024) {
+ totalMemory *= 2;
+ } else {
+ totalMemory += 16*1024*1024
+ }
+}
+if (totalMemory !== TOTAL_MEMORY) {
+ Module.printErr('increasing TOTAL_MEMORY to ' + totalMemory + ' to be more reasonable');
+ TOTAL_MEMORY = totalMemory;
+}
+#endif
+
// Initialize the runtime's memory
#if USE_TYPED_ARRAYS
// check for full engine support (use string 'subarray' to avoid closure compiler confusion)
diff --git a/src/relooper/Relooper.cpp b/src/relooper/Relooper.cpp
index d2a48f63..de69e0ef 100644
--- a/src/relooper/Relooper.cpp
+++ b/src/relooper/Relooper.cpp
@@ -101,9 +101,7 @@ void Branch::Render(Block *Target, bool SetLabel) {
// Block
-int Block::IdCounter = 1; // 0 is reserved for clearings
-
-Block::Block(const char *CodeInit, const char *BranchVarInit) : Parent(NULL), Id(Block::IdCounter++), IsCheckedMultipleEntry(false) {
+Block::Block(const char *CodeInit, const char *BranchVarInit) : Parent(NULL), Id(-1), IsCheckedMultipleEntry(false) {
Code = strdup(CodeInit);
BranchVar = BranchVarInit ? strdup(BranchVarInit) : NULL;
}
@@ -142,6 +140,7 @@ void Block::Render(bool InLoop) {
if (!ProcessedBranchesOut.size()) return;
bool SetLabel = true; // in some cases it is clear we can avoid setting label, see later
+ bool ForceSetLabel = Shape::IsEmulated(Parent);
// A setting of the label variable (label = x) is necessary if it can
// cause an impact. The main case is where we set label to x, then elsewhere
@@ -210,7 +209,7 @@ void Block::Render(bool InLoop) {
Target = DefaultTarget;
Details = ProcessedBranchesOut[DefaultTarget];
}
- bool SetCurrLabel = SetLabel && Target->IsCheckedMultipleEntry;
+ bool SetCurrLabel = (SetLabel && Target->IsCheckedMultipleEntry) || ForceSetLabel;
bool HasFusedContent = Fused && contains(Fused->InnerMap, Target);
bool HasContent = SetCurrLabel || Details->Type != Branch::Direct || HasFusedContent || Details->Code;
if (iter != ProcessedBranchesOut.end()) {
@@ -272,10 +271,6 @@ void Block::Render(bool InLoop) {
}
}
-// Shape
-
-int Shape::IdCounter = 0;
-
// MultipleShape
void MultipleShape::RenderLoopPrefix() {
@@ -330,16 +325,19 @@ void LoopShape::Render(bool InLoop) {
if (Next) Next->Render(InLoop);
};
-/*
// EmulatedShape
void EmulatedShape::Render(bool InLoop) {
+ PrintIndented("label = %d;\n", Entry->Id);
+ if (Labeled) {
+ PrintIndented("L%d: ", Id);
+ }
PrintIndented("while(1) {\n");
Indenter::Indent();
- PrintIndented("switch(label) {\n");
+ PrintIndented("switch(label|0) {\n");
Indenter::Indent();
- for (int i = 0; i < Blocks.size(); i++) {
- Block *Curr = Blocks[i];
+ for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter++) {
+ Block *Curr = *iter;
PrintIndented("case %d: {\n", Curr->Id);
Indenter::Indent();
Curr->Render(InLoop);
@@ -353,11 +351,10 @@ void EmulatedShape::Render(bool InLoop) {
PrintIndented("}\n");
if (Next) Next->Render(InLoop);
};
-*/
// Relooper
-Relooper::Relooper() : Root(NULL) {
+Relooper::Relooper() : Root(NULL), Emulate(false), BlockIdCounter(1), ShapeIdCounter(0) { // block ID 0 is reserved for clearings
}
Relooper::~Relooper() {
@@ -366,6 +363,7 @@ Relooper::~Relooper() {
}
void Relooper::AddBlock(Block *New) {
+ New->Id = BlockIdCounter++;
Blocks.push_back(New);
}
@@ -419,7 +417,7 @@ void Relooper::Calculate(Block *Entry) {
for (BlockSet::iterator iter = Original->BranchesIn.begin(); iter != Original->BranchesIn.end(); iter++) {
Block *Prior = *iter;
Block *Split = new Block(Original->Code, Original->BranchVar);
- Parent->Blocks.push_back(Split);
+ Parent->AddBlock(Split);
PrintDebug(" to %d\n", Split->Id);
Split->BranchesIn.insert(Prior);
Branch *Details = Prior->BranchesOut[Original];
@@ -461,7 +459,7 @@ void Relooper::Calculate(Block *Entry) {
}
}
- Pre.SplitDeadEnds();
+ if (!Emulate) Pre.SplitDeadEnds();
// Recursively process the graph
@@ -470,6 +468,7 @@ void Relooper::Calculate(Block *Entry) {
// Add a shape to the list of shapes in this Relooper calculation
void Notice(Shape *New) {
+ New->Id = Parent->ShapeIdCounter++;
Parent->Shapes.push_back(New);
}
@@ -526,6 +525,21 @@ void Relooper::Calculate(Block *Entry) {
return Simple;
}
+ Shape *MakeEmulated(BlockSet &Blocks, Block *Entry, BlockSet &NextEntries) {
+ PrintDebug("creating emulated block with entry #%d and everything it can reach, %d blocks\n", Entry->Id, Blocks.size());
+ EmulatedShape *Emulated = new EmulatedShape;
+ Notice(Emulated);
+ Emulated->Entry = Entry;
+ for (BlockSet::iterator iter = Blocks.begin(); iter != Blocks.end(); iter++) {
+ Block *Curr = *iter;
+ Emulated->Blocks.insert(Curr);
+ Curr->Parent = Emulated;
+ Solipsize(Curr, Branch::Continue, Emulated, Blocks);
+ }
+ Blocks.clear();
+ return Emulated;
+ }
+
Shape *MakeLoop(BlockSet &Blocks, BlockSet& Entries, BlockSet &NextEntries) {
// Find the inner blocks in this loop. Proceed backwards from the entries until
// you reach a seen block, collecting as you go.
@@ -837,6 +851,9 @@ void Relooper::Calculate(Block *Entry) {
if (Entries->size() == 0) return Ret;
if (Entries->size() == 1) {
Block *Curr = *(Entries->begin());
+ if (Parent->Emulate) {
+ Make(MakeEmulated(Blocks, Curr, *NextEntries));
+ }
if (Curr->BranchesIn.size() == 0) {
// One entry, no looping ==> Simple
Make(MakeSimple(Blocks, Curr, *NextEntries));
@@ -844,6 +861,7 @@ void Relooper::Calculate(Block *Entry) {
// One entry, looping ==> Loop
Make(MakeLoop(Blocks, *Entries, *NextEntries));
}
+
// More than one entry, try to eliminate through a Multiple groups of
// independent blocks from an entry/ies. It is important to remove through
// multiples as opposed to looping since the former is more performant.
diff --git a/src/relooper/Relooper.h b/src/relooper/Relooper.h
index f3dedf8c..04f2ffc3 100644
--- a/src/relooper/Relooper.h
+++ b/src/relooper/Relooper.h
@@ -57,7 +57,7 @@ struct Block {
BlockBranchMap ProcessedBranchesOut;
BlockSet ProcessedBranchesIn;
Shape *Parent; // The shape we are directly inside
- int Id; // A unique identifier
+ int Id; // A unique identifier, defined when added to relooper
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
@@ -69,9 +69,6 @@ struct Block {
// Prints out the instructions code and branchings
void Render(bool InLoop);
-
- // INTERNAL
- static int IdCounter;
};
// Represents a structured control flow shape, one of
@@ -96,20 +93,22 @@ class SimpleShape;
class LabeledShape;
class MultipleShape;
class LoopShape;
+class EmulatedShape;
struct Shape {
- int Id; // A unique identifier. Used to identify loops, labels are Lx where x is the Id.
+ int Id; // A unique identifier. Used to identify loops, labels are Lx where x is the Id. Defined when added to relooper
Shape *Next; // The shape that will appear in the code right after this one
Shape *Natural; // The shape that control flow gets to naturally (if there is Next, then this is Next)
enum ShapeType {
Simple,
Multiple,
- Loop
+ Loop,
+ Emulated
};
ShapeType Type;
- Shape(ShapeType TypeInit) : Id(Shape::IdCounter++), Next(NULL), Type(TypeInit) {}
+ Shape(ShapeType TypeInit) : Id(-1), Next(NULL), Type(TypeInit) {}
virtual ~Shape() {}
virtual void Render(bool InLoop) = 0;
@@ -118,9 +117,7 @@ struct Shape {
static MultipleShape *IsMultiple(Shape *It) { return It && It->Type == Multiple ? (MultipleShape*)It : NULL; }
static LoopShape *IsLoop(Shape *It) { return It && It->Type == Loop ? (LoopShape*)It : NULL; }
static LabeledShape *IsLabeled(Shape *It) { return IsMultiple(It) || IsLoop(It) ? (LabeledShape*)It : NULL; }
-
- // INTERNAL
- static int IdCounter;
+ static EmulatedShape *IsEmulated(Shape *It) { return It && It->Type == Emulated ? (EmulatedShape*)It : NULL; }
};
struct SimpleShape : public Shape {
@@ -162,12 +159,15 @@ struct LoopShape : public LabeledShape {
void Render(bool InLoop);
};
-/*
-struct EmulatedShape : public Shape {
- std::deque<Block*> Blocks;
+// TODO EmulatedShape is only partially functional. Currently it can be used for the
+// entire set of blocks being relooped, but not subsets.
+struct EmulatedShape : public LabeledShape {
+ Block *Entry;
+ BlockSet Blocks;
+
+ EmulatedShape() : LabeledShape(Emulated) { Labeled = true; }
void Render(bool InLoop);
};
-*/
// Implements the relooper algorithm for a function's blocks.
//
@@ -184,6 +184,9 @@ struct Relooper {
std::deque<Block*> Blocks;
std::deque<Shape*> Shapes;
Shape *Root;
+ bool Emulate;
+ int BlockIdCounter;
+ int ShapeIdCounter;
Relooper();
~Relooper();
@@ -204,6 +207,9 @@ struct Relooper {
// Sets asm.js mode on or off (default is off)
static void SetAsmJSMode(int On);
+
+ // Sets whether we must emulate everything with switch-loop code
+ void SetEmulate(int E) { Emulate = E; }
};
typedef std::map<Block*, BlockSet> BlockBlockSetMap;
diff --git a/src/relooper/fuzzer.py b/src/relooper/fuzzer.py
index 50846d10..fa47583e 100644
--- a/src/relooper/fuzzer.py
+++ b/src/relooper/fuzzer.py
@@ -87,6 +87,12 @@ int main() {
Relooper r;
'''
+ if random.random() < 0.1:
+ print 'emulate'
+ fast += '''
+ r.SetEmulate(true);
+'''
+
for i in range(num):
fast += ''' r.AddBlock(b%d);
''' % i
diff --git a/src/relooper/test.cpp b/src/relooper/test.cpp
index fbd9c7aa..773f6ee4 100644
--- a/src/relooper/test.cpp
+++ b/src/relooper/test.cpp
@@ -258,5 +258,33 @@ int main() {
puts(buffer);
}
+
+ if (1) {
+ Relooper::SetOutputBuffer(buffer, sizeof(buffer));
+
+ printf("\n\n-- If pattern, emulated --\n\n", "the_var");
+
+ Block *b_a = new Block("// block A\n", NULL);
+ Block *b_b = new Block("// block B\n", "b_check()");
+ Block *b_c = new Block("// block C\n", NULL);
+
+ b_a->AddBranchTo(b_b, "check == 10", "atob();");
+ b_a->AddBranchTo(b_c, NULL, "atoc();");
+
+ b_b->AddBranchTo(b_c, "case 17:", "btoc();");
+ b_b->AddBranchTo(b_a, NULL, NULL);
+
+ Relooper r;
+ r.SetEmulate(true);
+ r.AddBlock(b_a);
+ r.AddBlock(b_b);
+ r.AddBlock(b_c);
+
+ r.Calculate(b_a);
+ printf("\n\n", "the_var");
+ r.Render();
+
+ puts(buffer);
+ }
}
diff --git a/src/relooper/test.txt b/src/relooper/test.txt
index 2c530567..540f7bdb 100644
--- a/src/relooper/test.txt
+++ b/src/relooper/test.txt
@@ -4,23 +4,23 @@
-// block A
-switch (the_var) {
-check == 10 {
- atob();
- // block B
+ // block A
switch (the_var) {
+ check == 10 {
+ atob();
+ // block B
+ switch (the_var) {
+ default: {
+ btoc();
+ }
+ }
+ break;
+ }
default: {
- btoc();
+ atoc();
}
}
- break;
-}
-default: {
- atoc();
-}
-}
-// block C
+ // block C
@@ -28,25 +28,25 @@ default: {
-// block A
-switch (the_var) {
-check == 15 {
- // block B
+ // block A
switch (the_var) {
- default: {
- }
+ check == 15 {
+ // block B
+ switch (the_var) {
+ default: {
+ }
+ }
+ break;
}
- break;
-}
-default: {
- // block C
- switch (the_var) {
default: {
+ // block C
+ switch (the_var) {
+ default: {
+ }
+ }
}
}
-}
-}
-// block D
+ // block D
@@ -54,81 +54,81 @@ default: {
-L9: while(1) {
- // block A
- var check = maybe();
- switch (the_var) {
- default: {
- }
- }
- // block B
- switch (the_var) {
- check == 41 {
- break;
- }
- default: {
- break L9;
- }
+ L0: while(1) {
+ // block A
+ var check = maybe();
+ switch (the_var) {
+ default: {
+ }
+ }
+ // block B
+ switch (the_var) {
+ check == 41 {
+ break;
+ }
+ default: {
+ break L0;
+ }
+ }
}
-}
-// block C