Skip to content

Commit 54a15d2

Browse files
BertalanDIanWood1
authored andcommitted
[lld][WebAssembly] Fix spurious signature mismatch under LTO (llvm#136197)
When generating C++ vtables, Clang declares virtual functions as `void(void)` when their signature is not known (e.g.parameter types are forward-declared). As WASM type checks imports, this would conflict with the real definition during linking. Commit 59f959f introduced a workaround for this by deferring signature assignment until a definition or direct call is seen. When performing LTO, LLD first scans the bitcode files and creates `DefinedFunction` symbol table entries for their contents. After LTO codegen, they are replaced with `UndefinedFunction`s (so that the definitions will be pulled in from the native LTO-d files when they are added). At this point, if a function is only referenced in bitcode, its signature remains `nullptr`. From here, it should have behaved like in the non-LTO case: the first direct call sets the signature. However, as the `isCalledDirectly` flag was set to true, the missing signature was filled in by the type of the first reference to the function, which could be a `void(void)` vtable entry, which would then conflict with the real definition. This commit sets `isCalledDirectly` to false so that the signature will only be populated when a direct call is found. See godotengine/godot#104497 and emscripten-core/emscripten#10831
1 parent d46bb9d commit 54a15d2

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
; RUN: rm -rf %t && split-file %s %t && cd %t
2+
; RUN: opt -thinlto-bc a.ll -o a.o
3+
; RUN: opt -thinlto-bc b.ll -o b.o
4+
; RUN: llvm-ar rcs b.a b.o
5+
; RUN: opt -thinlto-bc c.ll -o c.o
6+
7+
;; Taking the address of the incorrectly declared @foo should not generate a warning.
8+
; RUN: wasm-ld --fatal-warnings --no-entry --export-all a.o b.a -o a.out \
9+
; RUN: | FileCheck %s --implicit-check-not 'warning' --allow-empty
10+
11+
;; But we should still warn if we call the function with the wrong signature.
12+
; RUN: not wasm-ld --fatal-warnings --no-entry --export-all a.o b.a c.o -o b.out 2>&1 \
13+
; RUN: | FileCheck %s --check-prefix=INVALID
14+
15+
; INVALID: error: function signature mismatch: foo
16+
; INVALID: >>> defined as () -> void
17+
; INVALID: >>> defined as () -> i32
18+
19+
;--- a.ll
20+
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
21+
target triple = "wasm32-unknown-unknown"
22+
23+
@ptr = constant ptr @foo
24+
declare void @foo()
25+
26+
;--- b.ll
27+
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
28+
target triple = "wasm32-unknown-unknown"
29+
30+
define i32 @foo() noinline {
31+
entry:
32+
ret i32 42
33+
}
34+
35+
;--- c.ll
36+
target datalayout = "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20"
37+
target triple = "wasm32-unknown-unknown"
38+
39+
declare void @foo()
40+
41+
define void @invalid() {
42+
entry:
43+
call void @foo()
44+
ret void
45+
}

lld/wasm/LTO.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,10 @@ BitcodeCompiler::~BitcodeCompiler() = default;
108108

109109
static void undefine(Symbol *s) {
110110
if (auto f = dyn_cast<DefinedFunction>(s))
111+
// If the signature is null, there were no calls from non-bitcode objects.
111112
replaceSymbol<UndefinedFunction>(f, f->getName(), std::nullopt,
112113
std::nullopt, 0, f->getFile(),
113-
f->signature);
114+
f->signature, f->signature != nullptr);
114115
else if (isa<DefinedData>(s))
115116
replaceSymbol<UndefinedData>(s, s->getName(), 0, s->getFile());
116117
else

0 commit comments

Comments
 (0)