LLD invalid x86_64 Reference Kind

This is release_50 branch. The LLVM IR file pasted at the bottom of this email causes LLD to hit unreachable. And this fixes it for me:

diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp b/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
index aee9959ca6b…efe23abb914 100644
— a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
+++ b/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
@@ -621,7 +621,7 @@ void ArchHandler_x86_64::applyFixupFinal(
// Fall into llvm_unreachable().
break;
}

  • llvm_unreachable(“invalid x86_64 Reference Kind”);
  • return;
    }

void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,

Without this “fix”:
$ ~/local/llvm5/bin/clang -c test.ll

$ ~/local/llvm5/bin/lld -flavor darwin -o test test.o -arch x86_64 -lSystem
warning: -sdk_version is required when emitting min version load command. Setting sdk version to match provided min version
invalid x86_64 Reference Kind
UNREACHABLE executed at /Users/andy/Downloads/llvm-project/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp:624!
0 lld 0x0000000101730af5 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 37
Stack dump:
0. Program arguments: /Users/andy/local/llvm5/bin/lld -flavor darwin -o test test.o -arch x86_64 -lSystem
Abort trap: 6

I understand that this is:

  1. Not a satisfying fix to the underlying problem
  2. The MACHO code in LLD is bit rotting right now and nobody cares about it

Can we have it as a workaround until anyone is ready to take responsibility for this codebase?

This makes all the Zig behavior tests pass on MacOS.

test.ll:

; ModuleID = ‘test’
source_filename = “test”
target datalayout = “e-m:o-i64:64-f80:128-n8:16:32:64-S128”
target triple = “x86_64-apple-darwin-unknown”

%"[]u8" = type { i8*, i64 }

@__zig_panic_implementation_provided = internal unnamed_addr constant i1 true, align 1
@assert.1 = internal unnamed_addr constant void (i1)* @assert, align 8
@is_test = internal unnamed_addr constant i1 false, align 1
@0 = internal unnamed_addr constant i8* getelementptr inbounds ([24 x i8], [24 x i8]* @1, i64 0, i64 0), align 8
@1 = internal unnamed_addr constant [24 x i8] c"reached unreachable code", align 16
@2 = internal unnamed_addr constant { i8*, i64 } { i8* getelementptr inbounds ([24 x i8], [24 x i8]* @1, i64 0, i64 0), i64 24 }, align 8
@3 = internal unnamed_addr constant i8* getelementptr inbounds ([17 x i8], [17 x i8]* @4, i64 0, i64 0), align 8
@4 = internal unnamed_addr constant [17 x i8] c"assertion failure", align 16
@5 = internal unnamed_addr constant { i8*, i64 } { i8* getelementptr inbounds ([17 x i8], [17 x i8]* @4, i64 0, i64 0), i64 17 }, align 8

; Function Attrs: nobuiltin nounwind sspstrong
define internal fastcc void @assert(i1) unnamed_addr #0 !dbg !25 {
Entry:
%ok = alloca i1, align 1
store i1 %0, i1* %ok
call void @llvm.dbg.declare(metadata i1* %ok, metadata !31, metadata !32), !dbg !33
%1 = load i1, i1* %ok, !dbg !34
%2 = icmp eq i1 %1, false, !dbg !37
br i1 %2, label %Then, label %Else, !dbg !37

Then: ; preds = %Entry
%3 = load i8*, i8** getelementptr inbounds ({ i8*, i64 }, { i8*, i64 }* @2, i32 0, i32 0), !dbg !38
%4 = load i64, i64* getelementptr inbounds ({ i8*, i64 }, { i8*, i64 }* @2, i32 0, i32 1), !dbg !38
call coldcc void @__zig_panic(i8* %3, i64 %4), !dbg !38
unreachable, !dbg !38

Else: ; preds = %Entry
br label %EndIf2, !dbg !41

EndIf2: ; preds = %Else
ret void, !dbg !42
}

; Function Attrs: nobuiltin nounwind sspstrong
define i32 @main(i32, i8** nonnull) #0 !dbg !43 {
Entry:
%argc = alloca i32, align 4
%argv = alloca i8**, align 8
store i32 %0, i32* %argc
call void @llvm.dbg.declare(metadata i32* %argc, metadata !51, metadata !32), !dbg !54
store i8** %1, i8*** %argv
call void @llvm.dbg.declare(metadata i8*** %argv, metadata !52, metadata !32), !dbg !55
call fastcc void @nonConstSwitch(i3 2), !dbg !56
ret i32 0, !dbg !59
}

; Function Attrs: cold nobuiltin noreturn nounwind
define linkonce coldcc void @__zig_panic(i8* nonnull readonly, i64) #1 !dbg !60 {
Entry:
%2 = alloca %"[]u8", align 8
%message_ptr = alloca i8*, align 8
%message_len = alloca i64, align 8
store i8* %0, i8** %message_ptr
call void @llvm.dbg.declare(metadata i8** %message_ptr, metadata !67, metadata !32), !dbg !70
store i64 %1, i64* %message_len
call void @llvm.dbg.declare(metadata i64* %message_len, metadata !68, metadata !32), !dbg !71
%3 = load i64, i64* %message_len, !dbg !72
%4 = load i8*, i8** %message_ptr, !dbg !76
%5 = getelementptr inbounds %"[]u8", %"[]u8"* %2, i32 0, i32 0, !dbg !76
%6 = getelementptr inbounds i8, i8* %4, i64 0, !dbg !76
store i8* %6, i8** %5, !dbg !76
%7 = getelementptr inbounds %"[]u8", %"[]u8"* %2, i32 0, i32 1, !dbg !76
%8 = sub nsw i64 %3, 0, !dbg !76
store i64 %8, i64* %7, !dbg !76
call fastcc void @panic(%"[]u8"* byval %2), !dbg !77
unreachable, !dbg !77
}

; Function Attrs: nobuiltin nounwind sspstrong
define internal fastcc void @nonConstSwitch(i3) unnamed_addr #0 !dbg !78 {
Entry:
%foo = alloca i3, align 1
%val = alloca i32, align 4
store i3 %0, i3* %foo
call void @llvm.dbg.declare(metadata i3* %foo, metadata !82, metadata !32), !dbg !87
%1 = load i3, i3* %foo, !dbg !88
switch i3 %1, label %SwitchElse [
i3 0, label %SwitchProng
i3 1, label %SwitchProng1
i3 2, label %SwitchProng2
i3 3, label %SwitchProng3
], !dbg !88

SwitchProng: ; preds = %Entry
br label %SwitchEnd, !dbg !88

SwitchProng1: ; preds = %Entry
br label %SwitchEnd, !dbg !88

SwitchProng2: ; preds = %Entry
br label %SwitchEnd, !dbg !88

SwitchProng3: ; preds = %Entry
br label %SwitchEnd, !dbg !88

SwitchElse: ; preds = %Entry
%2 = load i8*, i8** getelementptr inbounds ({ i8*, i64 }, { i8*, i64 }* @2, i32 0, i32 0), !dbg !88
%3 = load i64, i64* getelementptr inbounds ({ i8*, i64 }, { i8*, i64 }* @2, i32 0, i32 1), !dbg !88
call coldcc void @__zig_panic(i8* %2, i64 %3), !dbg !88
unreachable, !dbg !88

SwitchEnd: ; preds = %SwitchProng3, %SwitchProng2, %SwitchProng1, %SwitchProng
%4 = phi i32 [ 1, %SwitchProng ], [ 2, %SwitchProng1 ], [ 3, %SwitchProng2 ], [ 4, %SwitchProng3 ], !dbg !88
store i32 %4, i32* %val, !dbg !89
call void @llvm.dbg.declare(metadata i32* %val, metadata !83, metadata !32), !dbg !89
%5 = load i32, i32* %val, !dbg !90
%6 = icmp eq i32 %5, 3, !dbg !92
call fastcc void @assert(i1 %6), !dbg !93
ret void, !dbg !94
}

; Function Attrs: nobuiltin noreturn nounwind sspstrong
define internal fastcc void @panic(%"[]u8"* byval nonnull readonly) unnamed_addr #2 !dbg !95 {
Entry:
call void @llvm.dbg.declare(metadata %"[]u8"* %0, metadata !104, metadata !32), !dbg !105
br label %WhileCond, !dbg !106

WhileCond: ; preds = %WhileCond, %Entry
br label %WhileCond, !dbg !106
}

; Function Attrs: nounwind readnone speculatable
declare void @llvm.dbg.declare(metadata, metadata, metadata) #3

; Function Attrs: nounwind
declare void @llvm.stackprotector(i8*, i8**) #4

attributes #0 = { nobuiltin nounwind sspstrong “no-frame-pointer-elim”=“true” “no-frame-pointer-elim-non-leaf” “stack-protector-buffer-size”=“4” }
attributes #1 = { cold nobuiltin noreturn nounwind “no-frame-pointer-elim”=“true” “no-frame-pointer-elim-non-leaf” }
attributes #2 = { nobuiltin noreturn nounwind sspstrong “no-frame-pointer-elim”=“true” “no-frame-pointer-elim-non-leaf” “stack-protector-buffer-size”=“4” }
attributes #3 = { nounwind readnone speculatable }
attributes #4 = { nounwind }

!llvm.module.flags = !{!0}
llvm.dbg.cu = !{!1}

!0 = !{i32 2, !“Debug Info Version”, i32 3}
!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: “zig 0.0.0”, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !3, globals: !19)
!2 = !DIFile(filename: “test”, directory: “.”)
!3 = !{!4, !12}
!4 = !DICompositeType(tag: DW_TAG_enumeration_type, name: “Foo”, scope: !5, file: !5, line: 17, baseType: !6, size: 8, align: 8, elements: !7)
!5 = !DIFile(filename: “test.zig”, directory: “/Users/andy/dev/zig/build”)
!6 = !DIBasicType(name: “u3”, size: 8, encoding: DW_ATE_unsigned)
!7 = !{!8, !9, !10, !11}
!8 = !DIEnumerator(name: “A”, value: 0)
!9 = !DIEnumerator(name: “B”, value: 1)
!10 = !DIEnumerator(name: “C”, value: 2)
!11 = !DIEnumerator(name: “D”, value: 3)
!12 = !DICompositeType(tag: DW_TAG_enumeration_type, name: “GlobalLinkage”, scope: !13, file: !13, line: 138, baseType: !6, size: 8, align: 8, elements: !14)
!13 = !DIFile(filename: “builtin.zig”, directory: “/Users/andy/dev/zig/build/zig-cache”)
!14 = !{!15, !16, !17, !18}
!15 = !DIEnumerator(name: “Internal”, value: 0)
!16 = !DIEnumerator(name: “Strong”, value: 1)
!17 = !DIEnumerator(name: “Weak”, value: 2)
!18 = !DIEnumerator(name: “LinkOnce”, value: 3)
!19 = !{!20, !23}
!20 = !DIGlobalVariableExpression(var: !21)
!21 = distinct !DIGlobalVariable(name: “__zig_panic_implementation_provided”, linkageName: “__zig_panic_implementation_provided”, scope: !13, file: !13, line: 201, type: !22, isLocal: true, isDefinition: true)
!22 = !DIBasicType(name: “bool”, size: 8, encoding: DW_ATE_boolean)
!23 = !DIGlobalVariableExpression(var: !24)
!24 = distinct !DIGlobalVariable(name: “is_test”, linkageName: “is_test”, scope: !13, file: !13, line: 194, type: !22, isLocal: true, isDefinition: true)
!25 = distinct !DISubprogram(name: “assert”, scope: !26, file: !26, line: 14, type: !27, isLocal: true, isDefinition: true, scopeLine: 14, isOptimized: false, unit: !1, variables: !30)
!26 = !DIFile(filename: “debug.zig”, directory: “/Users/andy/dev/zig/build/lib/zig/std”)
!27 = !DISubroutineType(types: !28)
!28 = !{!29, !22}
!29 = !DIBasicType(name: “void”, encoding: DW_ATE_unsigned)
!30 = !{!31}
!31 = !DILocalVariable(name: “ok”, arg: 1, scope: !25, file: !26, line: 14, type: !22)
!32 = !DIExpression()
!33 = !DILocation(line: 14, column: 15, scope: !25)
!34 = !DILocation(line: 15, column: 10, scope: !35)
!35 = distinct !DILexicalBlock(scope: !36, file: !26, line: 14, column: 25)
!36 = distinct !DILexicalBlock(scope: !25, file: !26, line: 14, column: 15)
!37 = !DILocation(line: 15, column: 9, scope: !35)
!38 = !DILocation(line: 21, column: 13, scope: !39)
!39 = distinct !DILexicalBlock(scope: !40, file: !26, line: 20, column: 16)
!40 = distinct !DILexicalBlock(scope: !35, file: !26, line: 15, column: 14)
!41 = !DILocation(line: 15, column: 5, scope: !35)
!42 = !DILocation(line: 15, column: 5, scope: !36)
!43 = distinct !DISubprogram(name: “main”, scope: !5, file: !5, line: 4, type: !44, isLocal: false, isDefinition: true, scopeLine: 4, isOptimized: false, unit: !1, variables: !50)
!44 = !DISubroutineType(types: !45)
!45 = !{!46, !46, !47}
!46 = !DIBasicType(name: “c_int”, size: 32, encoding: DW_ATE_signed)
!47 = !DIDerivedType(tag: DW_TAG_pointer_type, name: “&&u8”, baseType: !48, size: 64, align: 64)
!48 = !DIDerivedType(tag: DW_TAG_pointer_type, name: “&u8”, baseType: !49, size: 64, align: 64)
!49 = !DIBasicType(name: “u8”, size: 8, encoding: DW_ATE_unsigned_char)
!50 = !{!51, !52}
!51 = !DILocalVariable(name: “argc”, arg: 1, scope: !43, file: !5, line: 4, type: !46)
!52 = !DILocalVariable(name: “argv”, arg: 2, scope: !53, file: !5, line: 4, type: !47)
!53 = distinct !DILexicalBlock(scope: !43, file: !5, line: 4, column: 16)
!54 = !DILocation(line: 4, column: 16, scope: !43)
!55 = !DILocation(line: 4, column: 29, scope: !53)
!56 = !DILocation(line: 5, column: 19, scope: !57)
!57 = distinct !DILexicalBlock(scope: !58, file: !5, line: 4, column: 50)
!58 = distinct !DILexicalBlock(scope: !53, file: !5, line: 4, column: 29)
!59 = !DILocation(line: 6, column: 5, scope: !57)
!60 = distinct !DISubprogram(name: “__zig_panic”, scope: !61, file: !61, line: 7, type: !62, isLocal: false, isDefinition: true, scopeLine: 7, isOptimized: false, unit: !1, variables: !66)
!61 = !DIFile(filename: “zigrt.zig”, directory: “/Users/andy/dev/zig/build/lib/zig/std/special”)
!62 = !DISubroutineType(types: !63)
!63 = !{!29, !64, !65}
!64 = !DIDerivedType(tag: DW_TAG_pointer_type, name: “&const u8”, baseType: !49, size: 64, align: 64)
!65 = !DIBasicType(name: “usize”, size: 64, encoding: DW_ATE_unsigned)
!66 = !{!67, !68}
!67 = !DILocalVariable(name: “message_ptr”, arg: 1, scope: !60, file: !61, line: 7, type: !64)
!68 = !DILocalVariable(name: “message_len”, arg: 2, scope: !69, file: !61, line: 7, type: !65)
!69 = distinct !DILexicalBlock(scope: !60, file: !61, line: 7, column: 30)
!70 = !DILocation(line: 7, column: 30, scope: !60)
!71 = !DILocation(line: 7, column: 54, scope: !69)
!72 = !DILocation(line: 12, column: 48, scope: !73)
!73 = distinct !DILexicalBlock(scope: !74, file: !61, line: 11, column: 54)
!74 = distinct !DILexicalBlock(scope: !75, file: !61, line: 7, column: 86)
!75 = distinct !DILexicalBlock(scope: !69, file: !61, line: 7, column: 54)
!76 = !DILocation(line: 12, column: 43, scope: !73)
!77 = !DILocation(line: 12, column: 31, scope: !73)
!78 = distinct !DISubprogram(name: “nonConstSwitch”, scope: !5, file: !5, line: 8, type: !79, isLocal: true, isDefinition: true, scopeLine: 8, isOptimized: false, unit: !1, variables: !81)
!79 = !DISubroutineType(types: !80)
!80 = !{!29, !4}
!81 = !{!82, !83}
!82 = !DILocalVariable(name: “foo”, arg: 1, scope: !78, file: !5, line: 8, type: !4)
!83 = !DILocalVariable(name: “val”, scope: !84, file: !5, line: 9, type: !86)
!84 = distinct !DILexicalBlock(scope: !85, file: !5, line: 8, column: 29)
!85 = distinct !DILexicalBlock(scope: !78, file: !5, line: 8, column: 19)
!86 = !DIBasicType(name: “i32”, size: 32, encoding: DW_ATE_signed)
!87 = !DILocation(line: 8, column: 19, scope: !78)
!88 = !DILocation(line: 9, column: 17, scope: !84)
!89 = !DILocation(line: 9, column: 5, scope: !84)
!90 = !DILocation(line: 15, column: 12, scope: !91)
!91 = distinct !DILexicalBlock(scope: !84, file: !5, line: 9, column: 5)
!92 = !DILocation(line: 15, column: 16, scope: !91)
!93 = !DILocation(line: 15, column: 11, scope: !91)
!94 = !DILocation(line: 8, column: 29, scope: !85)
!95 = distinct !DISubprogram(name: “panic”, scope: !5, file: !5, line: 1, type: !96, isLocal: true, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !1, variables: !103)
!96 = !DISubroutineType(types: !97)
!97 = !{!29, !98}
!98 = !DIDerivedType(tag: DW_TAG_pointer_type, name: “&const []const u8”, baseType: !99, size: 64, align: 64)
!99 = !DICompositeType(tag: DW_TAG_structure_type, name: “[]u8”, size: 128, align: 128, elements: !100)
!100 = !{!101, !102}
!101 = !DIDerivedType(tag: DW_TAG_member, name: “ptr”, scope: !99, baseType: !48, size: 64, align: 64)
!102 = !DIDerivedType(tag: DW_TAG_member, name: “len”, scope: !99, baseType: !65, size: 64, align: 64, offset: 64)
!103 = !{!104}
!104 = !DILocalVariable(name: “msg”, arg: 1, scope: !95, file: !5, line: 1, type: !99)
!105 = !DILocation(line: 1, column: 14, scope: !95)
!106 = !DILocation(line: 1, column: 45, scope: !107)
!107 = distinct !DILexicalBlock(scope: !108, file: !5, line: 1, column: 43)
!108 = distinct !DILexicalBlock(scope: !95, file: !5, line: 1, column: 14)

This is release_50 branch. The LLVM IR file pasted at the bottom of this
email causes LLD to hit unreachable. And this fixes it for me:

diff --git a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
b/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
index aee9959ca6b..efe23abb914 100644
--- a/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
+++ b/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp
@@ -621,7 +621,7 @@ void ArchHandler_x86_64::applyFixupFinal(
     // Fall into llvm_unreachable().
     break;
   }
- llvm_unreachable("invalid x86_64 Reference Kind");
+ return;
}

void ArchHandler_x86_64::applyFixupRelocatable(const Reference &ref,

Without this "fix":
$ ~/local/llvm5/bin/clang -c test.ll
$ ~/local/llvm5/bin/lld -flavor darwin -o test test.o -arch x86_64 -lSystem
warning: -sdk_version is required when emitting min version load command.
Setting sdk version to match provided min version
invalid x86_64 Reference Kind
UNREACHABLE executed at /Users/andy/Downloads/llvm-
project/lld/lib/ReaderWriter/MachO/ArchHandler_x86_64.cpp:624!
0 lld 0x0000000101730af5 llvm::sys::PrintStackTrace(llvm::raw_ostream&)
+ 37
Stack dump:
0. Program arguments: /Users/andy/local/llvm5/bin/lld -flavor darwin -o
test test.o -arch x86_64 -lSystem
Abort trap: 6

I understand that this is:
1. Not a satisfying fix to the underlying problem
2. The MACHO code in LLD is bit rotting right now and nobody cares about it

It seems both points were unfortunately true.

Even though I do not fully understand the issue, I can say that this fix is
not satisfactory as it just converts an llvm_unreachable for a
(theoretically) fully-covered switch to a return so that it won't raise a
runtime error instead of reporting that something's wrong. That may be
useful as a temporary workaround for your local repository, but I doubt we
really want to check this in to the main repository of lld because it is in
theory obviously incorrect thing to do.

That brings us to the point the second point that mach-o lld is not
maintained well. Virtually no active development has been made in the past
few years while there's maintenance cost associated with it, and it looks
like if you seriously attempt to try to use it, you are likely to hit some
issue. Perhaps we should start discussing to deprecate it if no one wants
to revive its development.