julia
dfda6311 - Fix egal codegen on large (>512 bytes), split representation isbitsegal types (#62095)

Commit
28 days ago
Fix egal codegen on large (>512 bytes), split representation isbitsegal types (#62095) Large (> 512 bytes) struct that are isbitsegal emit a memcmp instead of a comparison for each field. However, this requires that both the arguments to the memcmp be in the canonical struct layout, which is a problem for structs in the split layout because of the calling convention. Here's an example: ```julia mutable struct Box v::Int end struct Mixed obj::Box # Mixed must be bitsegal (Any won't work) num::Int end const N = 40 const T = NTuple{N, Mixed} f(a::T, b::T) = a === b x = ntuple(i -> Mixed(Box(i), i), Val(N)) x === x # true f(x, x) # false ``` `N=40` is chosen to make the resulting `NTuple{N, Mixed}` at least 512 bytes. To make the IR a little more readable, here's an example of the code we'd generate if the threshold for emitting `memcmp` for `===` was 64 bytes: ```llvm define swiftcc i8 @julia_f_0(ptr nonnull swiftself "gcstack" %0, ptr addrspace(11) noundef nonnull readonly align 8 captures(none) dereferenceable(32) %1, ptr noundef nonnull readonly align 8 captures(none) dereferenceable(16) %2, ptr addrspace(11) noundef nonnull readonly align 8 captures(none) dereferenceable(32) %3, ptr noundef nonnull readonly align 8 captures(none) dereferenceable(16) %4) #0 { ; Copy of each argument combined by `recombine_value`. %6 = alloca [2 x { ptr addrspace(10), i64 }], align 8 %7 = alloca [2 x { ptr addrspace(10), i64 }], align 8 ; Stores generated by `value_to_pointer` store ptr addrspace(10) null, ptr %7, align 8 %8 = getelementptr inbounds i8, ptr %7, i32 16 store ptr addrspace(10) null, ptr %8, align 8 store ptr addrspace(10) null, ptr %6, align 8 %9 = getelementptr inbounds i8, ptr %6, i32 16 store ptr addrspace(10) null, ptr %9, align 8 %10 = getelementptr inbounds i8, ptr %0, i32 -152 %11 = getelementptr inbounds i8, ptr %10, i32 168 %12 = load ptr, ptr %11, align 8, !tbaa !3 %13 = getelementptr inbounds i8, ptr %12, i32 16 %14 = load atomic ptr, ptr %13 monotonic, align 8, !tbaa !7, !invariant.load !9 fence syncscope("singlethread") seq_cst call void @julia.safepoint(ptr %14) fence syncscope("singlethread") seq_cst ; Copies from `recombine_value` %15 = load ptr addrspace(10), ptr %2, align 8, !tbaa !7, !invariant.load !9, !alias.scope !10, !noalias !13 store ptr addrspace(10) %15, ptr %6, align 8, !tbaa !18, !alias.scope !20, !noalias !21 %16 = getelementptr inbounds i8, ptr %6, i32 8 %17 = getelementptr inbounds i8, ptr addrspace(11) %1, i32 8 call void @llvm.memcpy.p0.p11.i64(ptr align 8 %16, ptr addrspace(11) align 8 %17, i64 8, i1 false), !tbaa !22, !alias.scope !23, !noalias !24 %18 = getelementptr inbounds i8, ptr %2, i32 8 %19 = load ptr addrspace(10), ptr %18, align 8, !tbaa !7, !invariant.load !9, !alias.scope !10, !noalias !13 %20 = getelementptr inbounds i8, ptr %6, i32 16 store ptr addrspace(10) %19, ptr %20, align 8, !tbaa !18, !alias.scope !20, !noalias !21 %21 = getelementptr inbounds i8, ptr %6, i32 24 %22 = getelementptr inbounds i8, ptr addrspace(11) %1, i32 24 call void @llvm.memcpy.p0.p11.i64(ptr align 8 %21, ptr addrspace(11) align 8 %22, i64 8, i1 false), !tbaa !22, !alias.scope !23, !noalias !24 %23 = load ptr addrspace(10), ptr %4, align 8, !tbaa !7, !invariant.load !9, !alias.scope !10, !noalias !13 store ptr addrspace(10) %23, ptr %7, align 8, !tbaa !18, !alias.scope !20, !noalias !21 %24 = getelementptr inbounds i8, ptr %7, i32 8 %25 = getelementptr inbounds i8, ptr addrspace(11) %3, i32 8 call void @llvm.memcpy.p0.p11.i64(ptr align 8 %24, ptr addrspace(11) align 8 %25, i64 8, i1 false), !tbaa !22, !alias.scope !23, !noalias !24 %26 = getelementptr inbounds i8, ptr %4, i32 8 %27 = load ptr addrspace(10), ptr %26, align 8, !tbaa !7, !invariant.load !9, !alias.scope !10, !noalias !13 %28 = getelementptr inbounds i8, ptr %7, i32 16 store ptr addrspace(10) %27, ptr %28, align 8, !tbaa !18, !alias.scope !20, !noalias !21 %29 = getelementptr inbounds i8, ptr %7, i32 24 %30 = getelementptr inbounds i8, ptr addrspace(11) %3, i32 24 call void @llvm.memcpy.p0.p11.i64(ptr align 8 %29, ptr addrspace(11) align 8 %30, i64 8, i1 false), !tbaa !22, !alias.scope !23, !noalias !24 %31 = load ptr addrspace(10), ptr %2, align 8, !tbaa !7, !invariant.load !9, !alias.scope !10, !noalias !13 %32 = getelementptr inbounds i8, ptr %2, i32 8 %33 = load ptr addrspace(10), ptr %32, align 8, !tbaa !7, !invariant.load !9, !alias.scope !10, !noalias !13 %34 = load ptr addrspace(10), ptr %4, align 8, !tbaa !7, !invariant.load !9, !alias.scope !10, !noalias !13 %35 = getelementptr inbounds i8, ptr %4, i32 8 %36 = load ptr addrspace(10), ptr %35, align 8, !tbaa !7, !invariant.load !9, !alias.scope !10, !noalias !13 %37 = call i32 @memcmp(ptr %6, ptr %7, i64 32) [ "jl_roots"(ptr addrspace(10) %31, ptr addrspace(10) %33, ptr addrspace(10) %34, ptr addrspace(10) %36) ], !tbaa !7, !alias.scope !10, !noalias !13 %38 = icmp eq i32 %37, 0 %39 = zext i1 %38 to i8 ret i8 %39 } !0 = !{i32 2, !"Dwarf Version", i32 4} !1 = !{i32 2, !"Debug Info Version", i32 3} !2 = !{i32 2, !"julia.optlevel", i32 2} !3 = !{!4, !4, i64 0} !4 = !{!"jtbaa_gcframe", !5, i64 0} !5 = !{!"jtbaa", !6, i64 0} !6 = !{!"jtbaa"} !7 = !{!8, !8, i64 0, i64 1} !8 = !{!"jtbaa_const", !5, i64 0} !9 = !{} !10 = !{!11} !11 = !{!"jnoalias_const", !12} !12 = !{!"jnoalias"} !13 = !{!14, !15, !16, !17} !14 = !{!"jnoalias_gcframe", !12} !15 = !{!"jnoalias_stack", !12} !16 = !{!"jnoalias_data", !12} !17 = !{!"jnoalias_typemd", !12} !18 = !{!19, !19, i64 0} !19 = !{!"jtbaa_stack", !5, i64 0} !20 = !{!15} !21 = !{!14, !16, !17, !11} !22 = !{!5, !5, i64 0} !23 = !{!11, !15} !24 = !{!14, !16, !17} !25 = !{!14} !26 = !{!15, !16, !17, !11} !27 = !{i64 32} !28 = !{i64 8} !29 = !{!30, !30, i64 0} !30 = !{!"jtbaa_immut", !31, i64 0} !31 = !{!"jtbaa_value", !32, i64 0} !32 = !{!"jtbaa_data", !5, i64 0} !33 = !{!16} !34 = !{!14, !15, !17, !11} !35 = !{i64 1} ``` The `memcmp` inherits its alias info from the arguments, meaning it gets `jtbaa_const` as its TBAA, and the `jnoalias_const` alias scope, with every other scope in the noalias set. LLVM concludes the copies from the arguments to the combined versions on the stack aren't read and deletes them, leaving us with uninitialized stack memory where the non-pointers should be. ``` Trying to eliminate MemoryDefs at the end of the function Check if def 16 = MemoryDef(15) ( store i64 %31, ptr %29, align 8, !tbaa !22, !alias.scope !23, !noalias !24) is at the end the function ... MemoryDef is not accessed until the end of the function Check if def 15 = MemoryDef(14) ( store ptr addrspace(10) %28, ptr %8, align 8, !tbaa !18, !alias.scope !20, !noalias !21) is at the end the function ... MemoryDef is not accessed until the end of the function Check if def 14 = MemoryDef(13) ( store i64 %26, ptr %24, align 8, !tbaa !22, !alias.scope !23, !noalias !24) is at the end the function ... MemoryDef is not accessed until the end of the function Check if def 13 = MemoryDef(12) ( store ptr addrspace(10) %23, ptr %7, align 8, !tbaa !18, !alias.scope !20, !noalias !21) is at the end the function ... MemoryDef is not accessed until the end of the function Check if def 12 = MemoryDef(11) ( store i64 %22, ptr %20, align 8, !tbaa !22, !alias.scope !23, !noalias !24) is at the end the function ... MemoryDef is not accessed until the end of the function Check if def 11 = MemoryDef(10) ( store ptr addrspace(10) %19, ptr %9, align 8, !tbaa !18, !alias.scope !20, !noalias !21) is at the end the function ... MemoryDef is not accessed until the end of the function Check if def 10 = MemoryDef(9) ( store i64 %17, ptr %15, align 8, !tbaa !22, !alias.scope !23, !noalias !24) is at the end the function ... MemoryDef is not accessed until the end of the function Check if def 9 = MemoryDef(8) ( store ptr addrspace(10) %14, ptr %6, align 8, !tbaa !18, !alias.scope !20, !noalias !21) is at the end the function ... MemoryDef is not accessed until the end of the function ``` Claude Fable 5 ran into this bug while I was having make a reproducer for a different bug.
Author
Parents
Loading