@@ -49,6 +49,8 @@ using namespace llvm;
49
49
#define DEBUG_TYPE " asm-printer"
50
50
51
51
extern cl::opt<bool > WasmKeepRegisters;
52
+ extern cl::opt<bool > EnableEmException;
53
+ extern cl::opt<bool > EnableEmSjLj;
52
54
53
55
// ===----------------------------------------------------------------------===//
54
56
// Helpers.
@@ -81,10 +83,91 @@ WebAssemblyTargetStreamer *WebAssemblyAsmPrinter::getTargetStreamer() {
81
83
return static_cast <WebAssemblyTargetStreamer *>(TS);
82
84
}
83
85
86
+ // Emscripten exception handling helpers
87
+ //
88
+ // This converts invoke names generated by LowerEmscriptenEHSjLj to real names
89
+ // that are expected by JavaScript glue code. The invoke names generated by
90
+ // Emscripten JS glue code are based on their argument and return types; for
91
+ // example, for a function that takes an i32 and returns nothing, it is
92
+ // 'invoke_vi'. But the format of invoke generated by LowerEmscriptenEHSjLj pass
93
+ // contains a mangled string generated from their IR types, for example,
94
+ // "__invoke_void_%struct.mystruct*_int", because final wasm types are not
95
+ // available in the IR pass. So we convert those names to the form that
96
+ // Emscripten JS code expects.
97
+ //
98
+ // Refer to LowerEmscriptenEHSjLj pass for more details.
99
+
100
+ // Returns true if the given function name is an invoke name generated by
101
+ // LowerEmscriptenEHSjLj pass.
102
+ static bool isEmscriptenInvokeName (StringRef Name) {
103
+ if (Name.front () == ' "' && Name.back () == ' "' )
104
+ Name = Name.substr (1 , Name.size () - 2 );
105
+ return Name.startswith (" __invoke_" );
106
+ }
107
+
108
+ // Returns a character that represents the given wasm value type in invoke
109
+ // signatures.
110
+ static char getInvokeSig (wasm::ValType VT) {
111
+ switch (VT) {
112
+ case wasm::ValType::I32:
113
+ return ' i' ;
114
+ case wasm::ValType::I64:
115
+ return ' j' ;
116
+ case wasm::ValType::F32:
117
+ return ' f' ;
118
+ case wasm::ValType::F64:
119
+ return ' d' ;
120
+ case wasm::ValType::V128:
121
+ return ' V' ;
122
+ case wasm::ValType::EXNREF:
123
+ return ' E' ;
124
+ case wasm::ValType::EXTERNREF:
125
+ return ' X' ;
126
+ }
127
+ }
128
+
129
+ // Given the wasm signature, generate the invoke name in the format JS glue code
130
+ // expects.
131
+ static std::string getEmscriptenInvokeSymbolName (wasm::WasmSignature *Sig) {
132
+ assert (Sig->Returns .size () <= 1 );
133
+ std::string Ret = " invoke_" ;
134
+ if (!Sig->Returns .empty ())
135
+ for (auto VT : Sig->Returns )
136
+ Ret += getInvokeSig (VT);
137
+ else
138
+ Ret += ' v' ;
139
+ // Invokes' first argument is a pointer to the original function, so skip it
140
+ for (unsigned I = 1 , E = Sig->Params .size (); I < E; I++)
141
+ Ret += getInvokeSig (Sig->Params [I]);
142
+ return Ret;
143
+ }
144
+
84
145
// ===----------------------------------------------------------------------===//
85
146
// WebAssemblyAsmPrinter Implementation.
86
147
// ===----------------------------------------------------------------------===//
87
148
149
+ MCSymbolWasm *WebAssemblyAsmPrinter::getMCSymbolForFunction (
150
+ const Function *F, bool EnableEmEH, wasm::WasmSignature *Sig,
151
+ bool &InvokeDetected) {
152
+ MCSymbolWasm *WasmSym = nullptr ;
153
+ if (EnableEmEH && isEmscriptenInvokeName (F->getName ())) {
154
+ assert (Sig);
155
+ InvokeDetected = true ;
156
+ if (Sig->Returns .size () > 1 ) {
157
+ std::string Msg =
158
+ " Emscripten EH/SjLj does not support multivalue returns: " +
159
+ std::string (F->getName ()) + " : " +
160
+ WebAssembly::signatureToString (Sig);
161
+ report_fatal_error (Msg);
162
+ }
163
+ WasmSym = cast<MCSymbolWasm>(
164
+ GetExternalSymbolSymbol (getEmscriptenInvokeSymbolName (Sig)));
165
+ } else {
166
+ WasmSym = cast<MCSymbolWasm>(getSymbol (F));
167
+ }
168
+ return WasmSym;
169
+ }
170
+
88
171
void WebAssemblyAsmPrinter::emitEndOfAsmFile (Module &M) {
89
172
for (auto &It : OutContext.getSymbols ()) {
90
173
// Emit a .globaltype and .eventtype declaration.
@@ -95,6 +178,7 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
95
178
getTargetStreamer ()->emitEventType (Sym);
96
179
}
97
180
181
+ DenseSet<MCSymbol *> InvokeSymbols;
98
182
for (const auto &F : M) {
99
183
if (F.isIntrinsic ())
100
184
continue ;
@@ -104,31 +188,46 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) {
104
188
SmallVector<MVT, 4 > Results;
105
189
SmallVector<MVT, 4 > Params;
106
190
computeSignatureVTs (F.getFunctionType (), &F, F, TM, Params, Results);
107
- auto *Sym = cast<MCSymbolWasm>(getSymbol (&F));
191
+ // At this point these MCSymbols may or may not have been created already
192
+ // and thus also contain a signature, but we need to get the signature
193
+ // anyway here in case it is an invoke that has not yet been created. We
194
+ // will discard it later if it turns out not to be necessary.
195
+ auto Signature = signatureFromMVTs (Results, Params);
196
+ bool InvokeDetected = false ;
197
+ auto *Sym = getMCSymbolForFunction (&F, EnableEmException || EnableEmSjLj,
198
+ Signature.get (), InvokeDetected);
199
+
200
+ // Multiple functions can be mapped to the same invoke symbol. For
201
+ // example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32'
202
+ // are both mapped to '__invoke_vi'. We keep them in a set once we emit an
203
+ // Emscripten EH symbol so we don't emit the same symbol twice.
204
+ if (InvokeDetected && !InvokeSymbols.insert (Sym).second )
205
+ continue ;
206
+
108
207
Sym->setType (wasm::WASM_SYMBOL_TYPE_FUNCTION);
109
208
if (!Sym->getSignature ()) {
110
- auto Signature = signatureFromMVTs (Results, Params);
111
209
Sym->setSignature (Signature.get ());
112
210
addSignature (std::move (Signature));
211
+ } else {
212
+ // This symbol has already been created and had a signature. Discard it.
213
+ Signature.reset ();
113
214
}
114
- // FIXME: this was originally intended for post-linking and was only used
115
- // for imports that were only called indirectly (i.e. s2wasm could not
116
- // infer the type from a call). With object files it applies to all
117
- // imports. so fix the names and the tests, or rethink how import
118
- // delcarations work in asm files.
215
+
119
216
getTargetStreamer ()->emitFunctionType (Sym);
120
217
121
- if (TM.getTargetTriple ().isOSBinFormatWasm () &&
122
- F.hasFnAttribute (" wasm-import-module" )) {
218
+ if (F.hasFnAttribute (" wasm-import-module" )) {
123
219
StringRef Name =
124
220
F.getFnAttribute (" wasm-import-module" ).getValueAsString ();
125
221
Sym->setImportModule (storeName (Name));
126
222
getTargetStreamer ()->emitImportModule (Sym, Name);
127
223
}
128
- if (TM.getTargetTriple ().isOSBinFormatWasm () &&
129
- F.hasFnAttribute (" wasm-import-name" )) {
224
+ if (F.hasFnAttribute (" wasm-import-name" )) {
225
+ // If this is a converted Emscripten EH/SjLj symbol, we shouldn't use
226
+ // the original function name but the converted symbol name.
130
227
StringRef Name =
131
- F.getFnAttribute (" wasm-import-name" ).getValueAsString ();
228
+ InvokeDetected
229
+ ? Sym->getName ()
230
+ : F.getFnAttribute (" wasm-import-name" ).getValueAsString ();
132
231
Sym->setImportName (storeName (Name));
133
232
getTargetStreamer ()->emitImportName (Sym, Name);
134
233
}
@@ -304,7 +403,6 @@ void WebAssemblyAsmPrinter::emitFunctionBodyStart() {
304
403
addSignature (std::move (Signature));
305
404
WasmSym->setType (wasm::WASM_SYMBOL_TYPE_FUNCTION);
306
405
307
- // FIXME: clean up how params and results are emitted (use signatures)
308
406
getTargetStreamer ()->emitFunctionType (WasmSym);
309
407
310
408
// Emit the function index.
0 commit comments