Skip to content

Commit 692f466

Browse files
authored
Fix EM_ASM not working with setjmp/longjmp (#2271)
This fix does not handle dynamic linking, which requires additional work. Refs emscripten-core/emscripten#8894.
1 parent ff9bd3a commit 692f466

3 files changed

Lines changed: 180 additions & 53 deletions

File tree

src/wasm/wasm-emscripten.cpp

Lines changed: 128 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "wasm-emscripten.h"
1818

19+
#include <functional>
1920
#include <sstream>
2021

2122
#include "asm_v_wasm.h"
@@ -32,6 +33,7 @@ namespace wasm {
3233

3334
cashew::IString EMSCRIPTEN_ASM_CONST("emscripten_asm_const");
3435
cashew::IString EM_JS_PREFIX("__em_js__");
36+
static const char* INVOKE_PREFIX = "invoke_";
3537

3638
static Name STACK_SAVE("stackSave");
3739
static Name STACK_RESTORE("stackRestore");
@@ -557,6 +559,12 @@ struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> {
557559
std::set<std::string> allSigs;
558560
// last sets in the current basic block, per index
559561
std::map<Index, LocalSet*> sets;
562+
// table indices that are calls to emscripten_asm_const_*
563+
std::map<Index, Name> asmTable;
564+
// cache used by tableIndexForName
565+
std::map<Name, Literal> tableIndices;
566+
// first available index after the table segment for each segment
567+
std::vector<int32_t> tableOffsets;
560568

561569
AsmConstWalker(Module& _wasm)
562570
: wasm(_wasm), segmentOffsets(getSegmentOffsets(wasm)) {}
@@ -577,7 +585,14 @@ struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> {
577585
void queueImport(Name importName, std::string baseSig);
578586
void addImports();
579587

588+
Index resolveConstIndex(Expression* arg,
589+
std::function<void(Expression*)> reportError);
590+
Const* resolveConstAddr(Expression* arg, const Name& target);
591+
void prepareAsmIndices(Table* table);
592+
Literal tableIndexForName(Name name);
593+
580594
std::vector<std::unique_ptr<Function>> queuedImports;
595+
std::vector<Name> queuedTableEntries;
581596
};
582597

583598
void AsmConstWalker::noteNonLinear(Expression* curr) {
@@ -587,45 +602,109 @@ void AsmConstWalker::noteNonLinear(Expression* curr) {
587602

588603
void AsmConstWalker::visitLocalSet(LocalSet* curr) { sets[curr->index] = curr; }
589604

605+
Const* AsmConstWalker::resolveConstAddr(Expression* arg, const Name& target) {
606+
while (!arg->dynCast<Const>()) {
607+
if (auto* get = arg->dynCast<LocalGet>()) {
608+
// The argument may be a local.get, in which case, the last set in this
609+
// basic block has the value.
610+
auto* set = sets[get->index];
611+
if (set) {
612+
assert(set->index == get->index);
613+
arg = set->value;
614+
}
615+
} else if (auto* value = arg->dynCast<Binary>()) {
616+
// In the dynamic linking case the address of the string constant
617+
// is the result of adding its offset to __memory_base.
618+
// In this case are only looking for the offset with the data segment so
619+
// the RHS of the addition is just what we want.
620+
assert(value->op == AddInt32);
621+
arg = value->right;
622+
} else {
623+
Fatal() << "Unexpected arg0 type (" << getExpressionName(arg)
624+
<< ") in call to to: " << target;
625+
}
626+
}
627+
return arg->cast<Const>();
628+
}
629+
630+
Index AsmConstWalker::resolveConstIndex(
631+
Expression* arg, std::function<void(Expression*)> reportError) {
632+
while (!arg->dynCast<Const>()) {
633+
if (auto* get = arg->dynCast<LocalGet>()) {
634+
// The argument may be a local.get, in which case, the last set in this
635+
// basic block has the value.
636+
auto* set = sets[get->index];
637+
if (set) {
638+
assert(set->index == get->index);
639+
arg = set->value;
640+
}
641+
} else if (arg->is<GlobalGet>()) {
642+
// In the dynamic linking case, indices start at __table_base.
643+
// We want the value relative to __table_base.
644+
return 0;
645+
} else {
646+
reportError(arg);
647+
}
648+
}
649+
return Index(arg->cast<Const>()->value.geti32());
650+
}
651+
590652
void AsmConstWalker::visitCall(Call* curr) {
591653
auto* import = wasm.getFunction(curr->target);
654+
if (!import->imported()) {
655+
return;
656+
}
657+
592658
// Find calls to emscripten_asm_const* functions whose first argument is
593659
// is always a string constant.
594-
if (import->imported() && import->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
660+
if (import->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
595661
auto baseSig = getSig(curr);
596662
auto sig = fixupNameWithSig(curr->target, baseSig);
597-
auto* arg = curr->operands[0];
598-
while (!arg->dynCast<Const>()) {
599-
if (auto* get = arg->dynCast<LocalGet>()) {
600-
// The argument may be a local.get, in which case, the last set in this
601-
// basic block has the value.
602-
auto* set = sets[get->index];
603-
if (set) {
604-
assert(set->index == get->index);
605-
arg = set->value;
606-
}
607-
} else if (auto* value = arg->dynCast<Binary>()) {
608-
// In the dynamic linking case the address of the string constant
609-
// is the result of adding its offset to __memory_base.
610-
// In this case are only looking for the offset with the data segment so
611-
// the RHS of the addition is just what we want.
612-
assert(value->op == AddInt32);
613-
arg = value->right;
614-
} else {
615-
if (!value) {
616-
Fatal() << "Unexpected arg0 type (" << getExpressionName(arg)
617-
<< ") in call to to: " << import->base;
618-
}
619-
}
620-
}
621-
622-
auto* value = arg->cast<Const>();
663+
auto* value = resolveConstAddr(curr->operands[0], import->base);
623664
auto code = codeForConstAddr(wasm, segmentOffsets, value);
624665
sigsForCode[code].insert(sig);
625666

626667
// Replace the first argument to the call with a Const index
627668
Builder builder(wasm);
628669
curr->operands[0] = builder.makeConst(idLiteralForCode(code));
670+
} else if (import->base.startsWith(INVOKE_PREFIX)) {
671+
auto idx = resolveConstIndex(curr->operands[0], [&](Expression* arg) {
672+
Fatal() << "Unexpected table index type (" << getExpressionName(arg)
673+
<< ") in call to: " << import->base;
674+
});
675+
676+
// If the address of the invoke call is an emscripten_asm_const_* function:
677+
if (asmTable.count(idx)) {
678+
auto* value = resolveConstAddr(curr->operands[1], asmTable[idx]);
679+
auto code = codeForConstAddr(wasm, segmentOffsets, value);
680+
681+
// Extract the base signature from the invoke_* function name.
682+
std::string baseSig(import->base.c_str() + sizeof(INVOKE_PREFIX) - 1);
683+
Name name;
684+
auto sig = fixupNameWithSig(name, baseSig);
685+
sigsForCode[code].insert(sig);
686+
687+
Builder builder(wasm);
688+
curr->operands[0] = builder.makeConst(tableIndexForName(name));
689+
curr->operands[1] = builder.makeConst(idLiteralForCode(code));
690+
}
691+
}
692+
}
693+
694+
void AsmConstWalker::prepareAsmIndices(Table* table) {
695+
for (auto& segment : table->segments) {
696+
auto idx = resolveConstIndex(segment.offset, [&](Expression* arg) {
697+
Fatal() << "Unexpected table index type (" << getExpressionName(arg)
698+
<< ") table";
699+
});
700+
for (auto& name : segment.data) {
701+
auto* func = wasm.getFunction(name);
702+
if (func->imported() && func->base.hasSubstring(EMSCRIPTEN_ASM_CONST)) {
703+
asmTable[idx] = name;
704+
}
705+
++idx;
706+
}
707+
tableOffsets.push_back(idx);
629708
}
630709
}
631710

@@ -642,6 +721,8 @@ void AsmConstWalker::visitTable(Table* curr) {
642721
}
643722

644723
void AsmConstWalker::process() {
724+
// Find table indices that point to emscripten_asm_const_* functions.
725+
prepareAsmIndices(&wasm.table);
645726
// Find and queue necessary imports
646727
walkModule(&wasm);
647728
// Add them after the walk, to avoid iterator invalidation on
@@ -697,10 +778,27 @@ void AsmConstWalker::queueImport(Name importName, std::string baseSig) {
697778
queuedImports.push_back(std::unique_ptr<Function>(import));
698779
}
699780

781+
Literal AsmConstWalker::tableIndexForName(Name name) {
782+
if (tableIndices.count(name)) {
783+
return tableIndices[name];
784+
}
785+
queuedTableEntries.push_back(name);
786+
return tableIndices[name] = Literal(tableOffsets[0]++);
787+
}
788+
700789
void AsmConstWalker::addImports() {
701790
for (auto& import : queuedImports) {
702791
wasm.addFunction(import.release());
703792
}
793+
794+
if (!queuedTableEntries.empty()) {
795+
assert(wasm.table.segments.size() == 1);
796+
std::vector<Name>& tableSegmentData = wasm.table.segments[0].data;
797+
for (auto& entry : queuedTableEntries) {
798+
tableSegmentData.push_back(entry);
799+
}
800+
wasm.table.initial.addr += queuedTableEntries.size();
801+
}
704802
}
705803

706804
AsmConstWalker fixEmAsmConstsAndReturnWalker(Module& wasm) {
@@ -835,7 +933,7 @@ struct FixInvokeFunctionNamesWalker
835933
}
836934
std::string sigWoOrigFunc = sig.front() + sig.substr(2, sig.size() - 2);
837935
invokeSigs.insert(sigWoOrigFunc);
838-
return Name("invoke_" + sigWoOrigFunc);
936+
return Name(INVOKE_PREFIX + sigWoOrigFunc);
839937
}
840938

841939
Name fixEmEHSjLjNames(const Name& name, const std::string& sig) {
@@ -975,7 +1073,7 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
9751073
ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
9761074
if (emJsWalker.codeByName.count(import->base.str) == 0 &&
9771075
!import->base.startsWith(EMSCRIPTEN_ASM_CONST.str) &&
978-
!import->base.startsWith("invoke_")) {
1076+
!import->base.startsWith(INVOKE_PREFIX)) {
9791077
if (declares.insert(import->base.str).second) {
9801078
meta << nextElement() << '"' << import->base.str << '"';
9811079
}
@@ -1029,7 +1127,7 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
10291127
meta << " \"invokeFuncs\": [";
10301128
commaFirst = true;
10311129
ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
1032-
if (import->base.startsWith("invoke_")) {
1130+
if (import->base.startsWith(INVOKE_PREFIX)) {
10331131
if (invokeFuncs.insert(import->base.str).second) {
10341132
meta << nextElement() << '"' << import->base.str << '"';
10351133
}

test/lld/em_asm_table.wast

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,18 @@
44
(import "env" "memory" (memory $2 8192))
55
(import "env" "emscripten_log" (func $fimport$0 (param i32 i32)))
66
(import "env" "emscripten_asm_const_int" (func $fimport$1 (param i32 i32 i32) (result i32)))
7+
(import "env" "__invoke_i32_i8*_i8*_..." (func $__invoke_i32_i8*_i8*_... (param i32 i32 i32 i32) (result i32)))
8+
(data (i32.const 1024) "{ console.log(\"hello world\"); }\00")
79
(table $0 159609 funcref)
810
(elem (i32.const 1) $fimport$0 $fimport$1)
911
(global $global$0 (mut i32) (i32.const 1024))
1012
(global $global$1 i32 (i32.const 1048))
1113
(export "__data_end" (global $global$1))
14+
(export "main" (func $main))
15+
(func $main
16+
(drop
17+
(call $__invoke_i32_i8*_i8*_... (i32.const 2) (i32.const 1024) (i32.const 13) (i32.const 27))
18+
)
19+
)
1220
)
1321

0 commit comments

Comments
 (0)