Skip to content

More wasm-only opts #727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion auto_update_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
# test mem init importing
open('a.mem', 'wb').write(asm)
cmd += ['--mem-init=a.mem']
if 'i64' in asm:
if 'i64' in asm or 'wasm-only' in asm:
cmd += ['--wasm-only']
print '..', asm, wasm
print ' ', ' '.join(cmd)
Expand Down
2 changes: 1 addition & 1 deletion check.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ def minify_check(wast, verify_final_result=True):
# test mem init importing
open('a.mem', 'wb').write(asm)
cmd += ['--mem-init=a.mem']
if 'i64' in asm:
if 'i64' in asm or 'wasm-only' in asm:
cmd += ['--wasm-only']
wasm = os.path.join('test', wasm)
print '..', asm, wasm
Expand Down
174 changes: 105 additions & 69 deletions src/asm2wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ Name I64("i64"),
I64_SHL("i64_shl"),
I64_ASHR("i64_ashr"),
I64_LSHR("i64_lshr"),
I64_LOAD("i64_load"),
I64_STORE("i64_store"),
I64_EQ("i64_eq"),
I64_NE("i64_ne"),
I64_ULE("i64_ule"),
Expand Down Expand Up @@ -87,7 +85,21 @@ Name I64("i64"),
I64S_REM("i64s-rem"),
I64U_REM("i64u-rem"),
I64S_DIV("i64s-div"),
I64U_DIV("i64u-div");
I64U_DIV("i64u-div"),
LOAD1("load1"),
LOAD2("load2"),
LOAD4("load4"),
LOAD8("load8"),
LOADF("loadf"),
LOADD("loadd"),
STORE1("store1"),
STORE2("store2"),
STORE4("store4"),
STORE8("store8"),
STOREF("storef"),
STORED("stored"),
I32_BC2F("i32_bc2f"),
I32_BC2I("i32_bc2i");

// Utilities

Expand Down Expand Up @@ -421,10 +433,6 @@ class Asm2WasmBuilder {
return -1; // avoid warning
}

bool maybeWasmInt64Intrinsic(Name name) {
return strncmp(name.str, "i64", 3) == 0;
}

std::map<unsigned, Ref> tempNums;

Literal checkLiteral(Ref ast) {
Expand Down Expand Up @@ -802,7 +810,6 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
} else if (key == GET_TEMP_RET0) {
getTempRet0 = value;
}
assert(wasm.checkFunction(value));
if (exported.count(key) > 0) {
// asm.js allows duplicate exports, but not wasm. use the last, like asm.js
exported[key]->value = value;
Expand Down Expand Up @@ -864,10 +871,6 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
void visitCall(Call* curr) {
if (!getModule()->checkFunction(curr->target)) {
std::cerr << "invalid call target: " << curr->target << '\n';
if (parent->maybeWasmInt64Intrinsic(curr->target)) {
std::cerr << " - perhaps this is a wasm-only i64() method, and you should run asm2wasm with --wasm-only?\n";
if (parent->wasmOnly) std::cerr << " - wait, you *did*. so this is an internal compiler error, please file an issue!\n";
}
WASM_UNREACHABLE();
}
auto result = getModule()->getFunction(curr->target)->result;
Expand Down Expand Up @@ -1525,66 +1528,99 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
}
return ret;
}
if (wasmOnly && maybeWasmInt64Intrinsic(name)) {
if (wasmOnly) {
auto num = ast[2]->size();
if (num == 1) {
auto* value = process(ast[2][0]);
if (name == I64) {
// no-op "coercion" / "cast", although we also tolerate i64(0) for constants that fit in i32
if (value->type == i32) {
return builder.makeConst(Literal(int64_t(value->cast<Const>()->value.geti32())));
} else {
fixCallType(value, i64);
return value;
switch (name.str[0]) {
case 'l': {
auto align = num == 2 ? ast[2][1][1]->getInteger() : 0;
if (name == LOAD1) return builder.makeLoad(1, true, 0, 1, process(ast[2][0]), i32);
if (name == LOAD2) return builder.makeLoad(2, true, 0, indexOr(align, 2), process(ast[2][0]), i32);
if (name == LOAD4) return builder.makeLoad(4, true, 0, indexOr(align, 4), process(ast[2][0]), i32);
if (name == LOAD8) return builder.makeLoad(8, true, 0, indexOr(align, 8), process(ast[2][0]), i64);
if (name == LOADF) return builder.makeLoad(4, true, 0, indexOr(align, 4), process(ast[2][0]), f32);
if (name == LOADD) return builder.makeLoad(8, true, 0, indexOr(align, 8), process(ast[2][0]), f64);
break;
}
case 's': {
auto align = num == 3 ? ast[2][2][1]->getInteger() : 0;
if (name == STORE1) return builder.makeStore(1, 0, 1, process(ast[2][0]), process(ast[2][1]), i32);
if (name == STORE2) return builder.makeStore(2, 0, indexOr(align, 2), process(ast[2][0]), process(ast[2][1]), i32);
if (name == STORE4) return builder.makeStore(4, 0, indexOr(align, 4), process(ast[2][0]), process(ast[2][1]), i32);
if (name == STORE8) return builder.makeStore(8, 0, indexOr(align, 8), process(ast[2][0]), process(ast[2][1]), i64);
if (name == STOREF) {
auto* value = process(ast[2][1]);
if (value->type == f64) {
// asm.js allows storing a double to HEAPF32, we must cast here
value = builder.makeUnary(DemoteFloat64, value);
}
return builder.makeStore(4, 0, indexOr(align, 4), process(ast[2][0]), value, f32);
}
if (name == STORED) return builder.makeStore(8, 0, indexOr(align, 8), process(ast[2][0]), process(ast[2][1]), f64);
break;
}
case 'i': {
if (num == 1) {
auto* value = process(ast[2][0]);
if (name == I64) {
// no-op "coercion" / "cast", although we also tolerate i64(0) for constants that fit in i32
if (value->type == i32) {
return builder.makeConst(Literal(int64_t(value->cast<Const>()->value.geti32())));
} else {
fixCallType(value, i64);
return value;
}
}
if (name == I32_BC2F) return builder.makeUnary(UnaryOp::ReinterpretInt32, value);
if (name == I32_BC2I) return builder.makeUnary(UnaryOp::ReinterpretFloat32, value);

if (name == I64_TRUNC) return builder.makeUnary(UnaryOp::WrapInt64, value);
if (name == I64_SEXT) return builder.makeUnary(UnaryOp::ExtendSInt32, value);
if (name == I64_ZEXT) return builder.makeUnary(UnaryOp::ExtendUInt32, value);
if (name == I64_S2F) return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat32, value);
if (name == I64_S2D) return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat64, value);
if (name == I64_U2F) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat32, value);
if (name == I64_U2D) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat64, value);
if (name == I64_F2S) return builder.makeUnary(UnaryOp::TruncSFloat32ToInt64, value);
if (name == I64_D2S) return builder.makeUnary(UnaryOp::TruncSFloat64ToInt64, value);
if (name == I64_F2U) return builder.makeUnary(UnaryOp::TruncUFloat32ToInt64, value);
if (name == I64_D2U) return builder.makeUnary(UnaryOp::TruncUFloat64ToInt64, value);
if (name == I64_BC2D) return builder.makeUnary(UnaryOp::ReinterpretInt64, value);
if (name == I64_BC2I) return builder.makeUnary(UnaryOp::ReinterpretFloat64, value);
if (name == I64_CTTZ) return builder.makeUnary(UnaryOp::CtzInt64, value);
if (name == I64_CTLZ) return builder.makeUnary(UnaryOp::ClzInt64, value);
} else if (num == 2) { // 2 params,binary
if (name == I64_CONST) return builder.makeConst(getLiteral(ast));
auto* left = process(ast[2][0]);
auto* right = process(ast[2][1]);
// maths
if (name == I64_ADD) return builder.makeBinary(BinaryOp::AddInt64, left, right);
if (name == I64_SUB) return builder.makeBinary(BinaryOp::SubInt64, left, right);
if (name == I64_MUL) return builder.makeBinary(BinaryOp::MulInt64, left, right);
if (name == I64_UDIV) return makeDangerousI64Binary(BinaryOp::DivUInt64, left, right);
if (name == I64_SDIV) return makeDangerousI64Binary(BinaryOp::DivSInt64, left, right);
if (name == I64_UREM) return makeDangerousI64Binary(BinaryOp::RemUInt64, left, right);
if (name == I64_SREM) return makeDangerousI64Binary(BinaryOp::RemSInt64, left, right);
if (name == I64_AND) return builder.makeBinary(BinaryOp::AndInt64, left, right);
if (name == I64_OR) return builder.makeBinary(BinaryOp::OrInt64, left, right);
if (name == I64_XOR) return builder.makeBinary(BinaryOp::XorInt64, left, right);
if (name == I64_SHL) return builder.makeBinary(BinaryOp::ShlInt64, left, right);
if (name == I64_ASHR) return builder.makeBinary(BinaryOp::ShrSInt64, left, right);
if (name == I64_LSHR) return builder.makeBinary(BinaryOp::ShrUInt64, left, right);
// comps
if (name == I64_EQ) return builder.makeBinary(BinaryOp::EqInt64, left, right);
if (name == I64_NE) return builder.makeBinary(BinaryOp::NeInt64, left, right);
if (name == I64_ULE) return builder.makeBinary(BinaryOp::LeUInt64, left, right);
if (name == I64_SLE) return builder.makeBinary(BinaryOp::LeSInt64, left, right);
if (name == I64_UGE) return builder.makeBinary(BinaryOp::GeUInt64, left, right);
if (name == I64_SGE) return builder.makeBinary(BinaryOp::GeSInt64, left, right);
if (name == I64_ULT) return builder.makeBinary(BinaryOp::LtUInt64, left, right);
if (name == I64_SLT) return builder.makeBinary(BinaryOp::LtSInt64, left, right);
if (name == I64_UGT) return builder.makeBinary(BinaryOp::GtUInt64, left, right);
if (name == I64_SGT) return builder.makeBinary(BinaryOp::GtSInt64, left, right);
}
break;
}
if (name == I64_TRUNC) return builder.makeUnary(UnaryOp::WrapInt64, value);
if (name == I64_SEXT) return builder.makeUnary(UnaryOp::ExtendSInt32, value);
if (name == I64_ZEXT) return builder.makeUnary(UnaryOp::ExtendUInt32, value);
if (name == I64_S2F) return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat32, value);
if (name == I64_S2D) return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat64, value);
if (name == I64_U2F) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat32, value);
if (name == I64_U2D) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat64, value);
if (name == I64_F2S) return builder.makeUnary(UnaryOp::TruncSFloat32ToInt64, value);
if (name == I64_D2S) return builder.makeUnary(UnaryOp::TruncSFloat64ToInt64, value);
if (name == I64_F2U) return builder.makeUnary(UnaryOp::TruncUFloat32ToInt64, value);
if (name == I64_D2U) return builder.makeUnary(UnaryOp::TruncUFloat64ToInt64, value);
if (name == I64_BC2D) return builder.makeUnary(UnaryOp::ReinterpretInt64, value);
if (name == I64_BC2I) return builder.makeUnary(UnaryOp::ReinterpretFloat64, value);
if (name == I64_CTTZ) return builder.makeUnary(UnaryOp::CtzInt64, value);
if (name == I64_CTLZ) return builder.makeUnary(UnaryOp::ClzInt64, value);
} else if (num == 2) { // 2 params,binary
if (name == I64_CONST) return builder.makeConst(getLiteral(ast));
if (name == I64_LOAD) return builder.makeLoad(8, true, 0, indexOr(ast[2][1][1]->getInteger(), 8), process(ast[2][0]), i64);
auto* left = process(ast[2][0]);
auto* right = process(ast[2][1]);
// maths
if (name == I64_ADD) return builder.makeBinary(BinaryOp::AddInt64, left, right);
if (name == I64_SUB) return builder.makeBinary(BinaryOp::SubInt64, left, right);
if (name == I64_MUL) return builder.makeBinary(BinaryOp::MulInt64, left, right);
if (name == I64_UDIV) return makeDangerousI64Binary(BinaryOp::DivUInt64, left, right);
if (name == I64_SDIV) return makeDangerousI64Binary(BinaryOp::DivSInt64, left, right);
if (name == I64_UREM) return makeDangerousI64Binary(BinaryOp::RemUInt64, left, right);
if (name == I64_SREM) return makeDangerousI64Binary(BinaryOp::RemSInt64, left, right);
if (name == I64_AND) return builder.makeBinary(BinaryOp::AndInt64, left, right);
if (name == I64_OR) return builder.makeBinary(BinaryOp::OrInt64, left, right);
if (name == I64_XOR) return builder.makeBinary(BinaryOp::XorInt64, left, right);
if (name == I64_SHL) return builder.makeBinary(BinaryOp::ShlInt64, left, right);
if (name == I64_ASHR) return builder.makeBinary(BinaryOp::ShrSInt64, left, right);
if (name == I64_LSHR) return builder.makeBinary(BinaryOp::ShrUInt64, left, right);
// comps
if (name == I64_EQ) return builder.makeBinary(BinaryOp::EqInt64, left, right);
if (name == I64_NE) return builder.makeBinary(BinaryOp::NeInt64, left, right);
if (name == I64_ULE) return builder.makeBinary(BinaryOp::LeUInt64, left, right);
if (name == I64_SLE) return builder.makeBinary(BinaryOp::LeSInt64, left, right);
if (name == I64_UGE) return builder.makeBinary(BinaryOp::GeUInt64, left, right);
if (name == I64_SGE) return builder.makeBinary(BinaryOp::GeSInt64, left, right);
if (name == I64_ULT) return builder.makeBinary(BinaryOp::LtUInt64, left, right);
if (name == I64_SLT) return builder.makeBinary(BinaryOp::LtSInt64, left, right);
if (name == I64_UGT) return builder.makeBinary(BinaryOp::GtUInt64, left, right);
if (name == I64_SGT) return builder.makeBinary(BinaryOp::GtSInt64, left, right);
} else if (num == 3) { // 3 params
if (name == I64_STORE) return builder.makeStore(8, 0, indexOr(ast[2][2][1]->getInteger(), 8), process(ast[2][0]), process(ast[2][1]), i64);
default: {}
}
}
Expression* ret;
Expand Down
85 changes: 73 additions & 12 deletions test/i64.asm.js → test/wasm-only.asm.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Test i64 support in wasm-only builds. In this case, fastcomp emits code that is
// Test wasm-only builds. In this case, fastcomp emits code that is
// not asm.js, it will only ever run as wasm, and contains special intrinsics for
// asm2wasm that map LLVM IR into i64s.
//
Expand All @@ -21,7 +21,66 @@ function asm(global, env, buffer) {
var illegalImport = env.illegalImport;
var illegalImportResult = env.illegalImportResult;

function loads() {
var i = 0, f = fround(0), d = +0;
i = load1(100);
i = load1(101, 0);
i = load2(102);
i = load2(103, 0);
i = load2(104, 1);
i = load2(105, 2);
i = load4(106);
i = load4(107, 0);
i = load4(108, 1);
i = load4(109, 2);
i = load4(110, 4);
f = loadf(111);
f = loadf(112, 0);
f = loadf(113, 1);
f = loadf(114, 2);
f = loadf(115, 4);
d = loadd(116);
d = loadd(117, 0);
d = loadd(118, 1);
d = loadd(119, 2);
d = loadd(120, 4);
d = loadd(121, 8);
}

function stores() {
var i = 0, f = fround(0), d = +0;
store1(100, i);
store1(101, i, 0);
store2(102, i);
store2(103, i, 0);
store2(104, i, 1);
store2(105, i, 2);
store4(106, i);
store4(107, i, 0);
store4(108, i, 1);
store4(109, i, 2);
store4(110, i, 4);
storef(111, f);
storef(112, f, 0);
storef(113, f, 1);
storef(114, f, 2);
storef(115, f, 4);
stored(116, d);
stored(117, d, 0);
stored(118, d, 1);
stored(119, d, 2);
stored(120, d, 4);
stored(121, d, 8);
}

function test() {
var i = 0, f = fround(0);
// bitcasts
i = i32_bc2i(f);
f = i32_bc2f(i);
}

function test64() {
var x = i64(), y = i64(), z = 0; // define i64 variables using special intrinsic
var int32 = 0, float32 = fround(0), float64 = +0;
x = i64_const(100, 0); // i64 constant
Expand All @@ -39,14 +98,16 @@ function asm(global, env, buffer) {
x = i64_shl(x, y);
x = i64_ashr(x, y);
x = i64_lshr(x, y);
x = i64_load(120, 0); // load and store
x = i64_load(120, 2);
x = i64_load(120, 4);
x = i64_load(120, 8);
i64_store(120, x, 0);
i64_store(120, x, 2);
i64_store(120, x, 4);
i64_store(120, x, 8);
x = load8(120, 0); // load and store
x = load8(120);
x = load8(120, 2);
x = load8(120, 4);
x = load8(120, 8);
store8(120, x, 0);
store8(120, x);
store8(120, x, 2);
store8(120, x, 4);
store8(120, x, 8);
// comps
z = i64_eq(x, y);
z = i64_ne(x, y);
Expand Down Expand Up @@ -83,14 +144,14 @@ function asm(global, env, buffer) {
}
function arg(x) { // illegal param, but not exported
x = i64(x);
i64_store(100, x, 0);
store8(100, x, 0);
arg(i64(x)); // "coercion"/"cast"
}
function illegalParam(a, x, c) {
a = 0;
x = i64(x);
b = +0;
i64_store(100, x, 0);
store8(100, x, 0);
illegalParam(0, i64(x), 12.34); // "coercion"/"cast"
}
function result() { // illegal result, but not exported
Expand Down Expand Up @@ -158,6 +219,6 @@ function asm(global, env, buffer) {
return $waka | 0;
}

return { test: test, illegalParam : illegalParam, illegalResult: illegalResult };
return { test64: test64, illegalParam : illegalParam, illegalResult: illegalResult };
}

Loading