summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ast/branch-utils.h22
-rw-r--r--src/ast_utils.h2
-rw-r--r--src/passes/OptimizeInstructions.cpp2
-rw-r--r--src/passes/Print.cpp5
-rw-r--r--src/passes/RemoveUnusedBrs.cpp4
-rw-r--r--src/passes/SimplifyLocals.cpp2
-rw-r--r--src/wasm-binary.h6
-rw-r--r--src/wasm-validator.h1
-rw-r--r--src/wasm.h4
-rw-r--r--src/wasm/wasm-binary.cpp22
-rw-r--r--src/wasm/wasm-s-parser.cpp9
-rw-r--r--src/wasm/wasm-validator.cpp20
-rw-r--r--src/wasm/wasm.cpp3
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;