help with this assertion failure? `isUniqued() && "Expected this to be uniqued"

Anyone have any clues as to what could be causing this? With llvm assertions off it works fine.

llvm-3.9.1.src/lib/IR/Metadata.cpp:540: void llvm::MDNode::resolve(): Assertion `isUniqued() && “Expected this to be uniqued”’ failed.

#3 0x00007ffff6936c82 in __GI___assert_fail (assertion=0x658a598 “isUniqued() && "Expected this to be uniqued"”,
file=0x6589d50 “/home/andy/Downloads/llvm-3.9.1.src/lib/IR/Metadata.cpp”, line=540,
function=0x6596850 llvm::MDNode::resolve()::__PRETTY_FUNCTION__ “void llvm::MDNode::resolve()”) at assert.c:101
#4 0x0000000004757368 in llvm::MDNode::resolve (this=0x79ebaa0)
at /home/andy/Downloads/llvm-3.9.1.src/lib/IR/Metadata.cpp:540
#5 0x0000000004757679 in llvm::MDNode::resolveCycles (this=0x79ebaa0)
at /home/andy/Downloads/llvm-3.9.1.src/lib/IR/Metadata.cpp:589
#6 0x0000000004699c2d in llvm::DIBuilder::finalize (this=0x79c71b0)
at /home/andy/Downloads/llvm-3.9.1.src/lib/IR/DIBuilder.cpp:97
#7 0x000000000150f2ee in ZigLLVMDIBuilderFinalize (dibuilder=0x79c71b0) at /home/andy/dev/zig/src/zig_llvm.cpp:496

Here’s the LLVMDumpModule output:

; ModuleID = ‘./test.zig’
source_filename = “./test.zig”
target datalayout = “e-m:e-i64:64-f80:128-n8:16:32:64-S128”
target triple = “x86_64-unknown-linux-gnu”

@0 = internal unnamed_addr constant i64 4

; Function Attrs: nounwind
declare void @llvm.debugtrap() #0

; Function Attrs: argmemonly nounwind
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) #1

; Function Attrs: argmemonly nounwind
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) #1

; Function Attrs: nounwind
define internal fastcc void @_constDeclsInStruct() #2 !dbg !9 {
Entry:
call fastcc void @_assert(i1 true), !dbg !12
ret void, !dbg !14
}

; Function Attrs: nounwind
define internal fastcc void @_assert(i1) #2 !dbg !15 {
Entry:
%b = alloca i1, align 1
store i1 %0, i1* %b
call void @llvm.dbg.declare(metadata i1* %b, metadata !20, metadata !21), !dbg !22
%1 = load i1, i1* %b, !dbg !23
%2 = icmp eq i1 %1, false, !dbg !26
br i1 %2, label %Then, label %Else, !dbg !26

Then: ; preds = %Entry
call void @llvm.debugtrap(), !dbg !27
unreachable, !dbg !27

Else: ; preds = %Entry
ret void, !dbg !28
}

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

attributes #0 = { nounwind }
attributes #1 = { argmemonly nounwind }
attributes #2 = { nounwind “no-frame-pointer-elim”=“true” “no-frame-pointer-elim-non-leaf” }
attributes #3 = { nounwind readnone }

!llvm.module.flags = !{!0}
= !{!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: !4)
!2 = !DIFile(filename: “./test.zig”, directory: “.”)
!3 = !{}
!4 = !{!5}
!5 = distinct !DIGlobalVariable(name: “count_plus_one”, linkageName: “count_plus_one”, scope: !6, file: !7, line: 6, type: !8, isLocal: true, isDefinition: true, variable: i64 4)
!6 = !DIBasicType(name: “void”, encoding: DW_ATE_unsigned)
!7 = !DIFile(filename: “test.zig”, directory: “.”)
!8 = !DIBasicType(name: “isize”, size: 64, align: 64, encoding: DW_ATE_signed)
!9 = distinct !DISubprogram(name: “constDeclsInStruct”, scope: !7, file: !7, line: 1, type: !10, isLocal: true, isDefinition: true, scopeLine: 1, isOptimized: false, unit: !1, variables: !3)
!10 = !DISubroutineType(types: !11)
!11 = !{!6}
!12 = !DILocation(line: 2, column: 11, scope: !13)
!13 = distinct !DILexicalBlock(scope: !9, file: !7, line: 1, column: 25)
!14 = !DILocation(line: 2, column: 52, scope: !9)
!15 = distinct !DISubprogram(name: “assert”, scope: !7, file: !7, line: 10, type: !16, isLocal: true, isDefinition: true, scopeLine: 10, isOptimized: false, unit: !1, variables: !19)
!16 = !DISubroutineType(types: !17)
!17 = !{!6, !18}
!18 = !DIBasicType(name: “bool”, size: 8, align: 8, encoding: DW_ATE_boolean)
!19 = !{!20}
!20 = !DILocalVariable(name: “b”, arg: 1, scope: !15, file: !7, line: 10, type: !18)
!21 = !DIExpression()
!22 = !DILocation(line: 10, column: 15, scope: !15)
!23 = !DILocation(line: 11, column: 10, scope: !24)
!24 = distinct !DILexicalBlock(scope: !25, file: !7, line: 10, column: 24)
!25 = distinct !DILexicalBlock(scope: !15, file: !7, line: 10, column: 15)
!26 = !DILocation(line: 11, column: 9, scope: !24)
!27 = !DILocation(line: 11, column: 13, scope: !24)
!28 = !DILocation(line: 11, column: 5, scope: !25)

It’s reproducible. Is there anything I can print about this MDNode to find out why it’s not uniqued?

(gdb) print this
$2 = (llvm::MDNode * const) 0x79ebaa0
(gdb) print this[0]
$3 = {llvm::Metadata = {SubclassID = 12 ‘\f’, Storage = 2 ‘\002’, SubclassData16 = 19, SubclassData32 = 0},
NumOperands = 8, NumUnresolved = 0, Context = {Ptr = {Val = {Value = 127843060}}}}

Is there any way to tell which node in the printed IR this MDNode corresponds to?

One more piece of data, I’m not sure this will be meaningful but here’s the source that caused this problem:

fn constDeclsInStruct() {
assert(GenericDataThing(3).count_plus_one == 4);
}
fn GenericDataThing(inline count: isize) → type {
struct {
const count_plus_one = count + 1;
}
}

pub fn assert(b: bool) {
if (!b) @unreachable()
}

Regards,
Andrew

I figured out the problem.

I was doing something like this:

entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), name,
ZigLLVMFileToScope(import->di_file), import->di_file, line + 1);

Later, for normal struct types I would do something like this:

uint64_t debug_size_in_bits = 8LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref);
uint64_t debug_align_in_bits = 8
LLVMABISizeOfType(g->target_data_ref, struct_type->type_ref);
ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
ZigLLVMFileToScope(import->di_file),
buf_ptr(&struct_type->name),
import->di_file, decl_node->line + 1,
debug_size_in_bits,
debug_align_in_bits,
0, nullptr, di_element_types, gen_field_count, 0, nullptr, “”);

ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);
struct_type->di_type = replacement_di_type;

However if the struct had no fields (as you can see in the source in the previous email that this is true) then I ignored the replaceable composite type and used a void type instead. The fix was to still call LLVMReplaceTemporary:

if (struct_type->zero_bits) {
struct_type->type_ref = LLVMVoidType();

  • ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, g->builtin_types.entry_void->di_type);
    struct_type->di_type = g->builtin_types.entry_void->di_type;
    return;
    }

I hope this helps someone else in the future.

Cheers,
Andrew