Skip to content

Commit 56f8cec

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 56f8cec

File tree

2 files changed

+1411
-57
lines changed

2 files changed

+1411
-57
lines changed

src/passes/Asyncify.cpp

Lines changed: 76 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,36 @@ 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),
809+
pointerType(wasm.memories[0]->indexType) {}
800810

801811
Expression* makeGetStackPos() {
802-
return makeLoad(4,
812+
return makeLoad(pointerType.getByteSize(),
803813
false,
804-
int32_t(DataOffset::BStackPos),
805-
4,
806-
makeGlobalGet(ASYNCIFY_DATA, Type::i32),
807-
Type::i32,
814+
int(DataOffset::BStackPos),
815+
pointerType.getByteSize(),
816+
makeGlobalGet(ASYNCIFY_DATA, pointerType),
817+
pointerType,
808818
wasm.memories[0]->name);
809819
}
810820

811821
Expression* makeIncStackPos(int32_t by) {
812822
if (by == 0) {
813823
return makeNop();
814824
}
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);
825+
auto literal = Literal::makeFromInt64(by, pointerType);
826+
return makeStore(pointerType.getByteSize(),
827+
int(DataOffset::BStackPos),
828+
pointerType.getByteSize(),
829+
makeGlobalGet(ASYNCIFY_DATA, pointerType),
830+
makeBinary(Abstract::getBinary(pointerType, Abstract::Add),
831+
makeGetStackPos(),
832+
makeConst(literal)),
833+
pointerType,
834+
wasm.memories[0]->name);
823835
}
824836

825837
Expression* makeStateCheck(State value) {
@@ -829,7 +841,8 @@ class AsyncifyBuilder : public Builder {
829841
}
830842

831843
Expression* makeNegatedStateCheck(State value) {
832-
return makeUnary(EqZInt32, makeStateCheck(value));
844+
return makeUnary(Abstract::getUnary(pointerType, Abstract::EqZ),
845+
makeStateCheck(value));
833846
}
834847
};
835848

@@ -1384,7 +1397,7 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> {
13841397
}
13851398
auto* block = builder->makeBlock();
13861399
block->list.push_back(builder->makeIncStackPos(-total));
1387-
auto tempIndex = builder->addVar(func, Type::i32);
1400+
auto tempIndex = builder->addVar(func, builder->pointerType);
13881401
block->list.push_back(
13891402
builder->makeLocalSet(tempIndex, builder->makeGetStackPos()));
13901403
Index offset = 0;
@@ -1398,14 +1411,14 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> {
13981411
auto size = getByteSize(type);
13991412
assert(size % STACK_ALIGN == 0);
14001413
// 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));
1414+
loads.push_back(builder->makeLoad(
1415+
size,
1416+
true,
1417+
offset,
1418+
STACK_ALIGN,
1419+
builder->makeLocalGet(tempIndex, builder->pointerType),
1420+
type,
1421+
getModule()->memories[0]->name));
14091422
offset += size;
14101423
}
14111424
Expression* load;
@@ -1429,7 +1442,7 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> {
14291442
auto* func = getFunction();
14301443
auto numLocals = func->getNumLocals();
14311444
auto* block = builder->makeBlock();
1432-
auto tempIndex = builder->addVar(func, Type::i32);
1445+
auto tempIndex = builder->addVar(func, builder->pointerType);
14331446
block->list.push_back(
14341447
builder->makeLocalSet(tempIndex, builder->makeGetStackPos()));
14351448
Index offset = 0;
@@ -1447,14 +1460,14 @@ struct AsyncifyLocals : public WalkerPass<PostWalker<AsyncifyLocals>> {
14471460
}
14481461
assert(size % STACK_ALIGN == 0);
14491462
// 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));
1463+
block->list.push_back(builder->makeStore(
1464+
size,
1465+
offset,
1466+
STACK_ALIGN,
1467+
builder->makeLocalGet(tempIndex, builder->pointerType),
1468+
localGet,
1469+
type,
1470+
getModule()->memories[0]->name));
14581471
offset += size;
14591472
++j;
14601473
}
@@ -1497,6 +1510,8 @@ struct Asyncify : public Pass {
14971510
void run(Module* module) override {
14981511
auto& options = getPassOptions();
14991512
bool optimize = options.optimizeLevel > 0;
1513+
is64 = module->memories.size() && module->memories[0]->is64();
1514+
pointerType = is64 ? Type::i64 : Type::i32;
15001515

15011516
// Ensure there is a memory, as we need it.
15021517
MemoryUtils::ensureExists(module);
@@ -1640,8 +1655,8 @@ struct Asyncify : public Pass {
16401655
module->addGlobal(std::move(asyncifyState));
16411656

16421657
auto asyncifyData = builder.makeGlobal(ASYNCIFY_DATA,
1643-
Type::i32,
1644-
builder.makeConst(int32_t(0)),
1658+
pointerType,
1659+
builder.makeConst(pointerType),
16451660
Builder::Mutable);
16461661
if (imported) {
16471662
asyncifyData->module = ENV;
@@ -1655,35 +1670,36 @@ struct Asyncify : public Pass {
16551670
auto makeFunction = [&](Name name, bool setData, State state) {
16561671
std::vector<Type> params;
16571672
if (setData) {
1658-
params.push_back(Type::i32);
1673+
params.push_back(pointerType);
16591674
}
16601675
auto* body = builder.makeBlock();
16611676
body->list.push_back(builder.makeGlobalSet(
16621677
ASYNCIFY_STATE, builder.makeConst(int32_t(state))));
16631678
if (setData) {
16641679
body->list.push_back(builder.makeGlobalSet(
1665-
ASYNCIFY_DATA, builder.makeLocalGet(0, Type::i32)));
1680+
ASYNCIFY_DATA, builder.makeLocalGet(0, pointerType)));
16661681
}
16671682
// Verify the data is valid.
16681683
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,
1684+
builder.makeLoad(pointerType.getByteSize(),
16781685
false,
1679-
int32_t(DataOffset::BStackEnd),
1680-
4,
1681-
builder.makeGlobalGet(ASYNCIFY_DATA, Type::i32),
1682-
Type::i32,
1686+
int(DataOffset::BStackPos),
1687+
pointerType.getByteSize(),
1688+
builder.makeGlobalGet(ASYNCIFY_DATA, pointerType),
1689+
pointerType,
16831690
module->memories[0]->name);
1684-
body->list.push_back(
1685-
builder.makeIf(builder.makeBinary(GtUInt32, stackPos, stackEnd),
1686-
builder.makeUnreachable()));
1691+
auto* stackEnd = builder.makeLoad(
1692+
pointerType.getByteSize(),
1693+
false,
1694+
int(is64 ? DataOffset::BStackEnd64 : DataOffset::BStackEnd),
1695+
pointerType.getByteSize(),
1696+
builder.makeGlobalGet(ASYNCIFY_DATA, pointerType),
1697+
pointerType,
1698+
module->memories[0]->name);
1699+
body->list.push_back(builder.makeIf(
1700+
builder.makeBinary(
1701+
Abstract::getBinary(pointerType, Abstract::GtU), stackPos, stackEnd),
1702+
builder.makeUnreachable()));
16871703
body->finalize();
16881704
auto func = builder.makeFunction(
16891705
name, Signature(Type(params), Type::none), {}, body);
@@ -1704,6 +1720,9 @@ struct Asyncify : public Pass {
17041720
module->addExport(builder.makeExport(
17051721
ASYNCIFY_GET_STATE, ASYNCIFY_GET_STATE, ExternalKind::Function));
17061722
}
1723+
1724+
bool is64;
1725+
Type pointerType;
17071726
};
17081727

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

0 commit comments

Comments
 (0)