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
3334cashew::IString EMSCRIPTEN_ASM_CONST (" emscripten_asm_const" );
3435cashew::IString EM_JS_PREFIX (" __em_js__" );
36+ static const char * INVOKE_PREFIX = " invoke_" ;
3537
3638static Name STACK_SAVE (" stackSave" );
3739static 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
583598void AsmConstWalker::noteNonLinear (Expression* curr) {
@@ -587,45 +602,109 @@ void AsmConstWalker::noteNonLinear(Expression* curr) {
587602
588603void 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+
590652void 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
644723void 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+
700789void 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
706804AsmConstWalker 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 }
0 commit comments