summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/asm2wasm.h18
-rw-r--r--src/binaryen-shell.cpp23
-rw-r--r--src/parsing.h39
-rw-r--r--src/passes/RemoveUnusedBrs.cpp2
-rw-r--r--src/s2wasm-main.cpp11
-rw-r--r--src/s2wasm.h36
-rw-r--r--src/support/bits.cpp22
-rw-r--r--src/support/bits.h6
-rw-r--r--src/wasm-binary.h36
-rw-r--r--src/wasm-interpreter.h136
-rw-r--r--src/wasm-s-parser.h3
-rw-r--r--src/wasm.h66
-rw-r--r--src/wasm2asm.h8
m---------test/spec0
14 files changed, 244 insertions, 162 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 473384f2e..d56bf0cce 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -804,8 +804,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
ret->offset = 0;
ret->align = ret->bytes;
auto ptr = allocator.alloc<Const>();
- ptr->value.type = WasmType::i32; // XXX for wasm64, need 64
- ptr->value.i32 = global.address;
+ ptr->value = Literal(int32_t(global.address)); // XXX for wasm64, need 64
ret->ptr = ptr;
ret->value = process(ast[3]);
ret->type = global.type;
@@ -874,14 +873,11 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
auto ret = allocator.alloc<Const>();
double num = ast[1]->getNumber();
if (isSInteger32(num)) {
- ret->value.type = WasmType::i32;
- ret->value.i32 = toSInteger32(num);
+ ret->value = Literal(int32_t(toSInteger32(num)));
} else if (isUInteger32(num)) {
- ret->value.type = WasmType::i32;
- ret->value.i32 = toUInteger32(num);
+ ret->value = Literal(uint32_t(toUInteger32(num)));
} else {
- ret->value.type = WasmType::f64;
- ret->value.f64 = num;
+ ret->value = Literal(num);
}
ret->type = ret->value.type;
return ret;
@@ -919,8 +915,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
ret->offset = 0;
ret->align = ret->bytes;
auto ptr = allocator.alloc<Const>();
- ptr->value.type = WasmType::i32; // XXX for wasm64, need 64
- ptr->value.i32 = global.address;
+ ptr->value = Literal(int32_t(global.address)); // XXX for wasm64, need 64
ret->ptr = ptr;
ret->type = global.type;
return ret;
@@ -1446,8 +1441,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
unsigned addr = ptr[1]->getInteger();
unsigned shifted = addr << shifts;
auto ret = allocator.alloc<Const>();
- ret->value.type = WasmType::i32;
- ret->value.i32 = shifted;
+ ret->value = Literal(int32_t(shifted));
return (Expression*)ret;
}
abort_on("bad processUnshifted", ptr);
diff --git a/src/binaryen-shell.cpp b/src/binaryen-shell.cpp
index a96716a02..d8277ad80 100644
--- a/src/binaryen-shell.cpp
+++ b/src/binaryen-shell.cpp
@@ -74,8 +74,10 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface {
public:
Memory() {}
void resize(size_t newSize) {
- // Allocate at least this size to get proper alignment: the allocator will
- // usually start allocating page-sized chunks which are properly aligned.
+ // Ensure the smallest allocation is large enough that most allocators
+ // will provide page-aligned storage. This hopefully allows the
+ // interpreter's memory to be as aligned as the memory being simulated,
+ // ensuring that the performance doesn't needlessly degrade.
//
// The code is optimistic this will work until WG21's p0035r0 happens.
const size_t minSize = 1 << 12;
@@ -224,6 +226,19 @@ struct Invocation {
}
};
+static void verify_result(Literal a, Literal b) {
+ if (a == b) return;
+ // accept equal nans if equal in all bits
+ assert(a.type == b.type);
+ if (a.type == f32) {
+ assert(a.reinterpreti32() == b.reinterpreti32());
+ } else if (a.type == f64) {
+ assert(a.reinterpreti64() == b.reinterpreti64());
+ } else {
+ abort();
+ }
+}
+
static void run_asserts(size_t* i, bool* checked, AllocatingModule* wasm,
Element* root,
std::unique_ptr<SExpressionWasmBuilder>* builder,
@@ -300,11 +315,11 @@ static void run_asserts(size_t* i, bool* checked, AllocatingModule* wasm,
->dyn_cast<Const>()
->value;
std::cerr << "seen " << result << ", expected " << expected << '\n';
- assert(expected == result);
+ verify_result(expected, result);
} else {
Literal expected;
std::cerr << "seen " << result << ", expected " << expected << '\n';
- assert(expected == result);
+ verify_result(expected, result);
}
}
if (id == ASSERT_TRAP) assert(trapped);
diff --git a/src/parsing.h b/src/parsing.h
index 1ea0ec254..ed2f65b59 100644
--- a/src/parsing.h
+++ b/src/parsing.h
@@ -29,12 +29,12 @@ namespace wasm {
Expression* parseConst(cashew::IString s, WasmType type, MixedArena& allocator) {
const char *str = s.str;
auto ret = allocator.alloc<Const>();
- ret->type = ret->value.type = type;
+ ret->type = type;
if (isWasmTypeFloat(type)) {
if (s == INFINITY_) {
switch (type) {
- case f32: ret->value.f32 = std::numeric_limits<float>::infinity(); break;
- case f64: ret->value.f64 = std::numeric_limits<double>::infinity(); break;
+ case f32: ret->value = Literal(std::numeric_limits<float>::infinity()); break;
+ case f64: ret->value = Literal(std::numeric_limits<double>::infinity()); break;
default: return nullptr;
}
//std::cerr << "make constant " << str << " ==> " << ret->value << '\n';
@@ -42,8 +42,8 @@ Expression* parseConst(cashew::IString s, WasmType type, MixedArena& allocator)
}
if (s == NEG_INFINITY) {
switch (type) {
- case f32: ret->value.f32 = -std::numeric_limits<float>::infinity(); break;
- case f64: ret->value.f64 = -std::numeric_limits<double>::infinity(); break;
+ case f32: ret->value = Literal(-std::numeric_limits<float>::infinity()); break;
+ case f64: ret->value = Literal(-std::numeric_limits<double>::infinity()); break;
default: return nullptr;
}
//std::cerr << "make constant " << str << " ==> " << ret->value << '\n';
@@ -51,8 +51,8 @@ Expression* parseConst(cashew::IString s, WasmType type, MixedArena& allocator)
}
if (s == NAN_) {
switch (type) {
- case f32: ret->value.f32 = std::nan(""); break;
- case f64: ret->value.f64 = std::nan(""); break;
+ case f32: ret->value = Literal(float(std::nan(""))); break;
+ case f64: ret->value = Literal(double(std::nan(""))); break;
default: return nullptr;
}
//std::cerr << "make constant " << str << " ==> " << ret->value << '\n';
@@ -80,8 +80,7 @@ Expression* parseConst(cashew::IString s, WasmType type, MixedArena& allocator)
}
if (negative) pattern |= 0x80000000U;
if (!isnan(bit_cast<float>(pattern))) pattern |= 1U;
- ret->value.f32 = bit_cast<float>(pattern);
- assert(isnan(ret->value.f32));
+ ret->value = Literal(pattern).castToF32();
break;
}
case f64: {
@@ -95,8 +94,7 @@ Expression* parseConst(cashew::IString s, WasmType type, MixedArena& allocator)
}
if (negative) pattern |= 0x8000000000000000ULL;
if (!isnan(bit_cast<double>(pattern))) pattern |= 1ULL;
- ret->value.f64 = bit_cast<double>(pattern);
- assert(isnan(ret->value.f64));
+ ret->value = Literal(pattern).castToF64();
break;
}
default: return nullptr;
@@ -106,8 +104,8 @@ Expression* parseConst(cashew::IString s, WasmType type, MixedArena& allocator)
}
if (s == NEG_NAN) {
switch (type) {
- case f32: ret->value.f32 = -std::nan(""); break;
- case f64: ret->value.f64 = -std::nan(""); break;
+ case f32: ret->value = Literal(float(-std::nan(""))); break;
+ case f64: ret->value = Literal(double(-std::nan(""))); break;
default: return nullptr;
}
//std::cerr << "make constant " << str << " ==> " << ret->value << '\n';
@@ -122,12 +120,12 @@ Expression* parseConst(cashew::IString s, WasmType type, MixedArena& allocator)
std::istringstream istr(str);
uint32_t temp;
istr >> std::hex >> temp;
- ret->value.i32 = negative ? -temp : temp;
+ ret->value = Literal(negative ? -temp : temp);
} else {
std::istringstream istr(str);
int32_t temp;
istr >> temp;
- ret->value.i32 = temp;
+ ret->value = Literal(temp);
}
break;
}
@@ -138,29 +136,28 @@ Expression* parseConst(cashew::IString s, WasmType type, MixedArena& allocator)
std::istringstream istr(str);
uint64_t temp;
istr >> std::hex >> temp;
- ret->value.i64 = negative ? -temp : temp;
+ ret->value = Literal(negative ? -temp : temp);
} else {
std::istringstream istr(str);
int64_t temp;
istr >> temp;
- ret->value.i64 = temp;
+ ret->value = Literal(temp);
}
break;
}
case f32: {
char *end;
- ret->value.f32 = strtof(str, &end);
- assert(!isnan(ret->value.f32));
+ ret->value = Literal(strtof(str, &end));
break;
}
case f64: {
char *end;
- ret->value.f64 = strtod(str, &end);
- assert(!isnan(ret->value.f64));
+ ret->value = Literal(strtod(str, &end));
break;
}
default: return nullptr;
}
+ assert(ret->value.type == type);
//std::cerr << "make constant " << str << " ==> " << ret->value << '\n';
return ret;
}
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index c7fa43df9..5302c368a 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -37,7 +37,7 @@ struct RemoveUnusedBrs : public WalkerPass<WasmWalker<RemoveUnusedBrs>> {
}
return;
}
- if (curr->type != none) return; // already has a returned value
+ if (isConcreteWasmType(curr->type)) return; // already has a returned value
// an if_else that indirectly returns a value by breaking to the same target can potentially remove both breaks, and break outside once
auto getLast = [](Expression *side) -> Expression* {
Block* b = side->dyn_cast<Block>();
diff --git a/src/s2wasm-main.cpp b/src/s2wasm-main.cpp
index c3f3d0a2f..e75147b75 100644
--- a/src/s2wasm-main.cpp
+++ b/src/s2wasm-main.cpp
@@ -46,6 +46,11 @@ int main(int argc, const char *argv[]) {
[](Options *o, const std::string &argument) {
o->extra["global-base"] = argument;
})
+ .add("--allocate-stack", "-s", "Size of the user stack in linear memory",
+ Options::Arguments::One,
+ [](Options *o, const std::string &argument) {
+ o->extra["stack-allocation"] = argument;
+ })
.add_positional("INFILE", Options::Arguments::One,
[](Options *o, const std::string &argument) {
o->extra["infile"] = argument;
@@ -59,9 +64,13 @@ int main(int argc, const char *argv[]) {
size_t globalBase = options.extra.find("global-base") != options.extra.end()
? std::stoull(options.extra["global-base"])
: 1;
+ size_t stackAllocation =
+ options.extra.find("stack-allocation") != options.extra.end()
+ ? std::stoull(options.extra["stack-allocation"])
+ : 0;
if (options.debug) std::cerr << "Global base " << globalBase << '\n';
S2WasmBuilder s2wasm(wasm, input.c_str(), options.debug, globalBase,
- ignoreUnknownSymbols);
+ stackAllocation, ignoreUnknownSymbols);
if (options.debug) std::cerr << "Emscripten gluing..." << std::endl;
std::stringstream meta;
diff --git a/src/s2wasm.h b/src/s2wasm.h
index abfb4a92d..1e0a28b43 100644
--- a/src/s2wasm.h
+++ b/src/s2wasm.h
@@ -42,7 +42,8 @@ class S2WasmBuilder {
public:
S2WasmBuilder(AllocatingModule& wasm, const char* input, bool debug,
- size_t globalBase, bool ignoreUnknownSymbols)
+ size_t globalBase, size_t stackAllocation,
+ bool ignoreUnknownSymbols)
: wasm(wasm),
allocator(wasm.allocator),
debug(debug),
@@ -52,8 +53,13 @@ class S2WasmBuilder {
s = input;
scan();
s = input;
- prepare();
+ // Place the stack pointer at the bottom of the linear memory, to keep its
+ // address small (and thus with a small encoding).
+ placeStackPointer(stackAllocation);
process();
+ // Place the stack after the user's static data, to keep those addresses
+ // small.
+ if (stackAllocation) placeStack(stackAllocation);
fix();
}
@@ -370,10 +376,30 @@ class S2WasmBuilder {
}
}
- void prepare() {
+ void placeStackPointer(size_t stackAllocation) {
assert(nextStatic == globalBase); // we are the first allocation
+ // Allocate space for the stack pointer
staticAddresses["__stack_pointer"] = nextStatic;
- nextStatic += 4;
+ const size_t pointerSize = 4;
+ if (stackAllocation) {
+ // If we are also allocating the stack, initialize the stack pointer to
+ // point to one past-the-end of the stack allocation.
+ auto* raw = new uint32_t;
+ relocations.emplace_back(raw, ".stack", stackAllocation);
+ assert(wasm.memory.segments.size() == 0);
+ addressSegments[nextStatic] = wasm.memory.segments.size();
+ wasm.memory.segments.emplace_back(
+ nextStatic, reinterpret_cast<char*>(raw), pointerSize);
+ wasm.memory.initial = nextStatic + pointerSize;
+ }
+ nextStatic += pointerSize;
+ }
+
+ void placeStack(size_t stackAllocation) {
+ // Allocate space for a user stack. It starts zeroed-out so needs no segment
+ staticAddresses[".stack"] = nextStatic;
+ nextStatic += stackAllocation;
+ wasm.memory.initial = nextStatic;
}
void process() {
@@ -744,7 +770,7 @@ class S2WasmBuilder {
// may be a relocation
auto curr = allocator.alloc<Const>();
curr->type = curr->value.type = i32;
- getConst((uint32_t*)&curr->value.i32);
+ getConst((uint32_t*)curr->value.geti32Ptr());
setOutput(curr, assign);
} else {
cashew::IString str = getStr();
diff --git a/src/support/bits.cpp b/src/support/bits.cpp
index 7bf00e595..c1de4da8b 100644
--- a/src/support/bits.cpp
+++ b/src/support/bits.cpp
@@ -17,8 +17,10 @@
#define wasm_support_bits_definitions
#include "support/bits.h"
+namespace wasm {
+
template<>
-int wasm::PopCount<uint8_t>(uint8_t v) {
+int PopCount<uint8_t>(uint8_t v) {
// Small table lookup.
static const uint8_t tbl[32] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
@@ -28,12 +30,12 @@ int wasm::PopCount<uint8_t>(uint8_t v) {
}
template<>
-int wasm::PopCount<uint16_t>(uint16_t v) {
+int PopCount<uint16_t>(uint16_t v) {
return PopCount((uint8_t)(v & 0xff)) + PopCount((uint8_t)(v >> 8));
}
template<>
-int wasm::PopCount<uint32_t>(uint32_t v) {
+int PopCount<uint32_t>(uint32_t v) {
// See Stanford bithacks, counting bits set in parallel, "best method":
// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
v = v - ((v >> 1) & 0x55555555);
@@ -42,12 +44,12 @@ int wasm::PopCount<uint32_t>(uint32_t v) {
}
template<>
-int wasm::PopCount<uint64_t>(uint64_t v) {
+int PopCount<uint64_t>(uint64_t v) {
return PopCount((uint32_t)v) + PopCount((uint32_t)(v >> 32));
}
template<>
-uint32_t wasm::BitReverse<uint32_t>(uint32_t v) {
+uint32_t BitReverse<uint32_t>(uint32_t v) {
// See Hacker's Delight, first edition, figure 7-1.
v = ((v & 0x55555555) << 1) | ((v >> 1) & 0x55555555);
v = ((v & 0x33333333) << 2) | ((v >> 2) & 0x33333333);
@@ -57,7 +59,7 @@ uint32_t wasm::BitReverse<uint32_t>(uint32_t v) {
}
template<>
-int wasm::CountTrailingZeroes<uint32_t>(uint32_t v) {
+int CountTrailingZeroes<uint32_t>(uint32_t v) {
// See Stanford bithacks, count the consecutive zero bits (trailing) on the
// right with multiply and lookup:
// http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
@@ -69,13 +71,13 @@ int wasm::CountTrailingZeroes<uint32_t>(uint32_t v) {
}
template<>
-int wasm::CountTrailingZeroes<uint64_t>(uint64_t v) {
+int CountTrailingZeroes<uint64_t>(uint64_t v) {
return (uint32_t)v ? CountTrailingZeroes((uint32_t)v)
: 32 + CountTrailingZeroes((uint32_t)(v >> 32));
}
template<>
-int wasm::CountLeadingZeroes<uint32_t>(uint32_t v) {
+int CountLeadingZeroes<uint32_t>(uint32_t v) {
// See Stanford bithacks, find the log base 2 of an N-bit integer in
// O(lg(N)) operations with multiply and lookup:
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn
@@ -92,7 +94,9 @@ int wasm::CountLeadingZeroes<uint32_t>(uint32_t v) {
}
template<>
-int wasm::CountLeadingZeroes<uint64_t>(uint64_t v) {
+int CountLeadingZeroes<uint64_t>(uint64_t v) {
return v >> 32 ? CountLeadingZeroes((uint32_t)(v >> 32))
: 32 + CountLeadingZeroes((uint32_t)v);
}
+
+} // namespace wasm
diff --git a/src/support/bits.h b/src/support/bits.h
index 3049a2cf1..bbafb29d4 100644
--- a/src/support/bits.h
+++ b/src/support/bits.h
@@ -53,15 +53,15 @@ extern template int CountLeadingZeroes(uint64_t);
// Convenience signed -> unsigned. It usually doesn't make much sense to use bit
// functions on signed types.
template <typename T>
-inline int PopCount(T v) {
+int PopCount(T v) {
return PopCount(typename std::make_unsigned<T>::type(v));
}
template <typename T>
-inline int CountTrailingZeroes(T v) {
+int CountTrailingZeroes(T v) {
return CountTrailingZeroes(typename std::make_unsigned<T>::type(v));
}
template <typename T>
-inline int CountLeadingZeroes(T v) {
+int CountLeadingZeroes(T v) {
return CountLeadingZeroes(typename std::make_unsigned<T>::type(v));
}
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 1cc6f0673..440689ea0 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -647,7 +647,14 @@ public:
return breakStack.size() - 1 - i;
}
}
+printf("bad!\n");
std::cerr << "bad break: " << name << std::endl;
+
+
+
+assert(0);
+
+
abort();
}
@@ -782,10 +789,10 @@ public:
recurse(curr->value);
}
void visitConst(Const *curr) {
- if (debug) std::cerr << "zz node: Const" << std::endl;
+ if (debug) std::cerr << "zz node: Const" << curr << " : " << curr->type << std::endl;
switch (curr->type) {
case i32: {
- uint32_t value = curr->value.i32;
+ uint32_t value = curr->value.geti32();
if (value <= 255) {
o << int8_t(BinaryConsts::I8Const) << uint8_t(value);
break;
@@ -794,19 +801,20 @@ public:
break;
}
case i64: {
- o << int8_t(BinaryConsts::I64Const) << curr->value.i64;
+ o << int8_t(BinaryConsts::I64Const) << curr->value.geti64();
break;
}
case f32: {
- o << int8_t(BinaryConsts::F32Const) << curr->value.f32;
+ o << int8_t(BinaryConsts::F32Const) << curr->value.getf32();
break;
}
case f64: {
- o << int8_t(BinaryConsts::F64Const) << curr->value.f64;
+ o << int8_t(BinaryConsts::F64Const) << curr->value.getf64();
break;
}
default: abort();
}
+ if (debug) std::cerr << "zz const node done.\n";
}
void visitUnary(Unary *curr) {
if (debug) std::cerr << "zz node: Unary" << std::endl;
@@ -843,7 +851,7 @@ public:
void visitBinary(Binary *curr) {
if (debug) std::cerr << "zz node: Binary" << std::endl;
#define TYPED_CODE(code) { \
- switch (curr->left->type) { \
+ switch (getReachableWasmType(curr->left->type, curr->right->type)) { \
case i32: o << int8_t(BinaryConsts::I32##code); break; \
case i64: o << int8_t(BinaryConsts::I64##code); break; \
case f32: o << int8_t(BinaryConsts::F32##code); break; \
@@ -853,7 +861,7 @@ public:
break; \
}
#define INT_TYPED_CODE(code) { \
- switch (curr->left->type) { \
+ switch (getReachableWasmType(curr->left->type, curr->right->type)) { \
case i32: o << int8_t(BinaryConsts::I32##code); break; \
case i64: o << int8_t(BinaryConsts::I64##code); break; \
default: abort(); \
@@ -861,7 +869,7 @@ public:
break; \
}
#define FLOAT_TYPED_CODE(code) { \
- switch (curr->left->type) { \
+ switch (getReachableWasmType(curr->left->type, curr->right->type)) { \
case f32: o << int8_t(BinaryConsts::F32##code); break; \
case f64: o << int8_t(BinaryConsts::F64##code); break; \
default: abort(); \
@@ -1480,14 +1488,14 @@ public:
}
bool maybeVisitImpl(Const *curr, uint8_t code) {
switch (code) {
- case BinaryConsts::I8Const: curr->value.i32 = getInt8(); curr->type = i32; break;
- case BinaryConsts::I32Const: curr->value.i32 = getInt32(); curr->type = i32; break;
- case BinaryConsts::I64Const: curr->value.i64 = getInt64(); curr->type = i64; break;
- case BinaryConsts::F32Const: curr->value.f32 = getFloat32(); curr->type = f32; break;
- case BinaryConsts::F64Const: curr->value.f64 = getFloat64(); curr->type = f64; break;
+ case BinaryConsts::I8Const: curr->value = Literal(int32_t(getInt8())); break;
+ case BinaryConsts::I32Const: curr->value = Literal(getInt32()); break;
+ case BinaryConsts::I64Const: curr->value = Literal(getInt64()); break;
+ case BinaryConsts::F32Const: curr->value = Literal(getFloat32()); break;
+ case BinaryConsts::F64Const: curr->value = Literal(getFloat64()); break;
default: return false;
}
- curr->value.type = curr->type;
+ curr->type = curr->value.type;
if (debug) std::cerr << "zz node: Const" << std::endl;
return true;
}
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index e0006f37b..022dd04bc 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -24,6 +24,7 @@
#define wasm_wasm_interpreter_h
#include <limits.h>
+#include <sstream>
#include "support/bits.h"
#include "wasm.h"
@@ -378,13 +379,7 @@ private:
case Clz: return Literal((int32_t)CountLeadingZeroes(v));
case Ctz: return Literal((int32_t)CountTrailingZeroes(v));
case Popcnt: return Literal((int32_t)PopCount(v));
- case ReinterpretInt: {
- float v = value.reinterpretf32();
- if (isnan(v)) {
- return Literal(Literal(value.geti32() | 0x7f800000).reinterpretf32());
- }
- return Literal(value.reinterpretf32());
- }
+ case ReinterpretInt: return value.castToF32();
case ExtendSInt32: return Literal(int64_t(value.geti32()));
case ExtendUInt32: return Literal(uint64_t((uint32_t)value.geti32()));
case ConvertUInt32: return curr->type == f32 ? Literal(float(uint32_t(value.geti32()))) : Literal(double(uint32_t(value.geti32())));
@@ -399,9 +394,7 @@ private:
case Ctz: return Literal((int64_t)CountTrailingZeroes(v));
case Popcnt: return Literal((int64_t)PopCount(v));
case WrapInt64: return Literal(int32_t(value.geti64()));
- case ReinterpretInt: {
- return Literal(value.reinterpretf64());
- }
+ case ReinterpretInt: return value.castToF64();
case ConvertUInt64: return curr->type == f32 ? Literal(float((uint64_t)value.geti64())) : Literal(double((uint64_t)value.geti64()));
case ConvertSInt64: return curr->type == f32 ? Literal(float(value.geti64())) : Literal(double(value.geti64()));
default: abort();
@@ -411,8 +404,9 @@ private:
float v = value.getf32();
float ret;
switch (curr->op) {
- case Neg: ret = -v; break;
- case Abs: ret = std::abs(v); break;
+ // operate on bits directly, to avoid signalling bit being set on a float
+ case Neg: return Literal(value.reinterpreti32() ^ 0x80000000).castToF32(); break;
+ case Abs: return Literal(value.reinterpreti32() & 0x7fffffff).castToF32(); break;
case Ceil: ret = std::ceil(v); break;
case Floor: ret = std::floor(v); break;
case Trunc: ret = std::trunc(v); break;
@@ -430,8 +424,9 @@ private:
double v = value.getf64();
double ret;
switch (curr->op) {
- case Neg: ret = -v; break;
- case Abs: ret = std::abs(v); break;
+ // operate on bits directly, to avoid signalling bit being set on a float
+ case Neg: return Literal(uint64_t((value.reinterpreti64() ^ 0x8000000000000000ULL))).castToF64(); break;
+ case Abs: return Literal(uint64_t(value.reinterpreti64() & 0x7fffffffffffffffULL)).castToF64(); break;
case Ceil: ret = std::ceil(v); break;
case Floor: ret = std::floor(v); break;
case Trunc: ret = std::trunc(v); break;
@@ -459,28 +454,29 @@ private:
assert(left.type == curr->left->type);
assert(right.type == curr->right->type);
if (left.type == i32) {
- int32_t l = left.geti32(), r = right.geti32();
+ uint32_t l = left.geti32(), r = right.geti32();
+ int32_t l_signed = l, r_signed = r;
switch (curr->op) {
case Add: return Literal(l + r);
case Sub: return Literal(l - r);
case Mul: return Literal(l * r);
case DivS: {
- if (r == 0) trap("i32.div_s by 0");
- if (l == INT32_MIN && r == -1) trap("i32.div_s overflow"); // signed division overflow
- return Literal(l / r);
+ if (r_signed == 0) trap("i32.div_s by 0");
+ if (l_signed == INT32_MIN && r_signed == -1) trap("i32.div_s overflow"); // signed division overflow
+ return Literal(l_signed / r_signed);
}
case DivU: {
if (r == 0) trap("i32.div_u by 0");
- return Literal(int32_t(uint32_t(l) / uint32_t(r)));
+ return Literal(l / r);
}
case RemS: {
- if (r == 0) trap("i32.rem_s by 0");
- if (l == INT32_MIN && r == -1) return Literal(int32_t(0));
- return Literal(l % r);
+ if (r_signed == 0) trap("i32.rem_s by 0");
+ if (l_signed == INT32_MIN && r_signed == -1) return Literal(int32_t(0));
+ return Literal(l_signed % r_signed);
}
case RemU: {
if (r == 0) trap("i32.rem_u by 0");
- return Literal(int32_t(uint32_t(l) % uint32_t(r)));
+ return Literal(l % r);
}
case And: return Literal(l & r);
case Or: return Literal(l | r);
@@ -491,47 +487,48 @@ private:
}
case ShrU: {
r = r & 31;
- return Literal(int32_t(uint32_t(l) >> uint32_t(r)));
+ return Literal(l >> r);
}
case ShrS: {
- r = r & 31;
- return Literal(l >> r);
+ r_signed = r_signed & 31;
+ return Literal(l_signed >> r_signed);
}
case Eq: return Literal(l == r);
case Ne: return Literal(l != r);
- case LtS: return Literal(l < r);
- case LtU: return Literal(uint32_t(l) < uint32_t(r));
- case LeS: return Literal(l <= r);
- case LeU: return Literal(uint32_t(l) <= uint32_t(r));
- case GtS: return Literal(l > r);
- case GtU: return Literal(uint32_t(l) > uint32_t(r));
- case GeS: return Literal(l >= r);
- case GeU: return Literal(uint32_t(l) >= uint32_t(r));
+ case LtS: return Literal(l_signed < r_signed);
+ case LtU: return Literal(l < r);
+ case LeS: return Literal(l_signed <= r_signed);
+ case LeU: return Literal(l <= r);
+ case GtS: return Literal(l_signed > r_signed);
+ case GtU: return Literal(l > r);
+ case GeS: return Literal(l_signed >= r_signed);
+ case GeU: return Literal(l >= r);
default: abort();
}
} else if (left.type == i64) {
- int64_t l = left.geti64(), r = right.geti64();
+ uint64_t l = left.geti64(), r = right.geti64();
+ int64_t l_signed = l, r_signed = r;
switch (curr->op) {
case Add: return Literal(l + r);
case Sub: return Literal(l - r);
case Mul: return Literal(l * r);
case DivS: {
- if (r == 0) trap("i64.div_s by 0");
- if (l == LLONG_MIN && r == -1) trap("i64.div_s overflow"); // signed division overflow
- return Literal(l / r);
+ if (r_signed == 0) trap("i64.div_s by 0");
+ if (l_signed == LLONG_MIN && r_signed == -1LL) trap("i64.div_s overflow"); // signed division overflow
+ return Literal(l_signed / r_signed);
}
case DivU: {
if (r == 0) trap("i64.div_u by 0");
- return Literal(int64_t(uint64_t(l) / uint64_t(r)));
+ return Literal(l / r);
}
case RemS: {
- if (r == 0) trap("i64.rem_s by 0");
- if (l == LLONG_MIN && r == -1) return Literal(int64_t(0));
- return Literal(l % r);
+ if (r_signed == 0) trap("i64.rem_s by 0");
+ if (l_signed == LLONG_MIN && r_signed == -1LL) return Literal(int64_t(0));
+ return Literal(l_signed % r_signed);
}
case RemU: {
if (r == 0) trap("i64.rem_u by 0");
- return Literal(int64_t(uint64_t(l) % uint64_t(r)));
+ return Literal(l % r);
}
case And: return Literal(l & r);
case Or: return Literal(l | r);
@@ -542,22 +539,22 @@ private:
}
case ShrU: {
r = r & 63;
- return Literal(int64_t(uint64_t(l) >> uint64_t(r)));
+ return Literal(l >> r);
}
case ShrS: {
- r = r & 63;
- return Literal(l >> r);
+ r_signed = r_signed & 63;
+ return Literal(l_signed >> r_signed);
}
case Eq: return Literal(l == r);
case Ne: return Literal(l != r);
- case LtS: return Literal(l < r);
- case LtU: return Literal(uint64_t(l) < uint64_t(r));
- case LeS: return Literal(l <= r);
- case LeU: return Literal(uint64_t(l) <= uint64_t(r));
- case GtS: return Literal(l > r);
- case GtU: return Literal(uint64_t(l) > uint64_t(r));
- case GeS: return Literal(l >= r);
- case GeU: return Literal(uint64_t(l) >= uint64_t(r));
+ case LtS: return Literal(l_signed < r_signed);
+ case LtU: return Literal(l < r);
+ case LeS: return Literal(l_signed <= r_signed);
+ case LeU: return Literal(l <= r);
+ case GtS: return Literal(l_signed > r_signed);
+ case GtU: return Literal(l > r);
+ case GeS: return Literal(l_signed >= r_signed);
+ case GeU: return Literal(l >= r);
default: abort();
}
} else if (left.type == f32) {
@@ -568,10 +565,8 @@ private:
case Sub: ret = l - r; break;
case Mul: ret = l * r; break;
case Div: ret = l / r; break;
- case CopySign: {
- ret = std::copysign(l, r);
- return Literal(ret);
- }
+ // operate on bits directly, to avoid signalling bit being set on a float
+ case CopySign: return Literal((left.reinterpreti32() & 0x7fffffff) | (right.reinterpreti32() & 0x80000000)).castToF32(); break;
case Min: {
if (l == r && l == 0) ret = 1/l < 0 ? l : r;
else ret = std::min(l, r);
@@ -599,10 +594,8 @@ private:
case Sub: ret = l - r; break;
case Mul: ret = l * r; break;
case Div: ret = l / r; break;
- case CopySign: {
- ret = std::copysign(l, r);
- return Literal(ret);
- }
+ // operate on bits directly, to avoid signalling bit being set on a float
+ case CopySign: return Literal((left.reinterpreti64() & 0x7fffffffffffffffUL) | (right.reinterpreti64() & 0x8000000000000000UL)).castToF64(); break;
case Min: {
if (l == r && l == 0) ret = 1/l < 0 ? l : r;
else ret = std::min(l, r);
@@ -775,14 +768,21 @@ private:
size_t memorySize;
- template<class LS>
- size_t getFinalAddress(LS *curr, Literal ptr) {
+ template <class LS>
+ size_t getFinalAddress(LS* curr, Literal ptr) {
+ auto trapIfGt = [this](size_t lhs, size_t rhs, const char* msg) {
+ if (lhs > rhs) {
+ std::stringstream ss;
+ ss << msg << ": " << lhs << " > " << rhs;
+ externalInterface->trap(ss.str().c_str());
+ }
+ };
uint64_t addr = ptr.type == i32 ? ptr.geti32() : ptr.geti64();
- if (memorySize < curr->offset) externalInterface->trap("offset > memory");
- if (addr > memorySize - curr->offset) externalInterface->trap("final > memory");
+ trapIfGt(curr->offset, memorySize, "offset > memory");
+ trapIfGt(addr, memorySize - curr->offset, "final > memory");
addr += curr->offset;
- if (curr->bytes > memorySize) externalInterface->trap("bytes > memory");
- if (addr > memorySize - curr->bytes) externalInterface->trap("highest > memory");
+ trapIfGt(curr->bytes, memorySize, "bytes > memory");
+ trapIfGt(addr, memorySize - curr->bytes, "highest > memory");
return addr;
}
diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h
index 61f3be3c0..1c704ac74 100644
--- a/src/wasm-s-parser.h
+++ b/src/wasm-s-parser.h
@@ -796,6 +796,9 @@ private:
if (ret->list.size() > 0) {
ret->type = ret->list.back()->type;
}
+ // Note that we do not name these implicit/synthetic blocks. They
+ // are the effects of syntactic sugar, and nothing can branch to
+ // them anyhow.
return ret;
}
diff --git a/src/wasm.h b/src/wasm.h
index 6f889737b..c030d9d60 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -138,35 +138,58 @@ inline WasmType getReachableWasmType(WasmType a, WasmType b) {
return a != unreachable ? a : b;
}
+inline bool isConcreteWasmType(WasmType type) {
+ return type != none && type != unreachable;
+}
+
// Literals
-struct Literal {
+class Literal {
+public:
WasmType type;
+
+private:
+ // store only integers, whose bits are deterministic. floats
+ // can have their signalling bit set, for example.
union {
int32_t i32;
int64_t i64;
- float f32;
- double f64;
};
- Literal() : Literal(WasmType::none) {}
- explicit Literal(WasmType type) : type(type) { memset(&f64, 0, sizeof(f64)); }
+public:
+ Literal() : type(WasmType::none), i64(0) {}
+ explicit Literal(WasmType type) : type(type), i64(0) {}
explicit Literal(int32_t init) : type(WasmType::i32), i32(init) {}
explicit Literal(uint32_t init) : type(WasmType::i32), i32(init) {}
explicit Literal(int64_t init) : type(WasmType::i64), i64(init) {}
explicit Literal(uint64_t init) : type(WasmType::i64), i64(init) {}
- explicit Literal(float init) : type(WasmType::f32), f32(init) {}
- explicit Literal(double init) : type(WasmType::f64), f64(init) {}
+ explicit Literal(float init) : type(WasmType::f32), i32(bit_cast<int32_t>(init)) {}
+ explicit Literal(double init) : type(WasmType::f64), i64(bit_cast<int64_t>(init)) {}
+
+ Literal castToF32() {
+ assert(type == WasmType::i32);
+ Literal ret(i32);
+ ret.type = f32;
+ return ret;
+ }
+ Literal castToF64() {
+ assert(type == WasmType::i64);
+ Literal ret(i64);
+ ret.type = f64;
+ return ret;
+ }
int32_t geti32() { assert(type == WasmType::i32); return i32; }
int64_t geti64() { assert(type == WasmType::i64); return i64; }
- float getf32() { assert(type == WasmType::f32); return f32; }
- double getf64() { assert(type == WasmType::f64); return f64; }
+ float getf32() { assert(type == WasmType::f32); return bit_cast<float>(i32); }
+ double getf64() { assert(type == WasmType::f64); return bit_cast<double>(i64); }
+
+ int32_t* geti32Ptr() { assert(type == WasmType::i32); return &i32; } // careful!
int32_t reinterpreti32() { assert(type == WasmType::f32); return i32; }
int64_t reinterpreti64() { assert(type == WasmType::f64); return i64; }
- float reinterpretf32() { assert(type == WasmType::i32); return f32; }
- double reinterpretf64() { assert(type == WasmType::i64); return f64; }
+ float reinterpretf32() { assert(type == WasmType::i32); return bit_cast<float>(i32); }
+ double reinterpretf64() { assert(type == WasmType::i64); return bit_cast<double>(i64); }
int64_t getInteger() {
switch (type) {
@@ -178,8 +201,8 @@ struct Literal {
double getFloat() {
switch (type) {
- case WasmType::f32: return f32;
- case WasmType::f64: return f64;
+ case WasmType::f32: return getf32();
+ case WasmType::f64: return getf64();
default: abort();
}
}
@@ -189,10 +212,9 @@ struct Literal {
switch (type) {
case WasmType::none: return true;
case WasmType::i32: return i32 == other.i32;
+ case WasmType::f32: return getf32() == other.getf32();
case WasmType::i64: return i64 == other.i64;
- // reinterpret floating-point, to avoid nan != nan
- case WasmType::f32: return reinterpreti32() == other.reinterpreti32();
- case WasmType::f64: return reinterpreti64() == other.reinterpreti64();
+ case WasmType::f64: return getf64() == other.getf64();
default: abort();
}
}
@@ -244,8 +266,8 @@ struct Literal {
case none: o << "?"; break;
case WasmType::i32: o << literal.i32; break;
case WasmType::i64: o << literal.i64; break;
- case WasmType::f32: literal.printFloat(o, literal.f32); break;
- case WasmType::f64: literal.printDouble(o, literal.f64); break;
+ case WasmType::f32: literal.printFloat(o, literal.getf32()); break;
+ case WasmType::f64: literal.printDouble(o, literal.getf64()); break;
default: WASM_UNREACHABLE();
}
restoreNormalColor(o);
@@ -479,7 +501,9 @@ public:
class Break : public Expression {
public:
- Break() : Expression(BreakId), condition(nullptr), value(nullptr) {}
+ Break() : Expression(BreakId), condition(nullptr), value(nullptr) {
+ type = unreachable;
+ }
Expression *condition;
Name name;
@@ -908,7 +932,9 @@ class Return : public Expression {
public:
Expression *value;
- Return() : Expression(ReturnId), value(nullptr) {}
+ Return() : Expression(ReturnId), value(nullptr) {
+ type = unreachable;
+ }
std::ostream& doPrint(std::ostream &o, unsigned indent) {
printOpening(o, "return");
diff --git a/src/wasm2asm.h b/src/wasm2asm.h
index 76ccbf8b6..c87e81eec 100644
--- a/src/wasm2asm.h
+++ b/src/wasm2asm.h
@@ -915,21 +915,21 @@ Ref Wasm2AsmBuilder::processFunctionBody(Expression* curr, IString result) {
}
Ref visitConst(Const *curr) {
switch (curr->type) {
- case i32: return ValueBuilder::makeInt(curr->value.i32);
+ case i32: return ValueBuilder::makeInt(curr->value.geti32());
case f32: {
Ref ret = ValueBuilder::makeCall(MATH_FROUND);
Const fake;
- fake.value = Literal(double(curr->value.f32));
+ fake.value = Literal(double(curr->value.getf32()));
fake.type = f64;
ret[2]->push_back(visitConst(&fake));
return ret;
}
case f64: {
- double d = curr->value.f64;
+ double d = curr->value.getf64();
if (d == 0 && std::signbit(d)) { // negative zero
return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeUnary(MINUS, ValueBuilder::makeDouble(0)));
}
- return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeDouble(curr->value.f64));
+ return ValueBuilder::makeUnary(PLUS, ValueBuilder::makeDouble(curr->value.getf64()));
}
default: abort();
}
diff --git a/test/spec b/test/spec
-Subproject b6284fdbf37c4055d375250d8b0b2e98a2c5b94
+Subproject edd932d4700caddc40950d1405afc986eac2e4d