diff options
author | Alon Zakai <alonzakai@gmail.com> | 2016-05-06 18:28:41 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2016-05-06 18:28:41 -0700 |
commit | 254601c78a03b2012d42f15d64c51b773a8fbe4c (patch) | |
tree | c9dfdc133fdd77b5a62d2e9bab8f32c07c0ae5ab | |
parent | a3b3a516bd8117cd83aa0625839e614110d1fc0b (diff) | |
parent | a2cfae4c54ba79d7e8b348da10c77840ca934595 (diff) | |
download | binaryen-254601c78a03b2012d42f15d64c51b773a8fbe4c.tar.gz binaryen-254601c78a03b2012d42f15d64c51b773a8fbe4c.tar.bz2 binaryen-254601c78a03b2012d42f15d64c51b773a8fbe4c.zip |
Merge pull request #449 from WebAssembly/fuzz-relooper
Fuzz the relooper through the c api
-rw-r--r-- | scripts/fuzz_relooper.py | 282 | ||||
-rw-r--r-- | src/binaryen-c.cpp | 6 | ||||
-rw-r--r-- | src/binaryen-c.h | 14 | ||||
-rw-r--r-- | src/wasm.h | 4 | ||||
-rw-r--r-- | test/example/c-api-kitchen-sink.c | 16 | ||||
-rw-r--r-- | test/example/c-api-kitchen-sink.txt | 42 | ||||
-rw-r--r-- | test/example/relooper-fuzz.c | 267 | ||||
-rw-r--r-- | test/example/relooper-fuzz.txt | 255 |
8 files changed, 857 insertions, 29 deletions
diff --git a/scripts/fuzz_relooper.py b/scripts/fuzz_relooper.py new file mode 100644 index 000000000..c8108da95 --- /dev/null +++ b/scripts/fuzz_relooper.py @@ -0,0 +1,282 @@ +#! /usr/bin/env python + +# Copyright 2016 WebAssembly Community Group participants +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +''' +This fuzzes the relooper using the C API. +''' + +import difflib +import os +import random +import subprocess + +if os.environ.get('LD_LIBRARY_PATH'): + os.environ['LD_LIBRARY_PATH'] += os.pathsep + 'lib' +else: + os.environ['LD_LIBRARY_PATH'] = 'lib' + +while True: + # Random decisions + num = random.randint(2, 100) # TODO: 250+ + density = random.random() * random.random() + decisions = [random.randint(1, num * 20) for x in range(num * 3)] + branches = [0] * num + defaults = [0] * num + for i in range(num): + b = set([]) + bs = random.randint(1, max(1, + round(density * random.random() * (num - 1)))) + for j in range(bs): + b.add(random.randint(1, num - 1)) + b = list(b) + defaults[i] = random.choice(b) + b.remove(defaults[i]) + branches[i] = b + optimize = random.random() < 0.5 + print num, density, optimize + + for temp in ['fuzz.wasm', 'fuzz.wast', 'fast.txt', 'fuzz.slow.js', + 'fuzz.cpp']: + try: + os.unlink(temp) + except: + pass + + # parts + entry = ''' +var label = 0; +var state; +var decisions = %s; +var index = 0; +function check() { + if (index == decisions.length) throw 'HALT'; + console.log('(i32.const ' + (-decisions[index]) + ')'); + return decisions[index++]; +} +''' % str(decisions) + + slow = entry + '\n' + slow += 'label = 0;\n' + + slow += ''' +while(1) switch(label) { +''' + + fast = ''' + +#include <assert.h> +#include <stdio.h> + +#include "binaryen-c.h" + +// globals: address 4 is index +// decisions are at address 8+ + +int main() { + BinaryenModuleRef module = BinaryenModuleCreate(); + + // check() + + // if the end, halt + BinaryenExpressionRef halter = BinaryenIf(module, + BinaryenBinary(module, + BinaryenEq(), + BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), + BinaryenConst(module, BinaryenLiteralInt32(4))), + BinaryenConst(module, BinaryenLiteralInt32(4 * %d)) // jumps of 4 bytes + ), + BinaryenUnreachable(module), + NULL + ); + // increment index + BinaryenExpressionRef incer = BinaryenStore(module, + 4, 0, 0, + BinaryenConst(module, BinaryenLiteralInt32(4)), + BinaryenBinary(module, + BinaryenAdd(), + BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), + BinaryenConst(module, BinaryenLiteralInt32(4))), + BinaryenConst(module, BinaryenLiteralInt32(4)) + ) + ); + + // optionally, print the return value + BinaryenExpressionRef args[] = { + BinaryenBinary(module, + BinaryenSub(), + BinaryenConst(module, BinaryenLiteralInt32(0)), + BinaryenLoad(module, + 4, 0, 4, 0, BinaryenInt32(), + BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), + BinaryenConst(module, BinaryenLiteralInt32(4))) + ) + ) + }; + BinaryenExpressionRef debugger; + if (1) debugger = BinaryenCallImport(module, "print", args, 1, + BinaryenNone()); + else debugger = BinaryenNop(module); + + // return the decision. need to subtract 4 that we just added, + // and add 8 since that's where we start, so overall offset 4 + BinaryenExpressionRef returner = BinaryenLoad(module, + 4, 0, 4, 0, BinaryenInt32(), + BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), + BinaryenConst(module, BinaryenLiteralInt32(4))) + ); + BinaryenExpressionRef checkBodyList[] = { halter, incer, debugger, + returner }; + BinaryenExpressionRef checkBody = BinaryenBlock(module, + NULL, checkBodyList, sizeof(checkBodyList) / sizeof(BinaryenExpressionRef) + ); + BinaryenFunctionTypeRef i = BinaryenAddFunctionType(module, "i", + BinaryenInt32(), + NULL, 0); + BinaryenAddFunction(module, "check", i, NULL, 0, checkBody); + + // contents of main() begin here + + RelooperRef relooper = RelooperCreate(); + +''' % len(decisions) + + for i in range(0, num): + slow += ' case %d: console.log("(i32.const %d)"); state = check(); \n' % ( + i, i) + 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 += ' label = %d; break\n' % defaults[i] + + for i in range(num): + fast += ''' + RelooperBlockRef b%d; + { + BinaryenExpressionRef args[] = { + BinaryenConst(module, BinaryenLiteralInt32(%d)) + }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, + BinaryenInt32())) + }; + b%d = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } +''' % (i, i, i) + + for i in range(num): + b = branches[i] + for j in range(len(b)): + fast += ''' + RelooperAddBranch(b%d, b%d, BinaryenBinary(module, + BinaryenEq(), + BinaryenBinary(module, + BinaryenRemU(), + BinaryenGetLocal(module, 0, BinaryenInt32()), + BinaryenConst(module, BinaryenLiteralInt32(%d)) + ), + BinaryenConst(module, BinaryenLiteralInt32(%d)) + ), NULL); +''' % (i, b[j], len(b) + 1, j) + fast += ''' + RelooperAddBranch(b%d, b%d, NULL, NULL); +''' % (i, defaults[i]) + + fast += ''' + BinaryenExpressionRef body = RelooperRenderAndDispose(relooper, b0, 1, + module); + + int decisions[] = { %s }; + int numDecisions = sizeof(decisions)/sizeof(int); + + // write out all the decisions, then the body of the function + BinaryenExpressionRef full[numDecisions + 1]; + + for (int i = 0; i < numDecisions; i++) { + full[i] = BinaryenStore(module, + 4, 0, 0, + BinaryenConst(module, BinaryenLiteralInt32(8 + 4 * i)), + BinaryenConst(module, BinaryenLiteralInt32(decisions[i])) + ); + } + full[numDecisions] = body; + BinaryenExpressionRef all = BinaryenBlock(module, NULL, full, + numDecisions + 1); + + BinaryenFunctionTypeRef v = BinaryenAddFunctionType(module, "v", + BinaryenNone(), + NULL, 0); + // locals: state, free-for-label + BinaryenType localTypes[] = { BinaryenInt32(), BinaryenInt32() }; + BinaryenFunctionRef theMain = BinaryenAddFunction(module, "main", v, + localTypes, 2, all); + BinaryenSetStart(module, theMain); + + // import + + BinaryenType iparams[] = { BinaryenInt32() }; + BinaryenFunctionTypeRef vi = BinaryenAddFunctionType(module, "vi", + BinaryenNone(), + iparams, 1); + BinaryenAddImport(module, "print", "spectest", "print", vi); + + // memory + BinaryenSetMemory(module, 1, 1, "mem", NULL, NULL, NULL, 0); + + // optionally, optimize + if (%d) BinaryenModuleOptimize(module); + + assert(BinaryenModuleValidate(module)); + + // write it out + + BinaryenModulePrint(module); + + BinaryenModuleDispose(module); + + return 0; +} +''' % (', '.join(map(str, decisions)), optimize) + + slow += '}' + + open('fuzz.slow.js', 'w').write(slow) + open('fuzz.cpp', 'w').write(fast) + + print '.' + cmd = ['g++', 'fuzz.cpp', '-Isrc', '-g', '-lbinaryen-c', '-lasmjs', + '-lsupport', '-Llib/.', '-pthread', '-o', 'fuzz', '-g'] + subprocess.check_call(cmd) + print '^' + subprocess.check_call(['./fuzz'], stdout=open('fuzz.wast', 'w')) + print '*' + subprocess.call(['bin/binaryen-shell', 'fuzz.wast'], + stdout=open('fast.txt', 'w'), stderr=subprocess.PIPE) + fast_out = open('fast.txt').read() + print '-' + slow_out = subprocess.Popen(['nodejs', 'fuzz.slow.js'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate()[0] + print '_' + + if slow_out != fast_out: + print ''.join([a.rstrip() + '\n' for a in difflib.unified_diff( + slow_out.split('\n'), + fast_out.split('\n'), + fromfile='slow', + tofile='fast')]) + assert False diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp index d46125bb3..eca4ce5f1 100644 --- a/src/binaryen-c.cpp +++ b/src/binaryen-c.cpp @@ -209,21 +209,23 @@ BinaryenExpressionRef BinaryenSwitch(BinaryenModuleRef module, const char **name ret->finalize(); return ret; } -BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module, const char *target, BinaryenExpressionRef* operands, BinaryenIndex numOperands) { +BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module, const char *target, BinaryenExpressionRef* operands, BinaryenIndex numOperands, BinaryenType returnType) { auto* ret = ((Module*)module)->allocator.alloc<Call>(); ret->target = target; for (BinaryenIndex i = 0; i < numOperands; i++) { ret->operands.push_back((Expression*)operands[i]); } + ret->type = WasmType(returnType); ret->finalize(); return ret; } -BinaryenExpressionRef BinaryenCallImport(BinaryenModuleRef module, const char *target, BinaryenExpressionRef* operands, BinaryenIndex numOperands) { +BinaryenExpressionRef BinaryenCallImport(BinaryenModuleRef module, const char *target, BinaryenExpressionRef* operands, BinaryenIndex numOperands, BinaryenType returnType) { auto* ret = ((Module*)module)->allocator.alloc<CallImport>(); ret->target = target; for (BinaryenIndex i = 0; i < numOperands; i++) { ret->operands.push_back((Expression*)operands[i]); } + ret->type = WasmType(returnType); ret->finalize(); return ret; } diff --git a/src/binaryen-c.h b/src/binaryen-c.h index da445dfb2..583454a9f 100644 --- a/src/binaryen-c.h +++ b/src/binaryen-c.h @@ -199,8 +199,12 @@ BinaryenExpressionRef BinaryenLoop(BinaryenModuleRef module, const char* out, co BinaryenExpressionRef BinaryenBreak(BinaryenModuleRef module, const char* name, BinaryenExpressionRef condition, BinaryenExpressionRef value); // Switch: value can be NULL BinaryenExpressionRef BinaryenSwitch(BinaryenModuleRef module, const char **names, BinaryenIndex numNames, const char* defaultName, BinaryenExpressionRef condition, BinaryenExpressionRef value); -BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module, const char *target, BinaryenExpressionRef* operands, BinaryenIndex numOperands); -BinaryenExpressionRef BinaryenCallImport(BinaryenModuleRef module, const char *target, BinaryenExpressionRef* operands, BinaryenIndex numOperands); +// Call, CallImport: Note the 'returnType' parameter. You must declare the +// type returned by the function being called, as that +// function might not have been created yet, so we don't +// know what it is. +BinaryenExpressionRef BinaryenCall(BinaryenModuleRef module, const char *target, BinaryenExpressionRef* operands, BinaryenIndex numOperands, BinaryenType returnType); +BinaryenExpressionRef BinaryenCallImport(BinaryenModuleRef module, const char *target, BinaryenExpressionRef* operands, BinaryenIndex numOperands, BinaryenType returnType); BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module, BinaryenExpressionRef target, BinaryenExpressionRef* operands, BinaryenIndex numOperands, BinaryenFunctionTypeRef type); // GetLocal: Note the 'type' parameter. It might seem redundant, since the // local at that index must have a type. However, this API lets you @@ -214,7 +218,9 @@ BinaryenExpressionRef BinaryenCallIndirect(BinaryenModuleRef module, BinaryenExp // begins.) BinaryenExpressionRef BinaryenGetLocal(BinaryenModuleRef module, BinaryenIndex index, BinaryenType type); BinaryenExpressionRef BinaryenSetLocal(BinaryenModuleRef module, BinaryenIndex index, BinaryenExpressionRef value); +// Load: align can be 0, in which case it will be the natural alignment (equal to bytes) BinaryenExpressionRef BinaryenLoad(BinaryenModuleRef module, uint32_t bytes, int8_t signed_, uint32_t offset, uint32_t align, BinaryenType type, BinaryenExpressionRef ptr); +// Store: align can be 0, in which case it will be the natural alignment (equal to bytes) BinaryenExpressionRef BinaryenStore(BinaryenModuleRef module, uint32_t bytes, uint32_t offset, uint32_t align, BinaryenExpressionRef ptr, BinaryenExpressionRef value); BinaryenExpressionRef BinaryenConst(BinaryenModuleRef module, struct BinaryenLiteral value); BinaryenExpressionRef BinaryenUnary(BinaryenModuleRef module, BinaryenOp op, BinaryenExpressionRef value); @@ -302,10 +308,10 @@ RelooperBlockRef RelooperAddBlock(RelooperRef relooper, BinaryenExpressionRef co void RelooperAddBranch(RelooperBlockRef from, RelooperBlockRef to, BinaryenExpressionRef condition, BinaryenExpressionRef code); // Create a basic block that ends a switch on a condition -RelooperBlockRef RelooperAddBlockWithSwitch(RelooperRef relooper, BinaryenExpressionRef code, BinaryenExpressionRef condition); +// TODO RelooperBlockRef RelooperAddBlockWithSwitch(RelooperRef relooper, BinaryenExpressionRef code, BinaryenExpressionRef condition); // Create a switch-style branch to another basic block. The block's switch table will have an index for this branch -void RelooperAddBranchForSwitch(RelooperBlockRef from, RelooperBlockRef to, BinaryenIndex index, BinaryenExpressionRef code); +// TODO void RelooperAddBranchForSwitch(RelooperBlockRef from, RelooperBlockRef to, BinaryenIndex index, BinaryenExpressionRef code); // Generate structed wasm control flow from the CFG of blocks and branches that were created // on this relooper instance. This returns the rendered output, and also disposes of the diff --git a/src/wasm.h b/src/wasm.h index dff0bcd5a..049dc2e30 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1022,6 +1022,10 @@ public: ExpressionList operands; FunctionType *fullType; Expression *target; + + void finalize() { + type = fullType->result; + } }; class GetLocal : public SpecificExpression<Expression::GetLocalId> { diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c index 4a63e931d..db1903cfd 100644 --- a/test/example/c-api-kitchen-sink.c +++ b/test/example/c-api-kitchen-sink.c @@ -165,9 +165,15 @@ void test_core() { BinaryenBreak(module, "the-body", NULL, NULL), BinaryenSwitch(module, switchValueNames, 1, "the-value", makeInt32(module, 0), makeInt32(module, 1)), BinaryenSwitch(module, switchBodyNames, 1, "the-body", makeInt32(module, 2), NULL), - BinaryenCall(module, "kitchen-sinker", callOperands4, 4), - BinaryenCallImport(module, "an-imported", callOperands2, 2), - BinaryenCallIndirect(module, makeInt32(module, 2449), callOperands4, 4, iiIfF), + BinaryenUnary(module, BinaryenEqZ(), // check the output type of the call node + BinaryenCall(module, "kitchen-sinker", callOperands4, 4, BinaryenInt32()) + ), + BinaryenUnary(module, BinaryenEqZ(), // check the output type of the call node + BinaryenCallImport(module, "an-imported", callOperands2, 2, BinaryenFloat32()) + ), + BinaryenUnary(module, BinaryenEqZ(), // check the output type of the call node + BinaryenCallIndirect(module, makeInt32(module, 2449), callOperands4, 4, iiIfF) + ), BinaryenGetLocal(module, 0, BinaryenInt32()), BinaryenSetLocal(module, 0, makeInt32(module, 101)), BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), makeInt32(module, 1)), @@ -195,8 +201,8 @@ void test_core() { // Imports BinaryenType iparams[2] = { BinaryenInt32(), BinaryenFloat64() }; - BinaryenFunctionTypeRef viF = BinaryenAddFunctionType(module, "viF", BinaryenNone(), iparams, 2); - BinaryenAddImport(module, "an-imported", "module", "base", viF); + BinaryenFunctionTypeRef fiF = BinaryenAddFunctionType(module, "fiF", BinaryenFloat32(), iparams, 2); + BinaryenAddImport(module, "an-imported", "module", "base", fiF); // Exports diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index cb037d80c..7a1366888 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -10,9 +10,9 @@ BinaryenFloat64: 4 (export "mem" memory) (start $starter) (type $iiIfF (func (param i32 i64 f32 f64) (result i32))) - (type $viF (func (param i32 f64))) + (type $fiF (func (param i32 f64) (result f32))) (type $v (func)) - (import $an-imported "module" "base" (param i32 f64)) + (import $an-imported "module" "base" (param i32 f64) (result f32)) (export "kitchen_sinker" $kitchen-sinker) (table $kitchen-sinker) (func $kitchen-sinker (type $iiIfF) (param $0 i32) (param $1 i64) (param $2 f32) (param $3 f64) (result i32) @@ -293,22 +293,28 @@ BinaryenFloat64: 4 (br_table $the-body $the-body (i32.const 2) ) - (call $kitchen-sinker - (i32.const 13) - (i64.const 37) - (f32.const 1.2999999523162842) - (f64.const 3.7) - ) - (call_import $an-imported - (i32.const 13) - (f64.const 3.7) - ) - (call_indirect $iiIfF - (i32.const 2449) - (i32.const 13) - (i64.const 37) - (f32.const 1.2999999523162842) - (f64.const 3.7) + (i32.eqz + (call $kitchen-sinker + (i32.const 13) + (i64.const 37) + (f32.const 1.2999999523162842) + (f64.const 3.7) + ) + ) + (f32.eqz + (call_import $an-imported + (i32.const 13) + (f64.const 3.7) + ) + ) + (i32.eqz + (call_indirect $iiIfF + (i32.const 2449) + (i32.const 13) + (i64.const 37) + (f32.const 1.2999999523162842) + (f64.const 3.7) + ) ) (get_local $0) (set_local $0 diff --git a/test/example/relooper-fuzz.c b/test/example/relooper-fuzz.c new file mode 100644 index 000000000..ba9bcb3ed --- /dev/null +++ b/test/example/relooper-fuzz.c @@ -0,0 +1,267 @@ + + +#include <assert.h> +#include <stdio.h> + +#include "binaryen-c.h" + +// globals: address 4 is index +// decisions are at address 8+ + +int main() { + BinaryenModuleRef module = BinaryenModuleCreate(); + + // check() + + // if the end, halt + BinaryenExpressionRef halter = BinaryenIf(module, + BinaryenBinary(module, + BinaryenEq(), + BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))), + BinaryenConst(module, BinaryenLiteralInt32(4 * 27)) // jumps of 4 bytes + ), + BinaryenUnreachable(module), + NULL + ); + // increment index + BinaryenExpressionRef incer = BinaryenStore(module, + 4, 0, 0, + BinaryenConst(module, BinaryenLiteralInt32(4)), + BinaryenBinary(module, + BinaryenAdd(), + BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))), + BinaryenConst(module, BinaryenLiteralInt32(4)) + ) + ); + + // optionally, print the return value + BinaryenExpressionRef args[] = { + BinaryenBinary(module, + BinaryenSub(), + BinaryenConst(module, BinaryenLiteralInt32(0)), + BinaryenLoad(module, + 4, 0, 4, 0, BinaryenInt32(), + BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))) + ) + ) + }; + BinaryenExpressionRef debugger; + if (1) debugger = BinaryenCallImport(module, "print", args, 1, BinaryenNone()); + else debugger = BinaryenNop(module); + + // return the decision. need to subtract 4 that we just added, and add 8 since that's where we start, so overall offset 4 + BinaryenExpressionRef returner = BinaryenLoad(module, + 4, 0, 4, 0, BinaryenInt32(), + BinaryenLoad(module, 4, 0, 0, 0, BinaryenInt32(), BinaryenConst(module, BinaryenLiteralInt32(4))) + ); + BinaryenExpressionRef checkBodyList[] = { halter, incer, debugger, returner }; + BinaryenExpressionRef checkBody = BinaryenBlock(module, NULL, checkBodyList, sizeof(checkBodyList) / sizeof(BinaryenExpressionRef)); + BinaryenFunctionTypeRef i = BinaryenAddFunctionType(module, "i", BinaryenInt32(), NULL, 0); + BinaryenAddFunction(module, "check", i, NULL, 0, checkBody); + + // contents of main() begin here + + RelooperRef relooper = RelooperCreate(); + + + RelooperBlockRef b0; + { + BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(0)) }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) + }; + b0 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } + + RelooperBlockRef b1; + { + BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(1)) }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) + }; + b1 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } + + RelooperBlockRef b2; + { + BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(2)) }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) + }; + b2 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } + + RelooperBlockRef b3; + { + BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(3)) }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) + }; + b3 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } + + RelooperBlockRef b4; + { + BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(4)) }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) + }; + b4 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } + + RelooperBlockRef b5; + { + BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(5)) }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) + }; + b5 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } + + RelooperBlockRef b6; + { + BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(6)) }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) + }; + b6 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } + + RelooperBlockRef b7; + { + BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(7)) }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) + }; + b7 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } + + RelooperBlockRef b8; + { + BinaryenExpressionRef args[] = { BinaryenConst(module, BinaryenLiteralInt32(8)) }; + BinaryenExpressionRef list[] = { + BinaryenCallImport(module, "print", args, 1, BinaryenNone()), + BinaryenSetLocal(module, 0, BinaryenCall(module, "check", NULL, 0, BinaryenInt32())) + }; + b8 = RelooperAddBlock(relooper, BinaryenBlock(module, NULL, list, 2)); + } + + RelooperAddBranch(b0, b5, BinaryenBinary(module, + BinaryenEq(), + BinaryenBinary(module, + BinaryenRemU(), + BinaryenGetLocal(module, 0, BinaryenInt32()), + BinaryenConst(module, BinaryenLiteralInt32(2)) + ), + BinaryenConst(module, BinaryenLiteralInt32(0)) + ), NULL); + + RelooperAddBranch(b0, b8, NULL, NULL); + + RelooperAddBranch(b1, b5, NULL, NULL); + + RelooperAddBranch(b2, b5, NULL, NULL); + + RelooperAddBranch(b3, b5, BinaryenBinary(module, + BinaryenEq(), + BinaryenBinary(module, + BinaryenRemU(), + BinaryenGetLocal(module, 0, BinaryenInt32()), + BinaryenConst(module, BinaryenLiteralInt32(2)) + ), + BinaryenConst(module, BinaryenLiteralInt32(0)) + ), NULL); + + RelooperAddBranch(b3, b8, NULL, NULL); + + RelooperAddBranch(b4, b4, BinaryenBinary(module, + BinaryenEq(), + BinaryenBinary(module, + BinaryenRemU(), + BinaryenGetLocal(module, 0, BinaryenInt32()), + BinaryenConst(module, BinaryenLiteralInt32(3)) + ), + BinaryenConst(module, BinaryenLiteralInt32(0)) + ), NULL); + + RelooperAddBranch(b4, b5, BinaryenBinary(module, + BinaryenEq(), + BinaryenBinary(module, + BinaryenRemU(), + BinaryenGetLocal(module, 0, BinaryenInt32()), + BinaryenConst(module, BinaryenLiteralInt32(3)) + ), + BinaryenConst(module, BinaryenLiteralInt32(1)) + ), NULL); + + RelooperAddBranch(b4, b2, NULL, NULL); + + RelooperAddBranch(b5, b4, BinaryenBinary(module, + BinaryenEq(), + BinaryenBinary(module, + BinaryenRemU(), + BinaryenGetLocal(module, 0, BinaryenInt32()), + BinaryenConst(module, BinaryenLiteralInt32(2)) + ), + BinaryenConst(module, BinaryenLiteralInt32(0)) + ), NULL); + + RelooperAddBranch(b5, b5, NULL, NULL); + + RelooperAddBranch(b6, b6, NULL, NULL); + + RelooperAddBranch(b7, b8, NULL, NULL); + + RelooperAddBranch(b8, b4, NULL, NULL); + + BinaryenExpressionRef body = RelooperRenderAndDispose(relooper, b0, 1, module); + + int decisions[] = { 89, 12, 78, 149, 118, 179, 127, 80, 21, 34, 119, 98, 38, 29, 36, 147, 13, 55, 166, 16, 143, 52, 130, 150, 176, 91, 34 }; + int numDecisions = sizeof(decisions)/sizeof(int); + + BinaryenExpressionRef full[numDecisions + 1]; // write out all the decisions, then the body of the function + + for (int i = 0; i < numDecisions; i++) { + full[i] = BinaryenStore(module, + 4, 0, 0, + BinaryenConst(module, BinaryenLiteralInt32(8 + 4 * i)), + BinaryenConst(module, BinaryenLiteralInt32(decisions[i])) + ); + } + full[numDecisions] = body; + BinaryenExpressionRef all = BinaryenBlock(module, NULL, full, numDecisions + 1); + + BinaryenFunctionTypeRef v = BinaryenAddFunctionType(module, "v", BinaryenNone(), NULL, 0); + BinaryenType localTypes[] = { BinaryenInt32(), BinaryenInt32() }; // state, free-for-label + BinaryenFunctionRef theMain = BinaryenAddFunction(module, "main", v, localTypes, 2, all); + BinaryenSetStart(module, theMain); + + // import + + BinaryenType iparams[] = { BinaryenInt32() }; + BinaryenFunctionTypeRef vi = BinaryenAddFunctionType(module, "vi", BinaryenNone(), iparams, 1); + BinaryenAddImport(module, "print", "spectest", "print", vi); + + // memory + BinaryenSetMemory(module, 1, 1, "mem", NULL, NULL, NULL, 0); + + BinaryenModuleOptimize(module); + + assert(BinaryenModuleValidate(module)); + + // write it out + + BinaryenModulePrint(module); + + BinaryenModuleDispose(module); + + return 0; +} diff --git a/test/example/relooper-fuzz.txt b/test/example/relooper-fuzz.txt new file mode 100644 index 000000000..7dab4c5be --- /dev/null +++ b/test/example/relooper-fuzz.txt @@ -0,0 +1,255 @@ +(module + (memory 1 1) + (export "mem" memory) + (start $main) + (type $i (func (result i32))) + (type $v (func)) + (type $vi (func (param i32))) + (import $print "spectest" "print" (param i32)) + (func $check (type $i) (result i32) + (if + (i32.eq + (i32.load + (i32.const 4) + ) + (i32.const 108) + ) + (unreachable) + ) + (i32.store + (i32.const 4) + (i32.add + (i32.load + (i32.const 4) + ) + (i32.const 4) + ) + ) + (call_import $print + (i32.sub + (i32.const 0) + (i32.load offset=4 + (i32.load + (i32.const 4) + ) + ) + ) + ) + (i32.load offset=4 + (i32.load + (i32.const 4) + ) + ) + ) + (func $main (type $v) + (local $0 i32) + (local $1 i32) + (i32.store + (i32.const 8) + (i32.const 89) + ) + (i32.store + (i32.const 12) + (i32.const 12) + ) + (i32.store + (i32.const 16) + (i32.const 78) + ) + (i32.store + (i32.const 20) + (i32.const 149) + ) + (i32.store + (i32.const 24) + (i32.const 118) + ) + (i32.store + (i32.const 28) + (i32.const 179) + ) + (i32.store + (i32.const 32) + (i32.const 127) + ) + (i32.store + (i32.const 36) + (i32.const 80) + ) + (i32.store + (i32.const 40) + (i32.const 21) + ) + (i32.store + (i32.const 44) + (i32.const 34) + ) + (i32.store + (i32.const 48) + (i32.const 119) + ) + (i32.store + (i32.const 52) + (i32.const 98) + ) + (i32.store + (i32.const 56) + (i32.const 38) + ) + (i32.store + (i32.const 60) + (i32.const 29) + ) + (i32.store + (i32.const 64) + (i32.const 36) + ) + (i32.store + (i32.const 68) + (i32.const 147) + ) + (i32.store + (i32.const 72) + (i32.const 13) + ) + (i32.store + (i32.const 76) + (i32.const 55) + ) + (i32.store + (i32.const 80) + (i32.const 166) + ) + (i32.store + (i32.const 84) + (i32.const 16) + ) + (i32.store + (i32.const 88) + (i32.const 143) + ) + (i32.store + (i32.const 92) + (i32.const 52) + ) + (i32.store + (i32.const 96) + (i32.const 130) + ) + (i32.store + (i32.const 100) + (i32.const 150) + ) + (i32.store + (i32.const 104) + (i32.const 176) + ) + (i32.store + (i32.const 108) + (i32.const 91) + ) + (i32.store + (i32.const 112) + (i32.const 34) + ) + (call_import $print + (i32.const 0) + ) + (if + (i32.eq + (i32.rem_u + (set_local $0 + (call $check) + ) + (i32.const 2) + ) + (i32.const 0) + ) + (set_local $1 + (i32.const 6) + ) + (block + (call_import $print + (i32.const 8) + ) + (set_local $0 + (call $check) + ) + ) + ) + (loop $shape$3$break $shape$3$continue + (if + (i32.eq + (get_local $1) + (i32.const 6) + ) + (block + (set_local $1 + (i32.const 0) + ) + (call_import $print + (i32.const 5) + ) + (if + (i32.eq + (i32.rem_u + (set_local $0 + (call $check) + ) + (i32.const 2) + ) + (i32.const 0) + ) + (br $shape$3$continue) + (block + (set_local $1 + (i32.const 6) + ) + (br $shape$3$continue) + ) + ) + ) + ) + (call_import $print + (i32.const 4) + ) + (if + (i32.eq + (i32.rem_u + (set_local $0 + (call $check) + ) + (i32.const 3) + ) + (i32.const 0) + ) + (br $shape$3$continue) + (if + (i32.eq + (i32.rem_u + (get_local $0) + (i32.const 3) + ) + (i32.const 1) + ) + (block + (set_local $1 + (i32.const 6) + ) + (br $shape$3$continue) + ) + ) + ) + (call_import $print + (i32.const 2) + ) + (set_local $0 + (call $check) + ) + (set_local $1 + (i32.const 6) + ) + (br $shape$3$continue) + ) + ) +) |