diff options
Diffstat (limited to 'src')
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 |