Skip to content

[lld][WebAssembly] Return 0 for synthetic function offsets #96134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions lld/test/wasm/Inputs/signature-mismatch-debug-info-a.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
target triple = "wasm32-unknown-emscripten"

define void @foo(i32 %a) !dbg !6 {
ret void
}

define void @test0() !dbg !10 {
entry:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this function have entry: but not the one above?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I generated these tests from .c file and manually removed some unnecessary parts, and I think I left some entrys. Removed them for consistency.

call void @foo(i32 3), !dbg !13
ret void, !dbg !14
}

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2, !3, !4}
!llvm.ident = !{!5}

!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 19.0.0git", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "a.c", directory: "")
!2 = !{i32 7, !"Dwarf Version", i32 4}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = !{!"clang version 19.0.0git"}
!6 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !7, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
!7 = !DISubroutineType(types: !8)
!8 = !{null, !9}
!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!10 = distinct !DISubprogram(name: "test0", scope: !1, file: !1, line: 7, type: !11, scopeLine: 7, spFlags: DISPFlagDefinition, unit: !0)
!11 = !DISubroutineType(types: !12)
!12 = !{null}
!13 = !DILocation(line: 8, column: 3, scope: !10)
!14 = !DILocation(line: 9, column: 1, scope: !10)
31 changes: 31 additions & 0 deletions lld/test/wasm/Inputs/signature-mismatch-debug-info-b.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
target triple = "wasm32-unknown-emscripten"

define void @foo(i32 %a, i32 %b) !dbg !6 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this not result in a link error due to multiple definitions of foo?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know. They are not marked as weak, so it's weird... I tested with system (x86) clang and gcc and they all seem to error out.

When we do this by LTO, meaning running wasm-ld directly on bitcode, they error out saying duplicate definition. When does this "signature mismatch" occur usually? When a definition and a callsite do not match in the signature?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, regardless of signature... you can't have two global definitions of foo in the same program unless one of them is weak. So I can't see why this wouldn't fail, even with matching signatures.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why the current non-weak functions don't error out, but changed the test function linkages to be weak, which should not error out.

ret void
}

define void @test1() !dbg !10 {
entry:
call void @foo(i32 4, i32 5), !dbg !13
ret void, !dbg !14
}

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2, !3, !4}
!llvm.ident = !{!5}

!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 19.0.0git", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "b.c", directory: "")
!2 = !{i32 7, !"Dwarf Version", i32 4}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = !{!"clang version 19.0.0git"}
!6 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 3, type: !7, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
!7 = !DISubroutineType(types: !8)
!8 = !{null, !9, !9}
!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!10 = distinct !DISubprogram(name: "test1", scope: !1, file: !1, line: 7, type: !11, scopeLine: 7, spFlags: DISPFlagDefinition, unit: !0)
!11 = !DISubroutineType(types: !12)
!12 = !{null}
!13 = !DILocation(line: 8, column: 3, scope: !10)
!14 = !DILocation(line: 9, column: 1, scope: !10)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests include a lot of stuff that is perhaps not completely relevant to the bug I guess? Can we remove any of these lines that are not needed? Or is it better to keep them more like clang-generated bitcode?

One thing that would be great would be if we could write this in .s format... how does the .s format look for these tests? is it even more verbose?

Copy link
Member Author

@aheejin aheejin Jun 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I got after I spent some time deleting unnecessary debug info already... I can further delete DILocation nodes and the line debug info attached to instructions. But we at least need DICompileUnit and DISubprogram and other nodes that are (transitively) referenced by those nodes.

Can I attach debug info in .s files? If so, can you point me to an example?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think simply converting these files to .s will include the debug info. I'm not sure it will be harder to easier to read.

Copy link
Member Author

@aheejin aheejin Jun 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Converting these files to .s files generated this, which was too verbose: 6f7d8cd

So I just rewrote them just enough to reproduce the error: 165246a
(Turns out just mentioning the function name in .debug_info section was sufficient to reproduce the error)

30 changes: 30 additions & 0 deletions lld/test/wasm/Inputs/signature-mismatch-debug-info-main.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
target triple = "wasm32-unknown-emscripten"

define i32 @main() !dbg !6 {
entry:
call void @test0(), !dbg !10
call void @test1(), !dbg !11
ret i32 0, !dbg !12
}

declare void @test0()

declare void @test1()

!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!2, !3, !4}
!llvm.ident = !{!5}

!0 = distinct !DICompileUnit(language: DW_LANG_C11, file: !1, producer: "clang version 19.0.0git", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!1 = !DIFile(filename: "main.c", directory: "")
!2 = !{i32 7, !"Dwarf Version", i32 4}
!3 = !{i32 2, !"Debug Info Version", i32 3}
!4 = !{i32 1, !"wchar_size", i32 4}
!5 = !{!"clang version 19.0.0git"}
!6 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 4, type: !7, scopeLine: 4, spFlags: DISPFlagDefinition, unit: !0)
!7 = !DISubroutineType(types: !8)
!8 = !{!9}
!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
!10 = !DILocation(line: 5, column: 3, scope: !6)
!11 = !DILocation(line: 6, column: 3, scope: !6)
!12 = !DILocation(line: 7, column: 3, scope: !6)
8 changes: 8 additions & 0 deletions lld/test/wasm/signature-mismatch-debug-info.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# This is a regression test that checks whether a function signature mismatch
# in functions with debug info does not cause does not cause a segmentation
# fault when writing .debug_info section.

; RUN: llc -filetype=obj %p/Inputs/signature-mismatch-debug-info-a.ll -o %t.a.o
; RUN: llc -filetype=obj %p/Inputs/signature-mismatch-debug-info-b.ll -o %t.b.o
; RUN: llc -filetype=obj %p/Inputs/signature-mismatch-debug-info-main.ll -o %t.main.o
; RUN: wasm-ld -o %t.wasm %t.a.o %t.b.o %t.main.o --export=main --no-entry
11 changes: 9 additions & 2 deletions lld/wasm/InputChunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,14 @@ class InputFunction : public InputChunk {
}
void setExportName(std::string exportName) { this->exportName = exportName; }
uint32_t getFunctionInputOffset() const { return getInputSectionOffset(); }
uint32_t getFunctionCodeOffset() const { return function->CodeOffset; }
uint32_t getFunctionCodeOffset() const {
// For generated synthetic functions, such as unreachable stubs generated
// for signature mismatches, 'function' reference does not exist. This
// function is used to get function offsets for .debug_info section, and for
// those generated stubs function offsets are not meaningful anyway. So just
// return 0 in those cases.
return function ? function->CodeOffset : 0;
}
uint32_t getFunctionIndex() const { return *functionIndex; }
bool hasFunctionIndex() const { return functionIndex.has_value(); }
void setFunctionIndex(uint32_t index);
Expand All @@ -301,7 +308,7 @@ class InputFunction : public InputChunk {
return compressedSize;
}

const WasmFunction *function;
const WasmFunction *function = nullptr;

protected:
std::optional<std::string> exportName;
Expand Down
Loading