Skip to content
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

Segmentation fault in Julia v1.11.4 due to unaligned SIMD loads #57713

Closed
dzhang314 opened this issue Mar 11, 2025 · 10 comments
Closed

Segmentation fault in Julia v1.11.4 due to unaligned SIMD loads #57713

dzhang314 opened this issue Mar 11, 2025 · 10 comments

Comments

@dzhang314
Copy link
Contributor

There is a serious regression in Julia v1.11 that makes it completely unusable for my applications built on top of SIMD.jl + MultiFloats.jl. I think it was supposed to be fixed in #56937 and #56938, but I still observe the issue in v1.11.4.

If we have a struct with a member of type NTuple{N,VecElement{T}}, reading that struct member from memory generates an aligned vector load instruction. This is a serious problem because allocations are no longer 64-byte-aligned in Julia v1.11, and AVX-512 loads segfault if the target address is not 64-byte aligned. This makes it impossible to work with these structs, which badly breaks SIMD.jl. We cannot even print them:

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.11.4 (2025-03-10)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> struct S; data::NTuple{8,VecElement{Float64}}; end

julia> for _ = 1:10; v = Vector{S}(undef, 1); println(v); end
S[
[22569] signal 11 (128): Segmentation fault
in expression starting at REPL[2]:1
getindex at ./essentials.jl:917 [inlined]
show_delim_array at ./show.jl:1397
show_delim_array at ./show.jl:1387 [inlined]
show_vector at ./arrayshow.jl:530
show_vector at ./arrayshow.jl:515 [inlined]
show at ./arrayshow.jl:486 [inlined]
print at ./strings/io.jl:35
print at ./strings/io.jl:46
println at ./strings/io.jl:75
unknown function (ip: 0x70b1b3f04e46)
println at ./coreio.jl:4
top-level scope at ./REPL[2]:1
jl_toplevel_eval_flex at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/src/toplevel.c:934
jl_toplevel_eval_flex at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/src/toplevel.c:886
ijl_toplevel_eval_in at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/src/toplevel.c:994
eval at ./boot.jl:430 [inlined]
eval_user_input at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:245
repl_backend_loop at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:342
#start_repl_backend#59 at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:327
start_repl_backend at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:324
#run_repl#72 at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:483
run_repl at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/usr/share/julia/stdlib/v1.11/REPL/src/REPL.jl:469
jfptr_run_repl_10102 at /home/dkzhang/.julia/juliaup/julia-1.11.4+0.x64.linux.gnu/share/julia/compiled/v1.11/REPL/u0gqU_PBQaY.so (unknown line)
#1150 at ./client.jl:446
jfptr_YY.1150_14761 at /home/dkzhang/.julia/juliaup/julia-1.11.4+0.x64.linux.gnu/share/julia/compiled/v1.11/REPL/u0gqU_PBQaY.so (unknown line)
jl_apply at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/src/julia.h:2157 [inlined]
jl_f__call_latest at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/src/builtins.c:875
#invokelatest#2 at ./essentials.jl:1055 [inlined]
invokelatest at ./essentials.jl:1052 [inlined]
run_main_repl at ./client.jl:430
repl_main at ./client.jl:567 [inlined]
_start at ./client.jl:541
jfptr__start_73560 at /home/dkzhang/.julia/juliaup/julia-1.11.4+0.x64.linux.gnu/lib/julia/sys.so (unknown line)
jl_apply at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/src/julia.h:2157 [inlined]
true_main at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/src/jlapi.c:900
jl_repl_entrypoint at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/src/jlapi.c:1059
main at /cache/build/builder-amdci5-5/julialang/julia-release-1-dot-11/cli/loader_exe.c:58
unknown function (ip: 0x70b1b522a1c9)
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
unknown function (ip: 0x4010b8)
Allocations: 800305 (Pool: 800275; Big: 30); GC: 1
Segmentation fault (core dumped)

Here, I'm performing 10 trials to be conservative -- on my machine, I always get a segfault in the first 3-4 tries. We can see the problematic load instruction using code_native:

julia> code_native(getindex, (Vector{S}, Int))
	.text
	.file	"getindex"
	.globl	julia_getindex_399              # -- Begin function julia_getindex_399
	.p2align	4, 0x90
	.type	julia_getindex_399,@function
julia_getindex_399:                     # @julia_getindex_399
; Function Signature: getindex(Array{Main.S, 1}, Int64)
; ┌ @ essentials.jl:914 within `getindex`
# %bb.0:                                # %top
; │ @ essentials.jl within `getindex`
	#DEBUG_VALUE: getindex:A <- [DW_OP_deref] $rsi
	#DEBUG_VALUE: getindex:i <- $rdx
	#DEBUG_VALUE: getindex:A <- [DW_OP_deref] 0
	push	rbp
	mov	rbp, rsp
	sub	rsp, 16
; │ @ essentials.jl:916 within `getindex`
	lea	rax, [rdx - 1]
	cmp	rax, qword ptr [rsi + 16]
	jae	.LBB0_2
# %bb.1:                                # %L15
; │ @ essentials.jl:917 within `getindex`
	mov	rcx, qword ptr [rsi]
	shl	rax, 6
	vmovaps	zmm0, zmmword ptr [rcx + rax]
	vmovaps	zmmword ptr [rdi], zmm0
	mov	rax, rdi
	add	rsp, 16
	pop	rbp
	vzeroupper
	ret
.LBB0_2:                                # %L12
; │ @ essentials.jl:916 within `getindex`
	mov	qword ptr [rbp - 8], rdx
	movabs	rcx, offset j_throw_boundserror_411
	lea	rax, [rbp - 8]
	mov	rdi, rsi
	mov	rsi, rax
	call	rcx
.Lfunc_end0:
	.size	julia_getindex_399, .Lfunc_end0-julia_getindex_399
; └
                                        # -- End function
	.type	".L+Main.S#401",@object         # @"+Main.S#401"
	.section	.rodata,"a",@progbits
	.p2align	3, 0x0
".L+Main.S#401":
	.quad	".L+Main.S#401.jit"
	.size	".L+Main.S#401", 8

.set ".L+Main.S#401.jit", 124970931979792
	.size	".L+Main.S#401.jit", 8
	.section	".note.GNU-stack","",@progbits

Either that vmovaps needs to be a vmovups, or all allocations need to be 64-byte-aligned again.

@dzhang314
Copy link
Contributor Author

Curiously, this issue does not occur with Vector{NTuple{8,VecElement{Float64}}}, which correctly generates an unaligned load. This suggests that alignment constraints are not being correctly propagated from struct members to their parent structs.

julia> code_native(getindex, (Vector{NTuple{8,VecElement{Float64}}}, Int))
	.text
	.file	"getindex"
	.globl	julia_getindex_573              # -- Begin function julia_getindex_573
	.p2align	4, 0x90
	.type	julia_getindex_573,@function
julia_getindex_573:                     # @julia_getindex_573
; Function Signature: getindex(Array{NTuple{8, VecElement{Float64}}, 1}, Int64)
; ┌ @ essentials.jl:914 within `getindex`
# %bb.0:                                # %top
; │ @ essentials.jl within `getindex`
	#DEBUG_VALUE: getindex:A <- [DW_OP_deref] $rdi
	#DEBUG_VALUE: getindex:i <- $rsi
	#DEBUG_VALUE: getindex:A <- [DW_OP_deref] 0
	push	rbp
	mov	rbp, rsp
	sub	rsp, 16
; │ @ essentials.jl:916 within `getindex`
	lea	rax, [rsi - 1]
	cmp	rax, qword ptr [rdi + 16]
	jae	.LBB0_2
# %bb.1:                                # %L15
; │ @ essentials.jl:917 within `getindex`
	mov	rcx, qword ptr [rdi]
	shl	rax, 6
	vmovups	zmm0, zmmword ptr [rcx + rax]
	add	rsp, 16
	pop	rbp
	ret
.LBB0_2:                                # %L12
; │ @ essentials.jl:916 within `getindex`
	mov	qword ptr [rbp - 8], rsi
	movabs	rax, offset j_throw_boundserror_585
	lea	rsi, [rbp - 8]
	call	rax
.Lfunc_end0:
	.size	julia_getindex_573, .Lfunc_end0-julia_getindex_573
; └
                                        # -- End function
	.type	".L+Core.Tuple#575",@object     # @"+Core.Tuple#575"
	.section	.rodata,"a",@progbits
	.p2align	3, 0x0
".L+Core.Tuple#575":
	.quad	".L+Core.Tuple#575.jit"
	.size	".L+Core.Tuple#575", 8

.set ".L+Core.Tuple#575.jit", 124970931979984
	.size	".L+Core.Tuple#575.jit", 8
	.section	".note.GNU-stack","",@progbits

Note that vmovups is used above, while vmovaps is used in the erroneous code for Vector{S}.

@vchuravy
Copy link
Member

What is versioninfo(;verbose=true)?

@dzhang314
Copy link
Contributor Author

@vchuravy I'm on Linux x86-64 using an AMD Ryzen 9 9950X CPU (Zen 5 microarchitecture). Full info below:

julia> versioninfo(; verbose=true)
Julia Version 1.11.4
Commit 8561cc3d68d (2025-03-10 11:36 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
      Linux Mint 22.1
  uname: Linux 6.8.0-55-generic #57-Ubuntu SMP PREEMPT_DYNAMIC Wed Feb 12 23:42:21 UTC 2025 x86_64 x86_64
  CPU: AMD Ryzen 9 9950X 16-Core Processor: 
                 speed         user         nice          sys         idle          irq
       #1-32   600 MHz      42279 s        151 s      13250 s    4906320 s          0 s
  Memory: 62.39460754394531 GB (56782.5390625 MB free)
  Uptime: 15522.82 sec
  Load Avg:  0.39  0.47  0.51
  WORD_SIZE: 64
  LLVM: libLLVM-16.0.6 (ORCJIT, generic)
Threads: 1 default, 0 interactive, 1 GC (on 32 virtual cores)
Environment:
  XDG_SESSION_PATH = /org/freedesktop/DisplayManager/Session0
  HOME = /home/dkzhang
  XDG_SEAT_PATH = /org/freedesktop/DisplayManager/Seat0
  TERM = xterm-256color
  PATH = /home/dkzhang/.juliaup/bin:/home/dkzhang/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/dkzhang/.local/bin

@KristofferC
Copy link
Member

#56938 had to be reverted from the backport list to 1.11 and 1.10 because it caused other failures (in the VecElement tests). I will open backports for the new version and include it and the test error can be investigated.

@dzhang314
Copy link
Contributor Author

@KristofferC Got it, thanks! In that case, I'll keep using v1.10 for now and hold off until the next v1.11 release to port MultiFloats.jl and the rest of my code over.

@jakobnissen
Copy link
Member

Possibly related to (duplicate of?) #56265

@KristofferC
Copy link
Member

I actually get

julia> @noinline function bar32414(a)
           v = ntuple(w -> VecElement(Float64(10w)), Val(8))
           return a, (v, (a, (1e6, 1e9)))
       end
bar32414 (generic function with 1 method)

julia> bar32414(-35.0)
Assertion failed: (SL->getElementOffset(idx) == byte_offset), function convert_struct_offset, file cgutils.cpp, line 608.

[29361] signal 6: Abort trap: 6
in expression starting at REPL[2]:1
__pthread_kill at /usr/lib/system/libsystem_kernel.dylib (unknown line)
Allocations: 1932943 (Pool: 1932489; Big: 454); GC: 3
[1]    29361 abort      ./julia

on the 1.11 backport branch with the alignment fix in it. @gbaraldi, any idea?

@dzhang314
Copy link
Contributor Author

Digging into this more, there's a weird plot twist: I previously thought the cause was Julia v1.11 having weaker alignment guarantees than v1.10, but that's not the whole story. It turns out Vector{S} is unaligned on v1.10 as well:

   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.10.9 (2025-03-10)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

julia> struct S; data::NTuple{8,VecElement{Float64}}; end

julia> UInt(pointer(Vector{S}(undef, 5))) % 64
0x0000000000000000

julia> UInt(pointer(Vector{S}(undef, 5))) % 64
0x0000000000000010

julia> UInt(pointer(Vector{S}(undef, 5))) % 64
0x0000000000000020

julia> UInt(pointer(Vector{S}(undef, 5))) % 64
0x0000000000000030

julia> UInt(pointer(Vector{S}(undef, 5))) % 64
0x0000000000000000

But here's the kicker: Julia v1.10 was smart enough to generate an unaligned load!

julia> code_native(getindex, (Vector{S}, Int))
	[ ... snip ...]
	vmovups	zmm0, zmmword ptr [rcx + rax]
	vmovaps	zmmword ptr [rdi], zmm0
	[ ... snip ...]

So, there is indeed a regression in Julia v1.11, but it's not the one I thought it was. For some reason we started erroneously generating an aligned load where we used to correctly generate an unaligned load.

vtjnash added a commit that referenced this issue Mar 11, 2025
The alignment of a nested object (in C layouts) is not affected by the
alignment of its parent container when computing a field offset. This
can be strongly counter-intuitive (as it implies adding padding where it
does not seem to provide value), but is required to match the C standard.
It also permits users to write custom allocators which happen to provide
alignment in excess of that which codegen may assume is guaranteed, and
get the behavioral characteristics they intended to specify (without
resorting to computing explicit padding).

Addresses #57713 (comment)
vtjnash added a commit that referenced this issue Mar 11, 2025
The alignment of a nested object (in C layouts) is not affected by the
alignment of its parent container when computing a field offset. This
can be strongly counter-intuitive (as it implies adding padding where it
does not seem to provide value), but is required to match the C standard.
It also permits users to write custom allocators which happen to provide
alignment in excess of that which codegen may assume is guaranteed, and
get the behavioral characteristics they intended to specify (without
resorting to computing explicit padding).

Addresses #57713 (comment)
KristofferC pushed a commit that referenced this issue Mar 11, 2025
The alignment of a nested object (in C layouts) is not affected by the
alignment of its parent container when computing a field offset (as if
it will be allocated at address 0). This can be strongly
counter-intuitive (as it implies it should add padding where it does not
seem to provide value to the user), but this is required to match the C
standard. It also permits users to write custom allocators which happen
to provide alignment in excess of that which codegen may assume is
guaranteed, and get the behavioral characteristics they intended to
specify (without resorting to computing explicit padding).

Addresses
#57713 (comment)
vtjnash added a commit that referenced this issue Mar 12, 2025
The alignment of a nested object (in C layouts) is not affected by the
alignment of its parent container when computing a field offset (as if
it will be allocated at address 0). This can be strongly
counter-intuitive (as it implies it should add padding where it does not
seem to provide value to the user), but this is required to match the C
standard. It also permits users to write custom allocators which happen
to provide alignment in excess of that which codegen may assume is
guaranteed, and get the behavioral characteristics they intended to
specify (without resorting to computing explicit padding).

Addresses
#57713 (comment)

(Cherry-picked from c9008ff, with
typo fix to typed_loaded memcpy which was already deleted from master)
vtjnash added a commit that referenced this issue Mar 12, 2025
The alignment of a nested object (in C layouts) is not affected by the
alignment of its parent container when computing a field offset (as if
it will be allocated at address 0). This can be strongly
counter-intuitive (as it implies it should add padding where it does not
seem to provide value to the user), but this is required to match the C
standard. It also permits users to write custom allocators which happen
to provide alignment in excess of that which codegen may assume is
guaranteed, and get the behavioral characteristics they intended to
specify (without resorting to computing explicit padding).

Addresses
#57713 (comment)

(Cherry-picked from ec3c02a in
v1.11 backports branch)
@KristofferC
Copy link
Member

@dzhang314, can you try this now on the 1.11 backport branch? #57714

@dzhang314
Copy link
Contributor Author

@KristofferC Sorry about the delay on my end -- it looks good now! The initially problematic code now runs fine, and code_native(getindex, (Vector{S}, Int)) now correctly shows unaligned loads:

               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.11.3 (2025-03-12)
 _/ |\__'_|_|_|\__'_|  |  backports-release-1.11/ec3c02af5c (fork: 603 commits, 394 days)
|__/                   |

julia> struct S; data::NTuple{8,VecElement{Float64}}; end

julia> for _ = 1:10; v = Vector{S}(undef, 1); println(v); end
S[S((VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]
S[S((VecElement{Float64}(6.4062368986437e-310), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]
S[S((VecElement{Float64}(2.5e-323), VecElement{Float64}(4.4e-323), VecElement{Float64}(1.6e-322), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]
S[S((VecElement{Float64}(5.0e-324), VecElement{Float64}(6.0e-323), VecElement{Float64}(6.4e-323), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]
S[S((VecElement{Float64}(2.5e-323), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]
S[S((VecElement{Float64}(6.40624530162366e-310), VecElement{Float64}(6.4062453021549e-310), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]
S[S((VecElement{Float64}(9.4e-323), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]
S[S((VecElement{Float64}(1.63e-322), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]
S[S((VecElement{Float64}(2.96e-322), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]
S[S((VecElement{Float64}(1.83e-322), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0), VecElement{Float64}(0.0)))]

julia> code_native(getindex, (Vector{S}, Int))
	
.text
	.file	"getindex"
	.globl	julia_getindex_578              # -- Begin function julia_getindex_578
	.p2align	4, 0x90
	.type	julia_getindex_578,@function
julia_getindex_578:                     # @julia_getindex_578
; Function Signature: getindex(Array{Main.S, 1}, Int64)
; ┌ @ essentials.jl:914 within `getindex`
# %bb.0:                                # %top
; │ @ essentials.jl within `getindex`
	#DEBUG_VALUE: getindex:A <- [DW_OP_deref] $rsi
	#DEBUG_VALUE: getindex:i <- $rdx
	#DEBUG_VALUE: getindex:A <- [DW_OP_deref] 0
	push	rbp
	mov	rbp, rsp
	sub	rsp, 16
; │ @ essentials.jl:916 within `getindex`
	lea	rax, [rdx - 1]
	cmp	rax, qword ptr [rsi + 16]
	jae	.LBB0_2
# %bb.1:                                # %L15
; │ @ essentials.jl:917 within `getindex`
	mov	rcx, qword ptr [rsi]
	shl	rax, 6
	vmovups	zmm0, zmmword ptr [rcx + rax]
	vmovups	zmmword ptr [rdi], zmm0
	mov	rax, rdi
	add	rsp, 16
	pop	rbp
	vzeroupper
	ret
.LBB0_2:                                # %L12
; │ @ essentials.jl:916 within `getindex`
	mov	qword ptr [rbp - 8], rdx
	movabs	rcx, offset j_throw_boundserror_590
	lea	rax, [rbp - 8]
	mov	rdi, rsi
	mov	rsi, rax
	call	rcx
.Lfunc_end0:
	.size	julia_getindex_578, .Lfunc_end0-julia_getindex_578
; └
                                        # -- End function
	.type	".L+Main.S#580",@object         # @"+Main.S#580"
	.section	.rodata,"a",@progbits
	.p2align	3, 0x0
".L+Main.S#580":
	.quad	".L+Main.S#580.jit"
	.size	".L+Main.S#580", 8

.set ".L+Main.S#580.jit", 129663832398608
	.size	".L+Main.S#580.jit", 8
	.section	".note.GNU-stack","",@progbits

I suppose this means I can expect a fix in v1.11.5. Should I go ahead and close this issue now, or wait until v1.11.5 is released?

KristofferC pushed a commit that referenced this issue Mar 20, 2025
The alignment of a nested object (in C layouts) is not affected by the
alignment of its parent container when computing a field offset (as if
it will be allocated at address 0). This can be strongly
counter-intuitive (as it implies it should add padding where it does not
seem to provide value to the user), but this is required to match the C
standard. It also permits users to write custom allocators which happen
to provide alignment in excess of that which codegen may assume is
guaranteed, and get the behavioral characteristics they intended to
specify (without resorting to computing explicit padding).

Addresses
#57713 (comment)

(cherry picked from commit c9008ff)
@vtjnash vtjnash closed this as completed Mar 21, 2025
KristofferC pushed a commit that referenced this issue Mar 25, 2025
The alignment of a nested object (in C layouts) is not affected by the
alignment of its parent container when computing a field offset (as if
it will be allocated at address 0). This can be strongly
counter-intuitive (as it implies it should add padding where it does not
seem to provide value to the user), but this is required to match the C
standard. It also permits users to write custom allocators which happen
to provide alignment in excess of that which codegen may assume is
guaranteed, and get the behavioral characteristics they intended to
specify (without resorting to computing explicit padding).

Addresses
#57713 (comment)

(Cherry-picked from c9008ff, with
typo fix to typed_loaded memcpy which was already deleted from master)
KristofferC pushed a commit that referenced this issue Mar 31, 2025
The alignment of a nested object (in C layouts) is not affected by the
alignment of its parent container when computing a field offset (as if
it will be allocated at address 0). This can be strongly
counter-intuitive (as it implies it should add padding where it does not
seem to provide value to the user), but this is required to match the C
standard. It also permits users to write custom allocators which happen
to provide alignment in excess of that which codegen may assume is
guaranteed, and get the behavioral characteristics they intended to
specify (without resorting to computing explicit padding).

Addresses
#57713 (comment)

(Cherry-picked from ec3c02a in
v1.11 backports branch)
KristofferC pushed a commit that referenced this issue Mar 31, 2025
The alignment of a nested object (in C layouts) is not affected by the
alignment of its parent container when computing a field offset (as if
it will be allocated at address 0). This can be strongly
counter-intuitive (as it implies it should add padding where it does not
seem to provide value to the user), but this is required to match the C
standard. It also permits users to write custom allocators which happen
to provide alignment in excess of that which codegen may assume is
guaranteed, and get the behavioral characteristics they intended to
specify (without resorting to computing explicit padding).

Addresses
#57713 (comment)

(Cherry-picked from ec3c02a in
v1.11 backports branch)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants