diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.h @@ -84,7 +84,7 @@ wasm::WasmSignature *Sig, bool &InvokeDetected); MCSymbol *getOrCreateWasmSymbol(StringRef Name); - void emitExternalDecls(const Module &M); + void emitDecls(const Module &M); }; } // end namespace llvm diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -291,7 +291,7 @@ } } -void WebAssemblyAsmPrinter::emitExternalDecls(const Module &M) { +void WebAssemblyAsmPrinter::emitDecls(const Module &M) { if (signaturesEmitted) return; signaturesEmitted = true; @@ -324,55 +324,56 @@ if (F.isIntrinsic()) continue; - // Emit function type info for all undefined functions - if (F.isDeclarationForLinker()) { - SmallVector Results; - SmallVector Params; - computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); - // At this point these MCSymbols may or may not have been created already - // and thus also contain a signature, but we need to get the signature - // anyway here in case it is an invoke that has not yet been created. We - // will discard it later if it turns out not to be necessary. - auto Signature = signatureFromMVTs(Results, Params); - bool InvokeDetected = false; - auto *Sym = getMCSymbolForFunction( - &F, WebAssembly::WasmEnableEmEH || WebAssembly::WasmEnableEmSjLj, - Signature.get(), InvokeDetected); - - // Multiple functions can be mapped to the same invoke symbol. For - // example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32' - // are both mapped to '__invoke_vi'. We keep them in a set once we emit an - // Emscripten EH symbol so we don't emit the same symbol twice. - if (InvokeDetected && !InvokeSymbols.insert(Sym).second) - continue; + // Emit function type info for all functions. This will emit duplicate + // information for defined functions (which already have function type + // info emitted alongside their definition), but this is necessary in + // order to enable the single-pass WebAssemblyAsmTypeCheck to succeed. + SmallVector Results; + SmallVector Params; + computeSignatureVTs(F.getFunctionType(), &F, F, TM, Params, Results); + // At this point these MCSymbols may or may not have been created already + // and thus also contain a signature, but we need to get the signature + // anyway here in case it is an invoke that has not yet been created. We + // will discard it later if it turns out not to be necessary. + auto Signature = signatureFromMVTs(Results, Params); + bool InvokeDetected = false; + auto *Sym = getMCSymbolForFunction( + &F, WebAssembly::WasmEnableEmEH || WebAssembly::WasmEnableEmSjLj, + Signature.get(), InvokeDetected); + + // Multiple functions can be mapped to the same invoke symbol. For + // example, two IR functions '__invoke_void_i8*' and '__invoke_void_i32' + // are both mapped to '__invoke_vi'. We keep them in a set once we emit an + // Emscripten EH symbol so we don't emit the same symbol twice. + if (InvokeDetected && !InvokeSymbols.insert(Sym).second) + continue; - Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); - if (!Sym->getSignature()) { - Sym->setSignature(Signature.get()); - addSignature(std::move(Signature)); - } else { - // This symbol has already been created and had a signature. Discard it. - Signature.reset(); - } + Sym->setType(wasm::WASM_SYMBOL_TYPE_FUNCTION); + if (!Sym->getSignature()) { + Sym->setSignature(Signature.get()); + addSignature(std::move(Signature)); + } else { + // This symbol has already been created and had a signature. Discard it. + Signature.reset(); + } - getTargetStreamer()->emitFunctionType(Sym); + getTargetStreamer()->emitFunctionType(Sym); - if (F.hasFnAttribute("wasm-import-module")) { - StringRef Name = - F.getFnAttribute("wasm-import-module").getValueAsString(); - Sym->setImportModule(storeName(Name)); - getTargetStreamer()->emitImportModule(Sym, Name); - } - if (F.hasFnAttribute("wasm-import-name")) { - // If this is a converted Emscripten EH/SjLj symbol, we shouldn't use - // the original function name but the converted symbol name. - StringRef Name = - InvokeDetected - ? Sym->getName() - : F.getFnAttribute("wasm-import-name").getValueAsString(); - Sym->setImportName(storeName(Name)); - getTargetStreamer()->emitImportName(Sym, Name); - } + if (F.hasFnAttribute("wasm-import-module")) { + StringRef Name = + F.getFnAttribute("wasm-import-module").getValueAsString(); + Sym->setImportModule(storeName(Name)); + getTargetStreamer()->emitImportModule(Sym, Name); + } + if (F.hasFnAttribute("wasm-import-name")) { + // If this is a converted Emscripten EH/SjLj symbol, we shouldn't use + // the original function name but the converted symbol name. + StringRef Name = + InvokeDetected + ? Sym->getName() + : F.getFnAttribute("wasm-import-name").getValueAsString(); + Sym->setImportName(storeName(Name)); + getTargetStreamer()->emitImportName(Sym, Name); } if (F.hasFnAttribute("wasm-export-name")) { @@ -387,8 +388,8 @@ void WebAssemblyAsmPrinter::emitEndOfAsmFile(Module &M) { // This is required to emit external declarations (like .functypes) when // no functions are defined in the compilation unit and therefore, - // emitExternalDecls() is not called until now. - emitExternalDecls(M); + // emitDecls() is not called until now. + emitDecls(M); // When a function's address is taken, a TABLE_INDEX relocation is emitted // against the function symbol at the use site. However the relocation @@ -556,7 +557,7 @@ } void WebAssemblyAsmPrinter::emitConstantPool() { - emitExternalDecls(*MMI->getModule()); + emitDecls(*MMI->getModule()); assert(MF->getConstantPool()->getConstants().empty() && "WebAssembly disables constant pools"); } diff --git a/llvm/test/CodeGen/WebAssembly/byval.ll b/llvm/test/CodeGen/WebAssembly/byval.ll --- a/llvm/test/CodeGen/WebAssembly/byval.ll +++ b/llvm/test/CodeGen/WebAssembly/byval.ll @@ -16,7 +16,7 @@ declare void @ext_byval_func_alignedstruct(%AlignedStruct* byval(%AlignedStruct)) declare void @ext_byval_func_empty(%EmptyStruct* byval(%EmptyStruct)) -; CHECK-LABEL: byval_arg +; CHECK-LABEL: byval_arg: define void @byval_arg(%SmallStruct* %ptr) { ; CHECK: .functype byval_arg (i32) -> () ; Subtract 16 from SP (SP is 16-byte aligned) @@ -42,7 +42,7 @@ ret void } -; CHECK-LABEL: byval_arg_align8 +; CHECK-LABEL: byval_arg_align8: define void @byval_arg_align8(%SmallStruct* %ptr) { ; CHECK: .functype byval_arg_align8 (i32) -> () ; Don't check the entire SP sequence, just enough to get the alignment. @@ -61,7 +61,7 @@ ret void } -; CHECK-LABEL: byval_arg_double +; CHECK-LABEL: byval_arg_double: define void @byval_arg_double(%AlignedStruct* %ptr) { ; CHECK: .functype byval_arg_double (i32) -> () ; Subtract 16 from SP (SP is 16-byte aligned) @@ -79,7 +79,7 @@ ret void } -; CHECK-LABEL: byval_param +; CHECK-LABEL: byval_param: define void @byval_param(%SmallStruct* byval(%SmallStruct) align 32 %ptr) { ; CHECK: .functype byval_param (i32) -> () ; %ptr is just a pointer to a struct, so pass it directly through @@ -88,7 +88,7 @@ ret void } -; CHECK-LABEL: byval_empty_caller +; CHECK-LABEL: byval_empty_caller: define void @byval_empty_caller(%EmptyStruct* %ptr) { ; CHECK: .functype byval_empty_caller (i32) -> () ; CHECK: call ext_byval_func_empty, $0 @@ -96,7 +96,7 @@ ret void } -; CHECK-LABEL: byval_empty_callee +; CHECK-LABEL: byval_empty_callee: define void @byval_empty_callee(%EmptyStruct* byval(%EmptyStruct) %ptr) { ; CHECK: .functype byval_empty_callee (i32) -> () ; CHECK: call ext_func_empty, $0 diff --git a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll --- a/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll +++ b/llvm/test/CodeGen/WebAssembly/cfg-stackify-eh.ll @@ -24,7 +24,7 @@ ; } ; } -; CHECK-LABEL: test0 +; CHECK-LABEL: test0: ; CHECK: try ; CHECK: call foo ; CHECK: catch @@ -94,7 +94,7 @@ ; } ; } -; CHECK-LABEL: test1 +; CHECK-LABEL: test1: ; CHECK: try ; CHECK: call foo ; CHECK: catch @@ -217,7 +217,7 @@ ; } ; } -; CHECK-LABEL: test2 +; CHECK-LABEL: test2: ; CHECK: try ; CHECK: call foo ; CHECK: catch @@ -299,7 +299,7 @@ ; TRY marker should be placed at bb0 because there's a branch from bb0 to bb2, ; and scopes cannot be interleaved. -; NOOPT-LABEL: test3 +; NOOPT-LABEL: test3: ; NOOPT: try ; NOOPT: block ; NOOPT: block @@ -390,7 +390,7 @@ ; try-catch with try-delegate that rethrows an exception to the caller to fix ; this. -; NOSORT-LABEL: test5 +; NOSORT-LABEL: test5: ; NOSORT: try ; --- try-delegate starts (catch unwind mismatch) ; NOSORT try @@ -448,7 +448,7 @@ ; And the return value of 'baz' should NOT be stackified because the BB is split ; during fixing unwind mismatches. -; NOSORT-LABEL: test6 +; NOSORT-LABEL: test6: ; NOSORT: try ; NOSORT: call foo ; --- try-delegate starts (call unwind mismatch) @@ -492,7 +492,7 @@ ; to the caller. IN this case bb1 has two call unwind mismatches: 'call @foo' ; unwinds to the caller and 'call @bar' unwinds to catch C0. -; NOSORT-LABEL: test7 +; NOSORT-LABEL: test7: ; NOSORT: try ; --- try-delegate starts (catch unwind mismatch) ; NOSORT try @@ -557,7 +557,7 @@ ; nested 'try' should be placed before `i32.const 5', not between 'i32.const 5' ; and 'call @qux'. -; NOSORT-LABEL: test8 +; NOSORT-LABEL: test8: ; NOSORT: try i32 ; NOSORT: call foo ; --- try-delegate starts (call unwind mismatch) @@ -596,7 +596,7 @@ ; Tests the case when TEE stackifies a register in RegStackify but it gets ; unstackified in fixCallUnwindMismatches in CFGStackify. -; NOSORT-LOCALS-LABEL: test9 +; NOSORT-LOCALS-LABEL: test9: define void @test9(i32 %x) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { bb0: invoke void @foo() @@ -642,7 +642,7 @@ ; first catch because it is a non-C++ exception, it shouldn't unwind to the next ; catch, but it should unwind to the caller. -; NOSORT-LABEL: test10 +; NOSORT-LABEL: test10: ; NOSORT: try ; --- try-delegate starts (catch unwind mismatch) ; NOSORT: try @@ -711,7 +711,7 @@ ; (before 'cont' is sorted) and there should not be any unwind destination ; mismatches in CFGStackify. -; NOOPT-LABEL: test11 +; NOOPT-LABEL: test11: ; NOOPT: block ; NOOPT: try ; NOOPT: call foo @@ -755,7 +755,7 @@ ; invoke.cont BB fall within try~end_try, but they shouldn't cause crashes or ; unwinding destination mismatches in CFGStackify. -; NOSORT-LABEL: test12 +; NOSORT-LABEL: test12: ; NOSORT: try ; NOSORT: call foo ; NOSORT: call {{.*}} memcpy @@ -789,7 +789,7 @@ ; 'nothrow_i32' and 'fun', because the return value of 'nothrow_i32' is ; stackified and pushed onto the stack to be consumed by the call to 'fun'. -; CHECK-LABEL: test13 +; CHECK-LABEL: test13: ; CHECK: try ; CHECK: call $push{{.*}}=, nothrow_i32 ; CHECK: call fun, $pop{{.*}} @@ -890,7 +890,7 @@ ; ... ; bb2: <- Continuation BB ; end -; CHECK-LABEL: test15 +; CHECK-LABEL: test15: define void @test15(i32 %n) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { entry: invoke void @foo() @@ -959,7 +959,7 @@ ; bb3: <- Continuation BB ; end ; -; CHECK-LABEL: test16 +; CHECK-LABEL: test16: define void @test16() personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { ; CHECK: call foo entry: @@ -1007,7 +1007,7 @@ ; path back to the loop header), and is placed after the loop latch block ; 'invoke.cont' intentionally. This tests if 'end_loop' marker is placed ; correctly not right after 'invoke.cont' part but after 'ehcleanup' part, -; NOSORT-LABEL: test17 +; NOSORT-LABEL: test17: ; NOSORT: loop ; NOSORT: try ; NOSORT: end_try @@ -1056,7 +1056,7 @@ ; before its corresponding `catch_all`, because both `try` and `catch_all` body ; should satisfy the return type requirements. -; NOSORT-LABEL: test18 +; NOSORT-LABEL: test18: ; NOSORT: try i32 ; NOSORT: loop i32 ; NOSORT: end_loop @@ -1101,7 +1101,7 @@ ; because the initial TRY placement for 'call @quux' was done before 'call @baz' ; because 'call @baz''s return value is stackified. -; CHECK-LABEL: test19 +; CHECK-LABEL: test19: ; CHECK: try ; CHECK: try ; CHECK: call $[[RET:[0-9]+]]=, baz @@ -1151,7 +1151,7 @@ ; becomes invalid because it incorrectly branches into an inner scope. The ; destination should change to the BB where (b) points. -; NOSORT-LABEL: test20 +; NOSORT-LABEL: test20: ; NOSORT: try ; NOSORT: br_if 0 define void @test20(i1 %arg) personality i8* bitcast (i32 (...)* @__gxx_wasm_personality_v0 to i8*) { diff --git a/llvm/test/CodeGen/WebAssembly/function-pointer64.ll b/llvm/test/CodeGen/WebAssembly/function-pointer64.ll --- a/llvm/test/CodeGen/WebAssembly/function-pointer64.ll +++ b/llvm/test/CodeGen/WebAssembly/function-pointer64.ll @@ -30,6 +30,7 @@ ; of pointers the same size, so function pointers (which are 32-bit indices ; in Wasm) are represented as 64-bit until called. +; CHECK-LABEL: foo: ; CHECK: .functype foo (i64) -> () ; CHECK-NEXT: i32.const 1 ; CHECK-NEXT: local.get 0 diff --git a/llvm/test/CodeGen/WebAssembly/functype-emission.ll b/llvm/test/CodeGen/WebAssembly/functype-emission.ll --- a/llvm/test/CodeGen/WebAssembly/functype-emission.ll +++ b/llvm/test/CodeGen/WebAssembly/functype-emission.ll @@ -3,12 +3,10 @@ ; Demonstrates that appropriate .functype directives are emitted for defined ; functions, declared functions, and any libcalls. -; FIXME - functype directives should be emitted for defined functions as well, -; to ensure they pass the type checker (which requires any .functype to be -; encountered ahead of a call to that target). - ; CHECK: .functype __unordtf2 (i64, i64, i64, i64) -> (i32) ; CHECK: .functype __multi3 (i32, i64, i64, i64, i64) -> () +; CHECK: .functype defined_fun_1 (f64) -> (i64) +; CHECK: .functype defined_fun_2 (f64, i32) -> (i64) ; CHECK: .functype declared_fun (i32, f32, i64) -> (i32) define i64 @defined_fun_1(double %a) { diff --git a/llvm/test/CodeGen/WebAssembly/inlineasm-output-template.ll b/llvm/test/CodeGen/WebAssembly/inlineasm-output-template.ll --- a/llvm/test/CodeGen/WebAssembly/inlineasm-output-template.ll +++ b/llvm/test/CodeGen/WebAssembly/inlineasm-output-template.ll @@ -1,7 +1,10 @@ ; RUN: llc -mtriple=wasm32 < %s | FileCheck %s +; Skip past the functype directives, which interfere with the CHECK-LABEL +; matches. +; ; Test that %c works with immediates -; CHECK-LABEL: test_inlineasm_c_output_template0 +; CHECK-LABEL: test_inlineasm_c_output_template0: ; CHECK: #TEST 42 define dso_local i32 @test_inlineasm_c_output_template0() { tail call void asm sideeffect "#TEST ${0:c}", "i"(i32 42) @@ -9,7 +12,7 @@ } ; Test that %c works with global address -; CHECK-LABEL: test_inlineasm_c_output_template2 +; CHECK-LABEL: test_inlineasm_c_output_template2: ; CHECK: #TEST baz @baz = internal global i32 0, align 4 define dso_local i32 @test_inlineasm_c_output_template2() { @@ -18,7 +21,7 @@ } ; Test that %n works with immediates -; CHECK-LABEL: test_inlineasm_c_output_template1 +; CHECK-LABEL: test_inlineasm_c_output_template1: ; CHECK: #TEST -42 define dso_local i32 @test_inlineasm_c_output_template1() { tail call void asm sideeffect "#TEST ${0:n}", "i"(i32 42) diff --git a/llvm/test/CodeGen/WebAssembly/multi-return.ll b/llvm/test/CodeGen/WebAssembly/multi-return.ll --- a/llvm/test/CodeGen/WebAssembly/multi-return.ll +++ b/llvm/test/CodeGen/WebAssembly/multi-return.ll @@ -8,7 +8,7 @@ ; Test returning a single value from @return_multi_multi. define i64 @test0() { -; CHECK-LABEL: test0 +; CHECK-LABEL: test0: ; CHECK: call return_multi_multi ; CHECK: i64.load $[[RV:[0-9]+]]=, 8(${{[0-9]+}}) ; CHECK: local.copy $push8=, $[[RV]] @@ -18,7 +18,7 @@ } define i128 @test1() { -; CHECK-LABEL: test1 +; CHECK-LABEL: test1: ; CHECK: call return_multi_multi ; CHECK: i64.load $[[RV:[0-9]+]]=, 16($[[SP:[0-9]+]]) ; CHECK: i32.const $push0=, 24 @@ -32,7 +32,7 @@ } define i192 @test2() { -; CHECK-LABEL: test2 +; CHECK-LABEL: test2: ; CHECK: call return_multi_multi ; CHECK: i32.const $push0=, 40 ; CHECK: i32.add $push1=, $[[SP:[0-9]+]], $pop0 @@ -50,7 +50,7 @@ } define i128 @test3() { -; CHECK-LABEL: test3 +; CHECK-LABEL: test3: ; CHECK: call return_multi_multi ; CHECK: i64.load $[[L1:[0-9]+]]=, 56($[[SP:[0-9]+]]) ; CHECK: i32.const $push0=, 64 @@ -64,7 +64,7 @@ } define i64 @test4() { -; CHECK-LABEL: test4 +; CHECK-LABEL: test4: ; CHECK: call return_multi_multi ; CHECK: i64.load $[[L1:[0-9]+]]=, 72($[[SP:[0-9]+]]) ; CHECK: local.copy $push8=, $[[L1]] @@ -76,7 +76,7 @@ ; Test returning multiple values from @return_multi_multi. define { i64, i128 } @test5() { -; CHECK-LABEL: test5 +; CHECK-LABEL: test5: ; CHECK: call return_multi_multi ; CHECK: i32.const $push10=, 8 ; CHECK: i32.add $push11=, $[[SP:[0-9]+]], $pop10 @@ -99,7 +99,7 @@ } define { i128, i128 } @test6() { -; CHECK-LABEL: test6 +; CHECK-LABEL: test6: ; CHECK: call return_multi_multi ; CHECK: i32.const $push0=, 64 ; CHECK: i32.add $push1=, $[[SP:[0-9]+]], $pop0 @@ -124,7 +124,7 @@ } define { i64, i192 } @test7() { -; CHECK-LABEL: test7 +; CHECK-LABEL: test7: ; CHECK: call return_multi_multi ; CHECK: i32.const $push0=, 40 ; CHECK: i32.add $push1=, $[[SP:[0-9]+]], $pop0 @@ -151,7 +151,7 @@ } define { i128, i192, i128, i64 } @test8() { -; CHECK-LABEL: test8 +; CHECK-LABEL: test8: ; CHECK: call return_multi_multi ; CHECK: i32.const $push20=, 8 ; CHECK: i32.add $push21=, $[[SP:[0-9]+]], $pop20 diff --git a/llvm/test/CodeGen/WebAssembly/stack-protector.ll b/llvm/test/CodeGen/WebAssembly/stack-protector.ll --- a/llvm/test/CodeGen/WebAssembly/stack-protector.ll +++ b/llvm/test/CodeGen/WebAssembly/stack-protector.ll @@ -2,7 +2,7 @@ @"\01LC" = internal constant [11 x i8] c"buf == %s\0A\00" ; <[11 x i8]*> [#uses=1] -; WASM32-LABEL: test +; WASM32-LABEL: test: ; WASM32: i32.load 28 ; WASM32: br_if 0 ; WASM32: call __stack_chk_fail @@ -25,7 +25,7 @@ ret void } -; WASM32-LABEL: test_return_i32 +; WASM32-LABEL: test_return_i32: ; WASM32: call __stack_chk_fail ; WASM32-NEXT: unreachable diff --git a/llvm/test/MC/WebAssembly/stack-ptr-mclower.ll b/llvm/test/MC/WebAssembly/stack-ptr-mclower.ll --- a/llvm/test/MC/WebAssembly/stack-ptr-mclower.ll +++ b/llvm/test/MC/WebAssembly/stack-ptr-mclower.ll @@ -18,6 +18,9 @@ ; CHECK: .text ; CHECK-NEXT: .file "stack-ptr-mclower.ll" ; CHECK-NEXT: .globaltype __stack_pointer, [[PTR]] +; CHECK-NEXT: .functype bar () -> () +; CHECK-NEXT: .functype foo () -> () + ; CHECK-NEXT: .section .text.bar,"",@ ; CHECK-NEXT: .hidden bar ; CHECK-NEXT: .globl bar