llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.8k stars 11.9k forks source link

SelectionDAGBuilder.cpp's `llvm_unreachable` is quite reachable, actually #104718

Closed workingjubilee closed 2 months ago

workingjubilee commented 2 months ago

This IR:

source_filename = "example.1c26dad09674458a-cgu.0"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-none-elf"

@llvm.used = appending global [1 x ptr] [ptr @asan.module_ctor], section "llvm.metadata"
@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @asan.module_ctor, ptr null }]

define x86_intrcc void @_ZN7example18page_fault_handler17hdb4acb34a86c6a8bE(ptr byval([8 x i8]) align 8 %0, i64 %1) unnamed_addr #0 !dbg !8 {
start:
  %Arg0.byval = alloca [8 x i8], align 8, !dbg !13
  call void @llvm.memcpy.p0.p0.i64(ptr align 8 %Arg0.byval, ptr align 8 %0, i64 8, i1 false), !dbg !13
  call void asm sideeffect alignstack inteldialect "ud2", "~{dirflag},~{fpsr},~{flags},~{memory}"(), !dbg !13, !srcloc !14
  unreachable, !dbg !13
}

declare void @__asan_report_load_n(i64, i64)
declare void @__asan_loadN(i64, i64)
declare void @__asan_report_load1(i64)
declare void @__asan_load1(i64)
declare void @__asan_report_load2(i64)
declare void @__asan_load2(i64)
declare void @__asan_report_load4(i64)
declare void @__asan_load4(i64)
declare void @__asan_report_load8(i64)
declare void @__asan_load8(i64)
declare void @__asan_report_load16(i64)
declare void @__asan_load16(i64)
declare void @__asan_report_store_n(i64, i64)
declare void @__asan_storeN(i64, i64)
declare void @__asan_report_store1(i64)
declare void @__asan_store1(i64)
declare void @__asan_report_store2(i64)
declare void @__asan_store2(i64)
declare void @__asan_report_store4(i64)
declare void @__asan_store4(i64)
declare void @__asan_report_store8(i64)
declare void @__asan_store8(i64)
declare void @__asan_report_store16(i64)
declare void @__asan_store16(i64)
declare void @__asan_report_exp_load_n(i64, i64, i32)
declare void @__asan_exp_loadN(i64, i64, i32)
declare void @__asan_report_exp_load1(i64, i32)
declare void @__asan_exp_load1(i64, i32)
declare void @__asan_report_exp_load2(i64, i32)
declare void @__asan_exp_load2(i64, i32)
declare void @__asan_report_exp_load4(i64, i32)
declare void @__asan_exp_load4(i64, i32)
declare void @__asan_report_exp_load8(i64, i32)
declare void @__asan_exp_load8(i64, i32)
declare void @__asan_report_exp_load16(i64, i32)
declare void @__asan_exp_load16(i64, i32)
declare void @__asan_report_exp_store_n(i64, i64, i32)
declare void @__asan_exp_storeN(i64, i64, i32)
declare void @__asan_report_exp_store1(i64, i32)
declare void @__asan_exp_store1(i64, i32)
declare void @__asan_report_exp_store2(i64, i32)
declare void @__asan_exp_store2(i64, i32)
declare void @__asan_report_exp_store4(i64, i32)
declare void @__asan_exp_store4(i64, i32)
declare void @__asan_report_exp_store8(i64, i32)
declare void @__asan_exp_store8(i64, i32)
declare void @__asan_report_exp_store16(i64, i32)
declare void @__asan_exp_store16(i64, i32)
declare ptr @memmove(ptr, ptr, i64)
declare ptr @memcpy(ptr, ptr, i64)
declare ptr @memset(ptr, i32, i64)
declare void @__asan_handle_no_return()
declare void @__sanitizer_ptr_cmp(i64, i64)
declare void @__sanitizer_ptr_sub(i64, i64)
declare i1 @llvm.amdgcn.is.shared(ptr nocapture) #1
declare i1 @llvm.amdgcn.is.private(ptr nocapture) #1
declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #2
declare void @__asan_before_dynamic_init(i64)
declare void @__asan_after_dynamic_init()
declare void @__asan_register_globals(i64, i64)
declare void @__asan_unregister_globals(i64, i64)
declare void @__asan_register_image_globals(i64)
declare void @__asan_unregister_image_globals(i64)
declare void @__asan_register_elf_globals(i64, i64, i64)
declare void @__asan_unregister_elf_globals(i64, i64, i64)

define internal void @asan.module_ctor() #3 {
  ret void
}

attributes #0 = { naked nocf_check noinline noredzone nounwind nonlazybind sanitize_address "probe-stack"="inline-asm" "target-cpu"="x86-64" "target-features"="-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float" }
attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #3 = { nounwind }

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

!0 = !{i32 8, !"PIC Level", i32 2}
!1 = !{i32 1, !"Code Model", i32 2}
!2 = !{i32 2, !"RtLibUseGOT", i32 1}
!3 = !{i32 2, !"Dwarf Version", i32 4}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{!"rustc version 1.82.0-nightly (2c93fabd9 2024-08-15)"}
!6 = distinct !DICompileUnit(language: DW_LANG_Rust, file: !7, producer: "clang LLVM (rustc version 1.82.0-nightly (2c93fabd9 2024-08-15))", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
!7 = !DIFile(filename: "/app/example.rs/@/example.1c26dad09674458a-cgu.0", directory: "/app")
!8 = distinct !DISubprogram(name: "page_fault_handler", linkageName: "_ZN7example18page_fault_handler17hdb4acb34a86c6a8bE", scope: !10, file: !9, line: 6, type: !11, scopeLine: 6, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !6, templateParams: !12)
!9 = !DIFile(filename: "example.rs", directory: "/app", checksumkind: CSK_MD5, checksum: "85bf207046f646941a20021898b6c9b6")
!10 = !DINamespace(name: "example", scope: null)
!11 = !DISubroutineType(types: !12)
!12 = !{}
!13 = !DILocation(line: 8, column: 9, scope: !8)
!14 = !{i32 187}

Hits this llvm_unreachable: https://github.com/llvm/llvm-project/blob/bf5cd4220d20d0ee5533d55f463612fbe2980071/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp#L1957

If you don't enable trap on llvm_unreachable in the CMake, we get this instead:

PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace.
Stack dump:
0.  Program arguments: /opt/compiler-explorer/clang-trunk/bin/llc -o /app/output.s -x86-asm-syntax=intel <source>
1.  Running pass 'Function Pass Manager' on module '<source>'.
2.  Running pass 'X86 Assembly Printer' on function '@_ZN7example18page_fault_handler17hdb4acb34a86c6a8bE'
 #0 0x00000000037ec688 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/compiler-explorer/clang-trunk/bin/llc+0x37ec688)
 #1 0x00000000037ea02c SignalHandler(int) Signals.cpp:0:0
 #2 0x00007f26b2442520 (/lib/x86_64-linux-gnu/libc.so.6+0x42520)
 #3 0x0000000002306054 (anonymous namespace)::X86MCCodeEmitter::emitPrefixImpl(unsigned int&, llvm::MCInst const&, llvm::MCSubtargetInfo const&, llvm::SmallVectorImpl<char>&) const X86MCCodeEmitter.cpp:0:0
 #4 0x00000000023093e8 (anonymous namespace)::X86MCCodeEmitter::encodeInstruction(llvm::MCInst const&, llvm::SmallVectorImpl<char>&, llvm::SmallVectorImpl<llvm::MCFixup>&, llvm::MCSubtargetInfo const&) const X86MCCodeEmitter.cpp:0:0
 #5 0x0000000001b8b846 llvm::X86AsmPrinter::emitInstruction(llvm::MachineInstr const*) (/opt/compiler-explorer/clang-trunk/bin/llc+0x1b8b846)
 #6 0x000000000261e787 llvm::AsmPrinter::emitFunctionBody() (/opt/compiler-explorer/clang-trunk/bin/llc+0x261e787)
 #7 0x0000000001b7dedb llvm::X86AsmPrinter::runOnMachineFunction(llvm::MachineFunction&) (/opt/compiler-explorer/clang-trunk/bin/llc+0x1b7dedb)
 #8 0x00000000028aac6b llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (.part.0) MachineFunctionPass.cpp:0:0
 #9 0x0000000002df8272 llvm::FPPassManager::runOnFunction(llvm::Function&) (/opt/compiler-explorer/clang-trunk/bin/llc+0x2df8272)
#10 0x0000000002df8531 llvm::FPPassManager::runOnModule(llvm::Module&) (/opt/compiler-explorer/clang-trunk/bin/llc+0x2df8531)
#11 0x0000000002df77cb llvm::legacy::PassManagerImpl::run(llvm::Module&) (/opt/compiler-explorer/clang-trunk/bin/llc+0x2df77cb)
#12 0x0000000000859e94 compileModule(char**, llvm::LLVMContext&) llc.cpp:0:0
#13 0x000000000074c296 main (/opt/compiler-explorer/clang-trunk/bin/llc+0x74c296)
#14 0x00007f26b2429d90 (/lib/x86_64-linux-gnu/libc.so.6+0x29d90)
#15 0x00007f26b2429e40 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e40)
#16 0x0000000000851f1e _start (/opt/compiler-explorer/clang-trunk/bin/llc+0x851f1e)
Program terminated with signal: SIGSEGV
Compiler returned: 139

It seems to me, given the frequency with which code hits this, that LLVM prefers to put this macro in reachable branches of code. May I suggest renaming it, or perhaps removing the feature that it causes undefined behavior if reached at runtime?

workingjubilee commented 2 months ago

Mind, it is probably the case that rustc should not emit the IR in question, but it seems incorrect to call something unreachable if it isn't. Especially if this causes errors to flow downstream to places in the machine optimizer and emitter which probably shouldn't be asked to handle such questionably-formed IR, and it should instead be eagerly rejected, perhaps with an inquiry as to why the author ever thought that LLVMIR would pass muster.

nikic commented 2 months ago

Reduced test case:

define void @test(ptr %ptr) naked {
  getelementptr i8, ptr %ptr, i64 1
  call void @llvm.trap()
  unreachable
}

Probably the IR verifier should check that the arguments of a naked function are not used?

We don't lower arguments for naked functions, so everything in the backend will blow up if you try to use them.

topperc commented 2 months ago

Should we change SelectionDAGBuilder to have a report_fatal_error instead of llvm_unreachable too? That way compiler optimizations can't send the code down some random path the next time something like this happens?