Skip to content

Commit 46da9c9

Browse files
committed
Make Asyncify work with wasm64
The emscripten side is a little tricky but I've got some tests passing. Currently blocked on: emscripten-core/emscripten#17969
1 parent 2e848a0 commit 46da9c9

File tree

2 files changed

+1410
-57
lines changed

2 files changed

+1410
-57
lines changed

src/passes/Asyncify.cpp

Lines changed: 75 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,18 @@
107107
// contains a pointer to a data structure with the info needed to rewind
108108
// and unwind:
109109
//
110-
// { // offsets
110+
// { // offsets
111111
// i32 - current asyncify stack location // 0
112112
// i32 - asyncify stack end // 4
113113
// }
114114
//
115+
// Or for wasm64:
116+
//
117+
// { // offsets
118+
// i64 - current asyncify stack location // 0
119+
// i64 - asyncify stack end // 8
120+
// }
121+
//
115122
// The asyncify stack is a representation of the call frame, as a list of
116123
// indexes of calls. In the example above, we saw index "0" for calling "bar"
117124
// from "foo". When unwinding, the indexes are added to the stack; when
@@ -130,7 +137,7 @@
130137
// The pass will also create five functions that let you control unwinding
131138
// and rewinding:
132139
//
133-
// * asyncify_start_unwind(data : i32): call this to start unwinding the
140+
// * asyncify_start_unwind(data : iPTR): call this to start unwinding the
134141
// stack from the current location. "data" must point to a data
135142
// structure as described above (with fields containing valid data).
136143
//
@@ -142,7 +149,7 @@
142149
// the code will think it is still unwinding when it should not be,
143150
// which means it will keep unwinding in a meaningless way.
144151
//
145-
// * asyncify_start_rewind(data : i32): call this to start rewinding the
152+
// * asyncify_start_rewind(data : iPTR): call this to start rewinding the
146153
// stack vack up to the location stored in the provided data. This prepares
147154
// for the rewind; to start it, you must call the first function in the
148155
// call stack to be unwound.
@@ -335,7 +342,7 @@ static const Name ASYNCIFY_CHECK_CALL_INDEX = "__asyncify_check_call_index";
335342
// size, but make debugging harder
336343
enum class State { Normal = 0, Unwinding = 1, Rewinding = 2 };
337344

338-
enum class DataOffset { BStackPos = 0, BStackEnd = 4 };
345+
enum class DataOffset { BStackPos = 0, BStackEnd = 4, BStackEnd64 = 8 };
339346

340347
const auto STACK_ALIGN = 4;
341348

@@ -795,31 +802,35 @@ static bool doesCall(Expression* curr) {
795802
class AsyncifyBuilder : public Builder {
796803
public:
797804
Module& wasm;
805+
Type pointerType;
798806

799-
AsyncifyBuilder(Module& wasm) : Builder(wasm), wasm(wasm) {}
807+
AsyncifyBuilder(Module& wasm)
808+
: Builder(wasm), wasm(wasm), pointerType(wasm.memories[0]->indexType) {}
800809

801810
Expression* makeGetStackPos() {
802-
return makeLoad(4,
811+
return makeLoad(pointerType.getByteSize(),
803812
false,
804-
int32_t(DataOffset::BStackPos),
805-
4,
806-
makeGlobalGet(ASYNCIFY_DATA, Type::i32),
807-
Type::i32,
813+
int(DataOffset::BStackPos),
814+
pointerType.getByteSize(),
815+
makeGlobalGet(ASYNCIFY_DATA, pointerType),
816+
pointerType,
808817
wasm.memories[0]->name);
809818
}
810819

811820
Expression* makeIncStackPos(int32_t by) {
812821
if (by == 0) {
813822
return makeNop();
814823
}
815-
return makeStore(
816-
4,
817-
int32_t(DataOffset::BStackPos),
818-
4,
819-
makeGlobalGet(ASYNCIFY_DATA, Type::i32),
820-
makeBinary(AddInt32, makeGetStackPos(), makeConst(Literal(by))),
821-
Type::i32,
822-
wasm.memories[0]->name);
824+
auto literal = Literal::makeFromInt64(by, pointerType);
825+
return makeStore(pointerType.getByteSize(),
826+
int(DataOffset::BStackPos),
827+
pointerType.getByteSize(),
828+
makeGlobalGet(ASYNCIFY_DATA, pointerType),
829+
makeBinary(Abstract::getBinary(pointerType, Abstract::Add),
830+
makeGetStackPos(),
831+
makeConst(literal)),
832+
pointerType,
833+
wasm.memories[0]->name);
823834
}
824835

825836
Expression* makeStateCheck(State value) {
@@ -829,7 +840,8 @@ class AsyncifyBuilder : public Builder {
829840
}
830841

831842
Expression* makeNegatedStateCheck(State value) {
832-
return makeUnary(EqZInt32, makeStateCheck(value));
843+
return makeUnary(Abstract::getUnary(pointerType, Abstract::EqZ),
844+
makeStateCheck(value));
833845
}
834846
};
835847

@@ -1384,7 +1396,7 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> {
13841396
}
13851397
auto* block = builder->makeBlock();
13861398
block->list.push_back(builder->makeIncStackPos(-total));
1387-
auto tempIndex = builder->addVar(func, Type::i32);
1399+
auto tempIndex = builder->addVar(func, builder->pointerType);
13881400
block->list.push_back(
13891401
builder->makeLocalSet(tempIndex, builder->makeGetStackPos()));
13901402
Index offset = 0;
@@ -1398,14 +1410,14 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> {
13981410
auto size = getByteSize(type);
13991411
assert(size % STACK_ALIGN == 0);
14001412
// TODO: higher alignment?
1401-
loads.push_back(
1402-
builder->makeLoad(size,
1403-
true,
1404-
offset,
1405-
STACK_ALIGN,
1406-
builder->makeLocalGet(tempIndex, Type::i32),
1407-
type,
1408-
getModule()->memories[0]->name));
1413+
loads.push_back(builder->makeLoad(
1414+
size,
1415+
true,
1416+
offset,
1417+
STACK_ALIGN,
1418+
builder->makeLocalGet(tempIndex, builder->pointerType),
1419+
type,
1420+
getModule()->memories[0]->name));
14091421
offset += size;
14101422
}
14111423
Expression* load;
@@ -1429,7 +1441,7 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> {
14291441
auto* func = getFunction();
14301442
auto numLocals = func->getNumLocals();
14311443
auto* block = builder->makeBlock();
1432-
auto tempIndex = builder->addVar(func, Type::i32);
1444+
auto tempIndex = builder->addVar(func, builder->pointerType);
14331445
block->list.push_back(
14341446
builder->makeLocalSet(tempIndex, builder->makeGetStackPos()));
14351447
Index offset = 0;
@@ -1447,14 +1459,14 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> {
14471459
}
14481460
assert(size % STACK_ALIGN == 0);
14491461
// TODO: higher alignment?
1450-
block->list.push_back(
1451-
builder->makeStore(size,
1452-
offset,
1453-
STACK_ALIGN,
1454-
builder->makeLocalGet(tempIndex, Type::i32),
1455-
localGet,
1456-
type,
1457-
getModule()->memories[0]->name));
1462+
block->list.push_back(builder->makeStore(
1463+
size,
1464+
offset,
1465+
STACK_ALIGN,
1466+
builder->makeLocalGet(tempIndex, builder->pointerType),
1467+
localGet,
1468+
type,
1469+
getModule()->memories[0]->name));
14581470
offset += size;
14591471
++j;
14601472
}
@@ -1497,6 +1509,8 @@ struct Asyncify : public Pass {
14971509
void run(Module* module) override {
14981510
auto& options = getPassOptions();
14991511
bool optimize = options.optimizeLevel > 0;
1512+
is64 = module->memories.size() && module->memories[0]->is64();
1513+
pointerType = is64 ? Type::i64 : Type::i32;
15001514

15011515
// Ensure there is a memory, as we need it.
15021516
MemoryUtils::ensureExists(module);
@@ -1640,8 +1654,8 @@ struct Asyncify : public Pass {
16401654
module->addGlobal(std::move(asyncifyState));
16411655

16421656
auto asyncifyData = builder.makeGlobal(ASYNCIFY_DATA,
1643-
Type::i32,
1644-
builder.makeConst(int32_t(0)),
1657+
pointerType,
1658+
builder.makeConst(pointerType),
16451659
Builder::Mutable);
16461660
if (imported) {
16471661
asyncifyData->module = ENV;
@@ -1655,35 +1669,36 @@ struct Asyncify : public Pass {
16551669
auto makeFunction = [&](Name name, bool setData, State state) {
16561670
std::vector<Type> params;
16571671
if (setData) {
1658-
params.push_back(Type::i32);
1672+
params.push_back(pointerType);
16591673
}
16601674
auto* body = builder.makeBlock();
16611675
body->list.push_back(builder.makeGlobalSet(
16621676
ASYNCIFY_STATE, builder.makeConst(int32_t(state))));
16631677
if (setData) {
16641678
body->list.push_back(builder.makeGlobalSet(
1665-
ASYNCIFY_DATA, builder.makeLocalGet(0, Type::i32)));
1679+
ASYNCIFY_DATA, builder.makeLocalGet(0, pointerType)));
16661680
}
16671681
// Verify the data is valid.
16681682
auto* stackPos =
1669-
builder.makeLoad(4,
1670-
false,
1671-
int32_t(DataOffset::BStackPos),
1672-
4,
1673-
builder.makeGlobalGet(ASYNCIFY_DATA, Type::i32),
1674-
Type::i32,
1675-
module->memories[0]->name);
1676-
auto* stackEnd =
1677-
builder.makeLoad(4,
1683+
builder.makeLoad(pointerType.getByteSize(),
16781684
false,
1679-
int32_t(DataOffset::BStackEnd),
1680-
4,
1681-
builder.makeGlobalGet(ASYNCIFY_DATA, Type::i32),
1682-
Type::i32,
1685+
int(DataOffset::BStackPos),
1686+
pointerType.getByteSize(),
1687+
builder.makeGlobalGet(ASYNCIFY_DATA, pointerType),
1688+
pointerType,
16831689
module->memories[0]->name);
1684-
body->list.push_back(
1685-
builder.makeIf(builder.makeBinary(GtUInt32, stackPos, stackEnd),
1686-
builder.makeUnreachable()));
1690+
auto* stackEnd = builder.makeLoad(
1691+
pointerType.getByteSize(),
1692+
false,
1693+
int(is64 ? DataOffset::BStackEnd64 : DataOffset::BStackEnd),
1694+
pointerType.getByteSize(),
1695+
builder.makeGlobalGet(ASYNCIFY_DATA, pointerType),
1696+
pointerType,
1697+
module->memories[0]->name);
1698+
body->list.push_back(builder.makeIf(
1699+
builder.makeBinary(
1700+
Abstract::getBinary(pointerType, Abstract::GtU), stackPos, stackEnd),
1701+
builder.makeUnreachable()));
16871702
body->finalize();
16881703
auto func = builder.makeFunction(
16891704
name, Signature(Type(params), Type::none), {}, body);
@@ -1704,6 +1719,9 @@ struct Asyncify : public Pass {
17041719
module->addExport(builder.makeExport(
17051720
ASYNCIFY_GET_STATE, ASYNCIFY_GET_STATE, ExternalKind::Function));
17061721
}
1722+
1723+
bool is64;
1724+
Type pointerType;
17071725
};
17081726

17091727
Pass* createAsyncifyPass() { return new Asyncify(); }

0 commit comments

Comments
 (0)