Skip to content

Commit 6b4ab0a

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 a697046 commit 6b4ab0a

File tree

2 files changed

+1391
-39
lines changed

2 files changed

+1391
-39
lines changed

src/passes/Asyncify.cpp

Lines changed: 56 additions & 39 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,30 +802,35 @@ static bool doesCall(Expression* curr) {
795802
class AsyncifyBuilder : public Builder {
796803
public:
797804
Module& wasm;
805+
bool is64;
806+
Type pointerType;
798807

799-
AsyncifyBuilder(Module& wasm) : Builder(wasm), wasm(wasm) {}
808+
AsyncifyBuilder(Module& wasm)
809+
: Builder(wasm), wasm(wasm), is64(wasm.memories[0]->is64()),
810+
pointerType(is64 ? Type::i64 : Type::i32) {}
800811

801812
Expression* makeGetStackPos() {
802813
return makeLoad(4,
803814
false,
804815
int32_t(DataOffset::BStackPos),
805816
4,
806-
makeGlobalGet(ASYNCIFY_DATA, Type::i32),
807-
Type::i32,
817+
makeGlobalGet(ASYNCIFY_DATA, pointerType),
818+
pointerType,
808819
wasm.memories[0]->name);
809820
}
810821

811822
Expression* makeIncStackPos(int32_t by) {
812823
if (by == 0) {
813824
return makeNop();
814825
}
826+
Literal literal = is64 ? Literal(int64_t{by}) : Literal(int32_t{by});
815827
return makeStore(
816828
4,
817829
int32_t(DataOffset::BStackPos),
818830
4,
819-
makeGlobalGet(ASYNCIFY_DATA, Type::i32),
820-
makeBinary(AddInt32, makeGetStackPos(), makeConst(Literal(by))),
821-
Type::i32,
831+
makeGlobalGet(ASYNCIFY_DATA, pointerType),
832+
makeBinary(is64 ? AddInt64 : AddInt32, makeGetStackPos(), makeConst(literal)),
833+
pointerType,
822834
wasm.memories[0]->name);
823835
}
824836

@@ -829,7 +841,7 @@ class AsyncifyBuilder : public Builder {
829841
}
830842

831843
Expression* makeNegatedStateCheck(State value) {
832-
return makeUnary(EqZInt32, makeStateCheck(value));
844+
return makeUnary(is64 ? EqZInt64 : EqZInt32, 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,34 +1669,34 @@ 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 =
16691683
builder.makeLoad(4,
16701684
false,
16711685
int32_t(DataOffset::BStackPos),
16721686
4,
1673-
builder.makeGlobalGet(ASYNCIFY_DATA, Type::i32),
1674-
Type::i32,
1687+
builder.makeGlobalGet(ASYNCIFY_DATA, pointerType),
1688+
pointerType,
16751689
module->memories[0]->name);
16761690
auto* stackEnd =
16771691
builder.makeLoad(4,
16781692
false,
1679-
int32_t(DataOffset::BStackEnd),
1693+
int32_t(is64 ? DataOffset::BStackEnd64 : DataOffset::BStackEnd),
16801694
4,
1681-
builder.makeGlobalGet(ASYNCIFY_DATA, Type::i32),
1682-
Type::i32,
1695+
builder.makeGlobalGet(ASYNCIFY_DATA, pointerType),
1696+
pointerType,
16831697
module->memories[0]->name);
16841698
body->list.push_back(
1685-
builder.makeIf(builder.makeBinary(GtUInt32, stackPos, stackEnd),
1699+
builder.makeIf(builder.makeBinary(is64 ? GtUInt64 : GtUInt32, stackPos, stackEnd),
16861700
builder.makeUnreachable()));
16871701
body->finalize();
16881702
auto func = builder.makeFunction(
@@ -1704,6 +1718,9 @@ struct Asyncify : public Pass {
17041718
module->addExport(builder.makeExport(
17051719
ASYNCIFY_GET_STATE, ASYNCIFY_GET_STATE, ExternalKind::Function));
17061720
}
1721+
1722+
bool is64;
1723+
Type pointerType;
17071724
};
17081725

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

0 commit comments

Comments
 (0)