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" );
@@ -672,6 +674,12 @@ struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> {
672674 std::set<std::string> allSigs;
673675 // last sets in the current basic block, per index
674676 std::map<Index, LocalSet*> sets;
677+ // table indices that are calls to emscripten_asm_const_*
678+ std::map<Index, Name> asmTable;
679+ // cache used by tableIndexForName
680+ std::map<Name, Literal> tableIndices;
681+ // first available index after the table segment for each segment
682+ std::vector<int32_t > tableOffsets;
675683
676684 AsmConstWalker (Module& _wasm)
677685 : wasm(_wasm), segmentOffsets(getSegmentOffsets(wasm)) {}
@@ -692,7 +700,14 @@ struct AsmConstWalker : public LinearExecutionWalker<AsmConstWalker> {
692700 void queueImport (Name importName, std::string baseSig);
693701 void addImports ();
694702
703+ Index resolveConstIndex (Expression* arg,
704+ std::function<void (Expression*)> reportError);
705+ Const* resolveConstAddr (Expression* arg, const Name& target);
706+ void prepareAsmIndices (Table* table);
707+ Literal tableIndexForName (Name name);
708+
695709 std::vector<std::unique_ptr<Function>> queuedImports;
710+ std::vector<Name> queuedTableEntries;
696711};
697712
698713void AsmConstWalker::noteNonLinear (Expression* curr) {
@@ -702,45 +717,115 @@ void AsmConstWalker::noteNonLinear(Expression* curr) {
702717
703718void AsmConstWalker::visitLocalSet (LocalSet* curr) { sets[curr->index ] = curr; }
704719
720+ Const* AsmConstWalker::resolveConstAddr (Expression* arg, const Name& target) {
721+ while (!arg->dynCast <Const>()) {
722+ if (auto * get = arg->dynCast <LocalGet>()) {
723+ // The argument may be a local.get, in which case, the last set in this
724+ // basic block has the value.
725+ auto * set = sets[get->index ];
726+ if (set) {
727+ assert (set->index == get->index );
728+ arg = set->value ;
729+ }
730+ } else if (auto * value = arg->dynCast <Binary>()) {
731+ // In the dynamic linking case the address of the string constant
732+ // is the result of adding its offset to __memory_base.
733+ // In this case are only looking for the offset with the data segment so
734+ // the RHS of the addition is just what we want.
735+ assert (value->op == AddInt32);
736+ arg = value->right ;
737+ } else {
738+ Fatal () << " Unexpected arg0 type (" << getExpressionName (arg)
739+ << " ) in call to to: " << target;
740+ }
741+ }
742+ return arg->cast <Const>();
743+ }
744+
745+ Index AsmConstWalker::resolveConstIndex (
746+ Expression* arg, std::function<void (Expression*)> reportError) {
747+ while (!arg->is <Const>()) {
748+ if (auto * get = arg->dynCast <LocalGet>()) {
749+ // The argument may be a local.get, in which case, the last set in this
750+ // basic block has the value.
751+ auto * set = sets[get->index ];
752+ if (set) {
753+ assert (set->index == get->index );
754+ arg = set->value ;
755+ } else {
756+ reportError (arg);
757+ return 0 ;
758+ }
759+ } else if (arg->is <GlobalGet>()) {
760+ // In the dynamic linking case, indices start at __table_base.
761+ // We want the value relative to __table_base.
762+ // If we are doing a global.get, assume it's __table_base, then the
763+ // offset relative to __table_base must be 0.
764+ return 0 ;
765+ } else {
766+ reportError (arg);
767+ return 0 ;
768+ }
769+ }
770+ return Index (arg->cast <Const>()->value .geti32 ());
771+ }
772+
705773void AsmConstWalker::visitCall (Call* curr) {
706774 auto * import = wasm.getFunction (curr->target );
775+ if (!import ->imported ()) {
776+ return ;
777+ }
778+
707779 // Find calls to emscripten_asm_const* functions whose first argument is
708780 // is always a string constant.
709- if (import ->imported () && import -> base .hasSubstring (EMSCRIPTEN_ASM_CONST)) {
781+ if (import ->base .hasSubstring (EMSCRIPTEN_ASM_CONST)) {
710782 auto baseSig = getSig (curr);
711783 auto sig = fixupNameWithSig (curr->target , baseSig);
712- auto * arg = curr->operands [0 ];
713- while (!arg->dynCast <Const>()) {
714- if (auto * get = arg->dynCast <LocalGet>()) {
715- // The argument may be a local.get, in which case, the last set in this
716- // basic block has the value.
717- auto * set = sets[get->index ];
718- if (set) {
719- assert (set->index == get->index );
720- arg = set->value ;
721- }
722- } else if (auto * value = arg->dynCast <Binary>()) {
723- // In the dynamic linking case the address of the string constant
724- // is the result of adding its offset to __memory_base.
725- // In this case are only looking for the offset with the data segment so
726- // the RHS of the addition is just what we want.
727- assert (value->op == AddInt32);
728- arg = value->right ;
729- } else {
730- if (!value) {
731- Fatal () << " Unexpected arg0 type (" << getExpressionName (arg)
732- << " ) in call to to: " << import ->base ;
733- }
734- }
735- }
736-
737- auto * value = arg->cast <Const>();
784+ auto * value = resolveConstAddr (curr->operands [0 ], import ->base );
738785 auto code = codeForConstAddr (wasm, segmentOffsets, value);
739786 sigsForCode[code].insert (sig);
740787
741788 // Replace the first argument to the call with a Const index
742789 Builder builder (wasm);
743790 curr->operands [0 ] = builder.makeConst (idLiteralForCode (code));
791+ } else if (import ->base .startsWith (INVOKE_PREFIX)) {
792+ // A call to emscripten_asm_const_* maybe done indirectly via one of the
793+ // invoke_* functions, in case of setjmp/longjmp, for example.
794+ // We attempt to modify the invoke_* call instead.
795+ auto idx = resolveConstIndex (curr->operands [0 ], [&](Expression* arg) {});
796+
797+ // If the address of the invoke call is an emscripten_asm_const_* function:
798+ if (asmTable.count (idx)) {
799+ auto * value = resolveConstAddr (curr->operands [1 ], asmTable[idx]);
800+ auto code = codeForConstAddr (wasm, segmentOffsets, value);
801+
802+ // Extract the base signature from the invoke_* function name.
803+ std::string baseSig (import ->base .c_str () + sizeof (INVOKE_PREFIX) - 1 );
804+ Name name;
805+ auto sig = fixupNameWithSig (name, baseSig);
806+ sigsForCode[code].insert (sig);
807+
808+ Builder builder (wasm);
809+ curr->operands [0 ] = builder.makeConst (tableIndexForName (name));
810+ curr->operands [1 ] = builder.makeConst (idLiteralForCode (code));
811+ }
812+ }
813+ }
814+
815+ void AsmConstWalker::prepareAsmIndices (Table* table) {
816+ for (auto & segment : table->segments ) {
817+ auto idx = resolveConstIndex (segment.offset , [&](Expression* arg) {
818+ Fatal () << " Unexpected table index type (" << getExpressionName (arg)
819+ << " ) table" ;
820+ });
821+ for (auto & name : segment.data ) {
822+ auto * func = wasm.getFunction (name);
823+ if (func->imported () && func->base .hasSubstring (EMSCRIPTEN_ASM_CONST)) {
824+ asmTable[idx] = name;
825+ }
826+ ++idx;
827+ }
828+ tableOffsets.push_back (idx);
744829 }
745830}
746831
@@ -757,6 +842,8 @@ void AsmConstWalker::visitTable(Table* curr) {
757842}
758843
759844void AsmConstWalker::process () {
845+ // Find table indices that point to emscripten_asm_const_* functions.
846+ prepareAsmIndices (&wasm.table );
760847 // Find and queue necessary imports
761848 walkModule (&wasm);
762849 // Add them after the walk, to avoid iterator invalidation on
@@ -812,10 +899,28 @@ void AsmConstWalker::queueImport(Name importName, std::string baseSig) {
812899 queuedImports.push_back (std::unique_ptr<Function>(import ));
813900}
814901
902+ Literal AsmConstWalker::tableIndexForName (Name name) {
903+ auto result = tableIndices.find (name);
904+ if (result != tableIndices.end ()) {
905+ return result->second ;
906+ }
907+ queuedTableEntries.push_back (name);
908+ return tableIndices[name] = Literal (tableOffsets[0 ]++);
909+ }
910+
815911void AsmConstWalker::addImports () {
816912 for (auto & import : queuedImports) {
817913 wasm.addFunction (import .release ());
818914 }
915+
916+ if (!queuedTableEntries.empty ()) {
917+ assert (wasm.table .segments .size () == 1 );
918+ std::vector<Name>& tableSegmentData = wasm.table .segments [0 ].data ;
919+ for (auto & entry : queuedTableEntries) {
920+ tableSegmentData.push_back (entry);
921+ }
922+ wasm.table .initial .addr += queuedTableEntries.size ();
923+ }
819924}
820925
821926AsmConstWalker fixEmAsmConstsAndReturnWalker (Module& wasm) {
@@ -951,7 +1056,7 @@ struct FixInvokeFunctionNamesWalker
9511056 }
9521057 std::string sigWoOrigFunc = sig.front () + sig.substr (2 , sig.size () - 2 );
9531058 invokeSigs.insert (sigWoOrigFunc);
954- return Name (" invoke_ " + sigWoOrigFunc);
1059+ return Name (INVOKE_PREFIX + sigWoOrigFunc);
9551060 }
9561061
9571062 Name fixEmEHSjLjNames (const Name& name, const std::string& sig) {
@@ -1091,7 +1196,7 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
10911196 ModuleUtils::iterImportedFunctions (wasm, [&](Function* import ) {
10921197 if (emJsWalker.codeByName .count (import ->base .str ) == 0 &&
10931198 !import ->base .startsWith (EMSCRIPTEN_ASM_CONST.str ) &&
1094- !import ->base .startsWith (" invoke_ " )) {
1199+ !import ->base .startsWith (INVOKE_PREFIX )) {
10951200 if (declares.insert (import ->base .str ).second ) {
10961201 meta << nextElement () << ' "' << import ->base .str << ' "' ;
10971202 }
@@ -1145,7 +1250,7 @@ std::string EmscriptenGlueGenerator::generateEmscriptenMetadata(
11451250 meta << " \" invokeFuncs\" : [" ;
11461251 commaFirst = true ;
11471252 ModuleUtils::iterImportedFunctions (wasm, [&](Function* import ) {
1148- if (import ->base .startsWith (" invoke_ " )) {
1253+ if (import ->base .startsWith (INVOKE_PREFIX )) {
11491254 if (invokeFuncs.insert (import ->base .str ).second ) {
11501255 meta << nextElement () << ' "' << import ->base .str << ' "' ;
11511256 }
0 commit comments