diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ast/branch-utils.h | 22 | ||||
-rw-r--r-- | src/ast_utils.h | 2 | ||||
-rw-r--r-- | src/passes/OptimizeInstructions.cpp | 2 | ||||
-rw-r--r-- | src/passes/Print.cpp | 5 | ||||
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 4 | ||||
-rw-r--r-- | src/passes/SimplifyLocals.cpp | 2 | ||||
-rw-r--r-- | src/wasm-binary.h | 6 | ||||
-rw-r--r-- | src/wasm-validator.h | 1 | ||||
-rw-r--r-- | src/wasm.h | 4 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 22 | ||||
-rw-r--r-- | src/wasm/wasm-s-parser.cpp | 9 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 20 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 3 |
13 files changed, 70 insertions, 32 deletions
diff --git a/src/ast/branch-utils.h b/src/ast/branch-utils.h index 05ead8571..d574945ef 100644 --- a/src/ast/branch-utils.h +++ b/src/ast/branch-utils.h @@ -24,24 +24,23 @@ namespace wasm { namespace BranchUtils { -// branches not actually taken (e.g. (br $out (unreachable))) -// are trivially ignored in our type system +// Some branches are obviously not actually reachable (e.g. (br $out (unreachable))) -inline bool isBranchTaken(Break* br) { +inline bool isBranchReachable(Break* br) { return !(br->value && br->value->type == unreachable) && !(br->condition && br->condition->type == unreachable); } -inline bool isBranchTaken(Switch* sw) { +inline bool isBranchReachable(Switch* sw) { return !(sw->value && sw->value->type == unreachable) && sw->condition->type != unreachable; } -inline bool isBranchTaken(Expression* expr) { +inline bool isBranchReachable(Expression* expr) { if (auto* br = expr->dynCast<Break>()) { - return isBranchTaken(br); + return isBranchReachable(br); } else if (auto* sw = expr->dynCast<Switch>()) { - return isBranchTaken(sw); + return isBranchReachable(sw); } WASM_UNREACHABLE(); } @@ -103,8 +102,9 @@ inline std::set<Name> getBranchTargets(Expression* ast) { // Finds if there are branches targeting a name. Note that since names are // unique in our IR, we just need to look for the name, and do not need // to analyze scoping. -// By default we consider untaken branches (so any named use). You can unset named to -// avoid that (and only note branches that are not obviously unreachable) +// By default we consider all branches, so any place there is a branch that +// names the target. You can unset 'named' to only note branches that appear +// reachable (i.e., are not obviously unreachable). struct BranchSeeker : public PostWalker<BranchSeeker> { Name target; bool named = true; @@ -144,7 +144,7 @@ struct BranchSeeker : public PostWalker<BranchSeeker> { if (curr->default_ == target) noteFound(curr->value); } - static bool hasTaken(Expression* tree, Name target) { + static bool hasReachable(Expression* tree, Name target) { if (!target.is()) return false; BranchSeeker seeker(target); seeker.named = false; @@ -152,7 +152,7 @@ struct BranchSeeker : public PostWalker<BranchSeeker> { return seeker.found > 0; } - static Index countTaken(Expression* tree, Name target) { + static Index countReachable(Expression* tree, Name target) { if (!target.is()) return 0; BranchSeeker seeker(target); seeker.named = false; diff --git a/src/ast_utils.h b/src/ast_utils.h index 852cf89a3..95f725636 100644 --- a/src/ast_utils.h +++ b/src/ast_utils.h @@ -62,7 +62,7 @@ struct ExpressionAnalyzer { if (auto* br = curr->dynCast<Break>()) { if (!br->condition) return true; } else if (auto* block = curr->dynCast<Block>()) { - if (block->list.size() > 0 && obviouslyDoesNotFlowOut(block->list.back()) && !BranchUtils::BranchSeeker::hasTaken(block, block->name)) return true; + if (block->list.size() > 0 && obviouslyDoesNotFlowOut(block->list.back()) && !BranchUtils::BranchSeeker::hasReachable(block, block->name)) return true; } return false; } diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 0b47d3398..ad667f888 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -31,6 +31,8 @@ #include <ast/properties.h> #include <ast/literal-utils.h> +// TODO: Use the new sign-extension opcodes where appropriate. This needs to be conditionalized on the availability of atomics. + namespace wasm { Name I32_EXPR = "i32.expr", diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 3c538a40e..04b649ff6 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -474,6 +474,11 @@ struct PrintSExpression : public Visitor<PrintSExpression> { case DemoteFloat64: o << "f32.demote/f64"; break; case ReinterpretInt32: o << "f32.reinterpret/i32"; break; case ReinterpretInt64: o << "f64.reinterpret/i64"; break; + case ExtendS8Int32: o << "i32.extend8_s"; break; + case ExtendS16Int32: o << "i32.extend16_s"; break; + case ExtendS8Int64: o << "i64.extend8_s"; break; + case ExtendS16Int64: o << "i64.extend16_s"; break; + case ExtendS32Int64: o << "i64.extend32_s"; break; default: abort(); } incIndent(); diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index ec7809f48..7e122b806 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -31,7 +31,7 @@ namespace wasm { // condition and possible value, and the possible value must // not have side effects (as they would run unconditionally) static bool canTurnIfIntoBrIf(Expression* ifCondition, Expression* brValue, PassOptions& options) { - // if the if isn't even taken, this is all dead code anyhow + // if the if isn't even reached, this is all dead code anyhow if (ifCondition->type == unreachable) return false; if (!brValue) return true; EffectAnalyzer value(options, brValue); @@ -512,7 +512,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> { if (curr->name.is()) { auto* br = list[0]->dynCast<Break>(); // we seek a regular br_if; if the type is unreachable that means it is not - // actually taken, so ignore + // actually reached, so ignore if (br && br->condition && br->name == curr->name && br->type != unreachable) { assert(!br->value); // can't, it would be dropped or last in the block if (BranchUtils::BranchSeeker::countNamed(curr, curr->name) == 1) { diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp index 919784cdf..618f8dead 100644 --- a/src/passes/SimplifyLocals.cpp +++ b/src/passes/SimplifyLocals.cpp @@ -348,7 +348,7 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>> auto* brp = breaks[j].brp; auto* br = (*brp)->cast<Break>(); assert(!br->value); - // if the break is conditional, then we must set the value here - if the break is not taken, we must still have the new value in the local + // if the break is conditional, then we must set the value here - if the break is not reached, we must still have the new value in the local auto* set = (*breakSetLocalPointer)->cast<SetLocal>(); if (br->condition) { br->value = set; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 6df49b162..256cea75c 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -531,6 +531,12 @@ enum ASTNodes { F32ReinterpretI32 = 0xbe, F64ReinterpretI64 = 0xbf, + I32ExtendS8 = 0xc0, + I32ExtendS16 = 0xc1, + I64ExtendS8 = 0xc2, + I64ExtendS16 = 0xc3, + I64ExtendS32 = 0xc4, + AtomicPrefix = 0xfe }; diff --git a/src/wasm-validator.h b/src/wasm-validator.h index 35304d1c9..0851d33e8 100644 --- a/src/wasm-validator.h +++ b/src/wasm-validator.h @@ -78,7 +78,6 @@ struct WasmValidator : public PostWalker<WasmValidator> { std::map<Name, Expression*> breakTargets; std::map<Expression*, BreakInfo> breakInfos; - std::set<Name> namedBreakTargets; // even breaks not taken must not be named if they go to a place that does not exist WasmType returnType = unreachable; // type used in returns diff --git a/src/wasm.h b/src/wasm.h index 0cd5b05e8..d2bea45a1 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -98,6 +98,10 @@ enum UnaryOp { PromoteFloat32, // f32 to f64 DemoteFloat64, // f64 to f32 ReinterpretInt32, ReinterpretInt64, // reinterpret bits to float + // The following sign-extention operators go along with wasm atomics support. + // Extend signed subword-sized integer. This differs from e.g. ExtendSInt32 + // because the input integer is in an i64 value insetad of an i32 value. + ExtendS8Int32, ExtendS16Int32, ExtendS8Int64, ExtendS16Int64, ExtendS32Int64, }; enum BinaryOp { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index f0787600e..68fb293e8 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -670,7 +670,7 @@ void WasmBinaryWriter::visitBreak(Break *curr) { // then either the condition or the value is unreachable, which is // extremely rare, and may require us to make the stack polymorphic // (if the block we branch to has a value, we may lack one as we - // are not a taken branch; the wasm spec on the other hand does + // are not a reachable branch; the wasm spec on the other hand does // presume the br_if emits a value of the right type, even if it // popped unreachable) o << int8_t(BinaryConsts::Unreachable); @@ -683,11 +683,10 @@ void WasmBinaryWriter::visitSwitch(Switch *curr) { recurse(curr->value); } recurse(curr->condition); - if (!BranchUtils::isBranchTaken(curr)) { - // if the branch is not taken, then it's dangerous to emit it, as - // wasm type checking rules are stricter than ours - we tolerate - // an untaken branch to a target with a different value, but not - // wasm. so just don't emit it + if (!BranchUtils::isBranchReachable(curr)) { + // if the branch is not reachable, then it's dangerous to emit it, as + // wasm type checking rules are different, especially in unreachable + // code. so just don't emit that unreachable code. o << int8_t(BinaryConsts::Unreachable); return; } @@ -1049,6 +1048,11 @@ void WasmBinaryWriter::visitUnary(Unary *curr) { case ReinterpretFloat64: o << int8_t(BinaryConsts::I64ReinterpretF64); break; case ReinterpretInt32: o << int8_t(BinaryConsts::F32ReinterpretI32); break; case ReinterpretInt64: o << int8_t(BinaryConsts::F64ReinterpretI64); break; + case ExtendS8Int32: o << int8_t(BinaryConsts::I32ExtendS8); break; + case ExtendS16Int32: o << int8_t(BinaryConsts::I32ExtendS16); break; + case ExtendS8Int64: o << int8_t(BinaryConsts::I64ExtendS8); break; + case ExtendS16Int64: o << int8_t(BinaryConsts::I64ExtendS16); break; + case ExtendS32Int64: o << int8_t(BinaryConsts::I64ExtendS32); break; default: abort(); } if (curr->type == unreachable) { @@ -2679,6 +2683,12 @@ bool WasmBinaryBuilder::maybeVisitUnary(Expression*& out, uint8_t code) { case BinaryConsts::F32ReinterpretI32: curr = allocator.alloc<Unary>(); curr->op = ReinterpretInt32; curr->type = f32; break; case BinaryConsts::F64ReinterpretI64: curr = allocator.alloc<Unary>(); curr->op = ReinterpretInt64; curr->type = f64; break; + case BinaryConsts::I32ExtendS8: curr = allocator.alloc<Unary>(); curr->op = ExtendS8Int32; curr->type = i32; break; + case BinaryConsts::I32ExtendS16: curr = allocator.alloc<Unary>(); curr->op = ExtendS16Int32; curr->type = i32; break; + case BinaryConsts::I64ExtendS8: curr = allocator.alloc<Unary>(); curr->op = ExtendS8Int64; curr->type = i64; break; + case BinaryConsts::I64ExtendS16: curr = allocator.alloc<Unary>(); curr->op = ExtendS16Int64; curr->type = i64; break; + case BinaryConsts::I64ExtendS32: curr = allocator.alloc<Unary>(); curr->op = ExtendS32Int64; curr->type = i64; break; + default: return false; } if (debug) std::cerr << "zz node: Unary" << std::endl; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index fd7c7e7ae..a95b551ea 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -699,7 +699,12 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) { if (op[2] == 0) return makeBinary(s, BINARY_INT_OR_FLOAT(Eq), type); if (op[2] == 'z') return makeUnary(s, type == i32 ? UnaryOp::EqZInt32 : UnaryOp::EqZInt64, type); } - if (op[1] == 'x') return makeUnary(s, op[7] == 'u' ? UnaryOp::ExtendUInt32 : UnaryOp::ExtendSInt32, type); + if (op[1] == 'x') { + if (op[6] == '8') return makeUnary(s, type == i32 ? UnaryOp::ExtendS8Int32 : UnaryOp::ExtendS8Int64, type); + if (op[6] == '1') return makeUnary(s, type == i32 ? UnaryOp::ExtendS16Int32 : UnaryOp::ExtendS16Int64, type); + if (op[6] == '3') return makeUnary(s, UnaryOp::ExtendS32Int64, type); + return makeUnary(s, op[7] == 'u' ? UnaryOp::ExtendUInt32 : UnaryOp::ExtendSInt32, type); + } abort_on(op); } case 'f': { @@ -920,6 +925,8 @@ Expression* SExpressionWasmBuilder::makeUnary(Element& s, UnaryOp op, WasmType t break; } case ExtendSInt32: case ExtendUInt32: + case ExtendS8Int32: case ExtendS16Int32: + case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64: case WrapInt64: case PromoteFloat32: case DemoteFloat64: diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 877232521..5534eb7b9 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -58,7 +58,6 @@ void WasmValidator::visitBlock(Block *curr) { } } breakTargets.erase(curr->name); - namedBreakTargets.erase(curr->name); } if (curr->list.size() > 1) { for (Index i = 0; i < curr->list.size() - 1; i++) { @@ -88,7 +87,6 @@ void WasmValidator::visitLoop(Loop *curr) { if (curr->name.is()) { noteLabelName(curr->name); breakTargets.erase(curr->name); - namedBreakTargets.erase(curr->name); if (breakInfos.count(curr) > 0) { auto& info = breakInfos[curr]; shouldBeEqual(info.arity, Index(0), curr, "breaks to a loop cannot pass a value"); @@ -128,7 +126,6 @@ void WasmValidator::visitIf(If *curr) { } void WasmValidator::noteBreak(Name name, Expression* value, Expression* curr) { - namedBreakTargets.insert(name); WasmType valueType = none; Index arity = 0; if (value) { @@ -433,8 +430,17 @@ void WasmValidator::visitUnary(Unary *curr) { shouldBeTrue(curr->value->type == i64, curr, "i64.eqz input must be i64"); break; } - case ExtendSInt32: shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); break; - case ExtendUInt32: shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); break; + case ExtendSInt32: + case ExtendUInt32: + case ExtendS8Int32: + case ExtendS16Int32: { + shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); break; + } + case ExtendS8Int64: + case ExtendS16Int64: + case ExtendS32Int64: { + shouldBeEqual(curr->value->type, i64, curr, "extend type must be correct"); break; + } case WrapInt64: shouldBeEqual(curr->value->type, i64, curr, "wrap type must be correct"); break; case TruncSFloat32ToInt32: shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); break; case TruncSFloat32ToInt64: shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); break; @@ -550,9 +556,7 @@ void WasmValidator::visitFunction(Function *curr) { if (returnType != unreachable) { shouldBeEqual(curr->result, returnType, curr->body, "function result must match, if function has returns"); } - if (!shouldBeTrue(namedBreakTargets.empty(), curr->body, "all named break targets must exist") && !quiet) { - std::cerr << "(on label " << *namedBreakTargets.begin() << ")\n"; - } + shouldBeTrue(breakTargets.empty(), curr->body, "all named break targets must exist"); returnType = unreachable; labelNames.clear(); } diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index a781e0fa3..6db11cc7d 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -443,7 +443,8 @@ void Unary::finalize() { case SqrtFloat64: type = value->type; break; case EqZInt32: case EqZInt64: type = i32; break; - case ExtendSInt32: case ExtendUInt32: type = i64; break; + case ExtendS8Int32: case ExtendS16Int32: type = i32; break; + case ExtendSInt32: case ExtendUInt32: case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64: type = i64; break; case WrapInt64: type = i32; break; case PromoteFloat32: type = f64; break; case DemoteFloat64: type = f32; break; |