diff options
-rw-r--r-- | src/asm2wasm.h | 164 | ||||
-rw-r--r-- | test/wasm-only.asm.js | 36 | ||||
-rw-r--r-- | test/wasm-only.fromasm.imprecise.no-opts | 132 | ||||
-rw-r--r-- | test/wasm-only.fromasm.no-opts | 132 |
4 files changed, 412 insertions, 52 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h index 34825eb61..f7f084376 100644 --- a/src/asm2wasm.h +++ b/src/asm2wasm.h @@ -2089,6 +2089,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { Ref cases = ast[2]; bool seen = false; int64_t min = 0; // the lowest index we see; we will offset to it + int64_t max = 0; // the highest, to check if the range is too big for (unsigned i = 0; i < cases->size(); i++) { Ref curr = cases[i]; Ref condition = curr[0]; @@ -2097,71 +2098,130 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) { if (!seen) { seen = true; min = index; + max = index; } else { if (index < min) min = index; + if (index > max) max = index; } } } - if (br->condition->type == i32) { - Binary* offsetor = allocator.alloc<Binary>(); - offsetor->op = BinaryOp::SubInt32; - offsetor->left = br->condition; - offsetor->right = builder.makeConst(Literal(int32_t(min))); - offsetor->type = i32; - br->condition = offsetor; - } else { - assert(br->condition->type == i64); - // 64-bit condition. after offsetting it must be in a reasonable range, but the offsetting itself must be 64-bit - Binary* offsetor = allocator.alloc<Binary>(); - offsetor->op = BinaryOp::SubInt64; - offsetor->left = br->condition; - offsetor->right = builder.makeConst(Literal(int64_t(min))); - offsetor->type = i64; - br->condition = builder.makeUnary(UnaryOp::WrapInt64, offsetor); // TODO: check this fits in 32 bits - } + // we can use a switch if it's not too big + auto range = double(max) - double(min); // test using doubles to avoid UB + bool canSwitch = 0 <= range && range < 10240; auto top = allocator.alloc<Block>(); - top->list.push_back(br); - top->finalize(); - - for (unsigned i = 0; i < cases->size(); i++) { - Ref curr = cases[i]; - Ref condition = curr[0]; - Ref body = curr[1]; - auto case_ = processStatements(body, 0); - Name name; - if (condition->isNull()) { - name = br->default_ = nameMapper.pushLabelName("switch-default"); + if (canSwitch) { + if (br->condition->type == i32) { + Binary* offsetor = allocator.alloc<Binary>(); + offsetor->op = BinaryOp::SubInt32; + offsetor->left = br->condition; + offsetor->right = builder.makeConst(Literal(int32_t(min))); + offsetor->type = i32; + br->condition = offsetor; } else { - auto index = getLiteral(condition).getInteger(); - assert(index >= min); - index -= min; - assert(index >= 0); - uint64_t index_s = index; - name = nameMapper.pushLabelName("switch-case"); - if (br->targets.size() <= index_s) { - br->targets.resize(index_s + 1); + assert(br->condition->type == i64); + // 64-bit condition. after offsetting it must be in a reasonable range, but the offsetting itself must be 64-bit + Binary* offsetor = allocator.alloc<Binary>(); + offsetor->op = BinaryOp::SubInt64; + offsetor->left = br->condition; + offsetor->right = builder.makeConst(Literal(int64_t(min))); + offsetor->type = i64; + br->condition = builder.makeUnary(UnaryOp::WrapInt64, offsetor); // TODO: check this fits in 32 bits + } + + top->list.push_back(br); + top->finalize(); + + for (unsigned i = 0; i < cases->size(); i++) { + Ref curr = cases[i]; + Ref condition = curr[0]; + Ref body = curr[1]; + auto case_ = processStatements(body, 0); + Name name; + if (condition->isNull()) { + name = br->default_ = nameMapper.pushLabelName("switch-default"); + } else { + auto index = getLiteral(condition).getInteger(); + assert(index >= min); + index -= min; + assert(index >= 0); + uint64_t index_s = index; + name = nameMapper.pushLabelName("switch-case"); + if (br->targets.size() <= index_s) { + br->targets.resize(index_s + 1); + } + br->targets[index_s] = name; } - br->targets[index_s] = name; + auto next = allocator.alloc<Block>(); + top->name = name; + next->list.push_back(top); + next->list.push_back(case_); + next->finalize(); + top = next; + nameMapper.popLabelName(name); } - auto next = allocator.alloc<Block>(); + + // the outermost block can be branched to to exit the whole switch top->name = name; - next->list.push_back(top); - next->list.push_back(case_); - next->finalize(); - top = next; - nameMapper.popLabelName(name); - } - // the outermost block can be branched to to exit the whole switch - top->name = name; + // ensure a default + if (br->default_.isNull()) { + br->default_ = top->name; + } + for (size_t i = 0; i < br->targets.size(); i++) { + if (br->targets[i].isNull()) br->targets[i] = br->default_; + } + } else { + // we can't switch, make an if-chain instead of br_table + auto var = Builder::addVar(function, br->condition->type); + top->list.push_back(builder.makeSetLocal(var, br->condition)); + auto* brHolder = top; + If* chain = nullptr; + If* first = nullptr; + + for (unsigned i = 0; i < cases->size(); i++) { + Ref curr = cases[i]; + Ref condition = curr[0]; + Ref body = curr[1]; + auto case_ = processStatements(body, 0); + Name name; + if (condition->isNull()) { + name = br->default_ = nameMapper.pushLabelName("switch-default"); + } else { + name = nameMapper.pushLabelName("switch-case"); + auto* iff = builder.makeIf( + builder.makeBinary( + br->condition->type == i32 ? EqInt32 : EqInt64, + builder.makeGetLocal(var, br->condition->type), + builder.makeConst(getLiteral(condition)) + ), + builder.makeBreak(name), + chain + ); + chain = iff; + if (!first) first = iff; + } + auto next = allocator.alloc<Block>(); + top->name = name; + next->list.push_back(top); + next->list.push_back(case_); + next->finalize(); + top = next; + nameMapper.popLabelName(name); + } - // ensure a default - if (br->default_.isNull()) { - br->default_ = top->name; - } - for (size_t i = 0; i < br->targets.size(); i++) { - if (br->targets[i].isNull()) br->targets[i] = br->default_; + // the outermost block can be branched to to exit the whole switch + top->name = name; + + // ensure a default + if (br->default_.isNull()) { + br->default_ = top->name; + } + + first->ifFalse = builder.makeBreak(br->default_); + + brHolder->list.push_back(chain); + brHolder->finalize(); } breakStack.pop_back(); diff --git a/test/wasm-only.asm.js b/test/wasm-only.asm.js index 3adacf5be..371197173 100644 --- a/test/wasm-only.asm.js +++ b/test/wasm-only.asm.js @@ -248,6 +248,42 @@ function asm(global, env, buffer) { } return; } + function switch64TOOMUCH($a444) { + $a444 = i64($a444); + var $waka = 0; + switch (i64($a444)) { + case i64_const(0,1073741824): // spread is huge here, we should not make a jump table! + case i64_const(0,2147483648): { + return 40; + } + default: { + $waka = 1; + } + } + switch (100) { + case 107374182: // similar, but 32-bit + case 214748364: { + return 41; + } + default: { + $waka = 1001; + } + } + // no defaults + switch (i64($a444)) { + case i64_const(0,1073741824): // spread is huge here, we should not make a jump table! + case i64_const(0,2147483648): { + return 42; + } + } + switch (100) { + case 107374182: // similar, but 32-bit + case 214748364: { + return 43; + } + } + return 44; + } function keepAlive() { loads(); stores(); diff --git a/test/wasm-only.fromasm.imprecise.no-opts b/test/wasm-only.fromasm.imprecise.no-opts index 4b079e33e..4db467f47 100644 --- a/test/wasm-only.fromasm.imprecise.no-opts +++ b/test/wasm-only.fromasm.imprecise.no-opts @@ -757,6 +757,138 @@ ) (return) ) + (func $switch64TOOMUCH (param $$a444 i64) (result i32) + (local $$waka i32) + (local $2 i64) + (local $3 i32) + (local $4 i64) + (local $5 i32) + (block $switch + (block $switch-default + (block $switch-case0 + (block $switch-case + (set_local $2 + (get_local $$a444) + ) + (if + (i64.eq + (get_local $2) + (i64.const -9223372036854775808) + ) + (br $switch-case0) + (if + (i64.eq + (get_local $2) + (i64.const 4611686018427387904) + ) + (br $switch-case) + (br $switch-default) + ) + ) + ) + (nop) + ) + (return + (i32.const 40) + ) + ) + (set_local $$waka + (i32.const 1) + ) + ) + (block $switch1 + (block $switch-default4 + (block $switch-case3 + (block $switch-case2 + (set_local $3 + (i32.const 100) + ) + (if + (i32.eq + (get_local $3) + (i32.const 214748364) + ) + (br $switch-case3) + (if + (i32.eq + (get_local $3) + (i32.const 107374182) + ) + (br $switch-case2) + (br $switch-default4) + ) + ) + ) + (nop) + ) + (return + (i32.const 41) + ) + ) + (set_local $$waka + (i32.const 1001) + ) + ) + (block $switch5 + (block $switch-case7 + (block $switch-case6 + (set_local $4 + (get_local $$a444) + ) + (if + (i64.eq + (get_local $4) + (i64.const -9223372036854775808) + ) + (br $switch-case7) + (if + (i64.eq + (get_local $4) + (i64.const 4611686018427387904) + ) + (br $switch-case6) + (br $switch5) + ) + ) + ) + (nop) + ) + (return + (i32.const 42) + ) + ) + (block $switch8 + (block $switch-case10 + (block $switch-case9 + (set_local $5 + (i32.const 100) + ) + (if + (i32.eq + (get_local $5) + (i32.const 214748364) + ) + (br $switch-case10) + (if + (i32.eq + (get_local $5) + (i32.const 107374182) + ) + (br $switch-case9) + (br $switch8) + ) + ) + ) + (nop) + ) + (return + (i32.const 43) + ) + ) + (return + (i32.const 44) + ) + ) (func $keepAlive (call $loads) (call $stores) diff --git a/test/wasm-only.fromasm.no-opts b/test/wasm-only.fromasm.no-opts index b1f72e6dc..e71e6b2b2 100644 --- a/test/wasm-only.fromasm.no-opts +++ b/test/wasm-only.fromasm.no-opts @@ -805,6 +805,138 @@ ) (return) ) + (func $switch64TOOMUCH (param $$a444 i64) (result i32) + (local $$waka i32) + (local $2 i64) + (local $3 i32) + (local $4 i64) + (local $5 i32) + (block $switch + (block $switch-default + (block $switch-case0 + (block $switch-case + (set_local $2 + (get_local $$a444) + ) + (if + (i64.eq + (get_local $2) + (i64.const -9223372036854775808) + ) + (br $switch-case0) + (if + (i64.eq + (get_local $2) + (i64.const 4611686018427387904) + ) + (br $switch-case) + (br $switch-default) + ) + ) + ) + (nop) + ) + (return + (i32.const 40) + ) + ) + (set_local $$waka + (i32.const 1) + ) + ) + (block $switch1 + (block $switch-default4 + (block $switch-case3 + (block $switch-case2 + (set_local $3 + (i32.const 100) + ) + (if + (i32.eq + (get_local $3) + (i32.const 214748364) + ) + (br $switch-case3) + (if + (i32.eq + (get_local $3) + (i32.const 107374182) + ) + (br $switch-case2) + (br $switch-default4) + ) + ) + ) + (nop) + ) + (return + (i32.const 41) + ) + ) + (set_local $$waka + (i32.const 1001) + ) + ) + (block $switch5 + (block $switch-case7 + (block $switch-case6 + (set_local $4 + (get_local $$a444) + ) + (if + (i64.eq + (get_local $4) + (i64.const -9223372036854775808) + ) + (br $switch-case7) + (if + (i64.eq + (get_local $4) + (i64.const 4611686018427387904) + ) + (br $switch-case6) + (br $switch5) + ) + ) + ) + (nop) + ) + (return + (i32.const 42) + ) + ) + (block $switch8 + (block $switch-case10 + (block $switch-case9 + (set_local $5 + (i32.const 100) + ) + (if + (i32.eq + (get_local $5) + (i32.const 214748364) + ) + (br $switch-case10) + (if + (i32.eq + (get_local $5) + (i32.const 107374182) + ) + (br $switch-case9) + (br $switch8) + ) + ) + ) + (nop) + ) + (return + (i32.const 43) + ) + ) + (return + (i32.const 44) + ) + ) (func $keepAlive (call $loads) (call $stores) |