julia> @noinline function foo(a)
v = ntuple(Val(8)) do w Core.VecElement(Float64(10w)) end
a, (v, (a,(1e6,1e9)))
end
foo (generic function with 1 method)
julia> foo(-35.0)
(10.0, ((VecElement{Float64}(30.0), VecElement{Float64}(40.0), VecElement{Float64}(50.0), VecElement{Float64}(60.0), VecElement{Float64}(70.0), VecElement{Float64}(80.0), VecElement{Float64}(-35.0), VecElement{Float64}(1.0e6)), (1.0e9, (1.2353556789225747e-307, 6.9513596813479e-310))))
julia> versioninfo()
Julia Version 1.3.0-DEV.428
Commit 863e03fbb1* (2019-06-19 15:07 UTC)
Platform Info:
OS: Linux (x86_64-generic-linux)
CPU: Intel(R) Core(TM) i9-7900X CPU @ 3.30GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-8.0.0 (ORCJIT, skylake)
The answer should be:
(-35.0, ((VecElement{Float64}(10.0), VecElement{Float64}(20.0), VecElement{Float64}(30.0), VecElement{Float64}(40.0), VecElement{Float64}(50.0), VecElement{Float64}(60.0), VecElement{Float64}(70.0), VecElement{Float64}(80.0), VecElement{Float64}(-35.0), VecElement{Float64}(1.0e6)), (-35.0, (1.0e6, 1.0e9))))
That is, the incorrect answer we received is shifted to the left by 16 bytes, and there appears to be an 8-byte gap between the first float and the following vector.
If we replace the tuple of VecElement
s with a standard tuple, that is what we get:
julia> @noinline function bar(a)
v = ntuple(Val(8)) do w Float64(10w) end
a, (v, (a,(1e6,1e9)))
end
bar (generic function with 1 method)
julia> bar(-35.0)
(-35.0, ((10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0), (-35.0, (1.0e6, 1.0e9))))
This silently corrupts data -- not the easiest problem to track down!
I referenced the issue here, where vtjnash said:
Seems like a new issue: it looks like we're specifying the wrong sort of vector to LLVM for our desired alignment for Julia.
Is this more than just an alignment problem? On the latest master:
julia> @noinline function foo(a)
v = ntuple(Val(8)) do w Core.VecElement(Float64(10w)) end
(v, (a,(1e6,1e9))), a
end
foo (generic function with 1 method)
julia> foo(-35.0)
(((VecElement{Float64}(-35.0), VecElement{Float64}(20.0), VecElement{Float64}(30.0), VecElement{Float64}(40.0), VecElement{Float64}(50.0), VecElement{Float64}(60.0), VecElement{Float64}(70.0), VecElement{Float64}(80.0)), (-35.0, (1.0e6, 1.0e9))), 4.65313481751076e-310)
Somehow a
replaced the first element of v
while the a
at the end was replaced with uninitialized data. Turning the a
into a tuple eats further into the vector:
julia> @noinline function foo(a)
v = ntuple(Val(8)) do w Core.VecElement(Float64(10w)) end
(v, (a,(1e6,1e9))), (a,a)
end
foo (generic function with 1 method)
julia> foo(-35.0)
(((VecElement{Float64}(-35.0), VecElement{Float64}(-35.0), VecElement{Float64}(30.0), VecElement{Float64}(40.0), VecElement{Float64}(50.0), VecElement{Float64}(60.0), VecElement{Float64}(70.0), VecElement{Float64}(80.0)), (-35.0, (1.0e6, 1.0e9))), (4.65313472422326e-310, 6.939755661418e-310))
Or maybe this is the exact sample problem, and the compiler changed the order of the returned values?
(I'm the same person as above, just on a different account.)
julia> @noinline function foo(a)
v = ntuple(Val(4)) do w Core.VecElement(Float64(10w)) end
a, (v, (a,(1e6,1e9)))
end
foo (generic function with 1 method)
julia> foo(-35.)
(10.0, ((VecElement{Float64}(30.0), VecElement{Float64}(40.0), VecElement{Float64}(-35.0), VecElement{Float64}(1.0e6)), (1.0e9, (0.0, 0.0))))
julia> versioninfo()
Julia Version 1.1.0
Commit 80516ca202 (2019-01-21 21:24 UTC)
Platform Info:
OS: Linux (x86_64-pc-linux-gnu)
CPU: Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-6.0.1 (ORCJIT, haswell)
I did confirm that this bug affects avx2 machines as well.
If no one else has the time to look at it, is there any chance the problem code is accessible enough for someone unfamiliar with Julia's internals (ie, me) to fix? If so, can someone point me to where relevant values are defined?
https://github.com/JuliaLang/julia/blob/b451d5567b33c52580619a662166f9c0a600db41/src/datatype.c#L184-L224 might be useful to look at.
Is it possible for me to pay someone to fix this?
If so, how, and how much is it likely to cost?
This issue is a constant aggravation, but I think solving it is beyond me.
a
magically jumps to the front, replacing the first element of the vector:
julia> @noinline function foo(a)
v = ntuple(Val(8)) do w Core.VecElement(Float64(10w)) end
(v, (a,(1e6,1e9))), a
end
foo (generic function with 1 method)
julia> foo(23.4)
(((VecElement{Float64}(23.4), VecElement{Float64}(20.0), VecElement{Float64}(30.0), VecElement{Float64}(40.0), VecElement{Float64}(50.0), VecElement{Float64}(60.0), VecElement{Float64}(70.0), VecElement{Float64}(80.0)), (23.4, (1.0e6, 1.0e9))), 4.67707705511027e-310)
To me this looks like an LLVM bug before optimization:
julia> @code_llvm optimize=false foo(23.4)
; @ REPL[1]:2 within `foo'
; Function Attrs: noinline
define void @julia_foo_15969({ { <8 x double>, { double, [2 x double] } }, double }* noalias nocapture sret, double) #0 {
top:
%2 = alloca { double, [2 x double] }
%3 = alloca { <8 x double>, { double, [2 x double] } }
%4 = alloca { { <8 x double>, { double, [2 x double] } }, double }
%5 = call %jl_value_t*** @julia.ptls_states()
%6 = bitcast %jl_value_t*** %5 to %jl_value_t addrspace(10)**
%7 = getelementptr inbounds %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)** %6, i64 4
%8 = bitcast %jl_value_t addrspace(10)** %7 to i64**
%9 = load i64*, i64** %8
; @ REPL[1]:3 within `foo'
%10 = getelementptr inbounds { double, [2 x double] }, { double, [2 x double] }* %2, i32 0, i32 0
store double %1, double* %10
%11 = getelementptr inbounds { double, [2 x double] }, { double, [2 x double] }* %2, i32 0, i32 1
store [2 x double] [double 1.000000e+06, double 1.000000e+09], [2 x double]* %11
%12 = getelementptr inbounds { <8 x double>, { double, [2 x double] } }, { <8 x double>, { double, [2 x double] } }* %3, i32 0, i32 0
store <8 x double> <double 1.000000e+01, double 2.000000e+01, double 3.000000e+01, double 4.000000e+01, double 5.000000e+01, double 6.000000e+01, double 7.000000e+01, double 8.000000e+01>, <8 x double>* %12
%13 = getelementptr inbounds { <8 x double>, { double, [2 x double] } }, { <8 x double>, { double, [2 x double] } }* %3, i32 0, i32 1
%14 = bitcast { double, [2 x double] }* %13 to i8*
%15 = bitcast { double, [2 x double] }* %2 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %14, i8* %15, i64 24, i32 8, i1 false)
%16 = getelementptr inbounds { { <8 x double>, { double, [2 x double] } }, double }, { { <8 x double>, { double, [2 x double] } }, double }* %4, i32 0, i32 0
%17 = bitcast { <8 x double>, { double, [2 x double] } }* %16 to i8*
%18 = bitcast { <8 x double>, { double, [2 x double] } }* %3 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %17, i8* %18, i64 96, i32 16, i1 false)
%19 = getelementptr inbounds { { <8 x double>, { double, [2 x double] } }, double }, { { <8 x double>, { double, [2 x double] } }, double }* %4, i32 0, i32 0
%20 = bitcast { <8 x double>, { double, [2 x double] } }* %19 to double*
store double %1, double* %20
%21 = bitcast { { <8 x double>, { double, [2 x double] } }, double }* %0 to i8*
%22 = bitcast { { <8 x double>, { double, [2 x double] } }, double }* %4 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %21, i8* %22, i64 112, i32 16, i1 false)
ret void
}
after optimization:
julia> @code_llvm optimize=true foo(23.4)
; @ REPL[1]:2 within `foo'
; Function Attrs: noinline
define void @julia_foo_15969({ { <8 x double>, { double, [2 x double] } }, double }* noalias nocapture sret, double) #0 {
top:
; @ REPL[1]:3 within `foo'
%.sroa.0.0..sroa_idx = getelementptr inbounds { { <8 x double>, { double, [2 x double] } }, double }, { { <8 x double>, { double, [2 x double] } }, double }* %0, i64 0, i32 0, i32 0, i64 0
store double %1, double* %.sroa.0.0..sroa_idx, align 64
%.sroa.3.sroa.0.0..sroa.3.0..sroa_cast.sroa_idx = getelementptr inbounds { { <8 x double>, { double, [2 x double] } }, double }, { { <8 x double>, { double, [2 x double] } }, double }* %0, i64 0, i32 0, i32 0, i64 1
%.sroa.3.sroa.0.0..sroa.3.0..sroa_cast.sroa_cast = bitcast double* %.sroa.3.sroa.0.0..sroa.3.0..sroa_cast.sroa_idx to <7 x double>*
store <7 x double> <double 2.000000e+01, double 3.000000e+01, double 4.000000e+01, double 5.000000e+01, double 6.000000e+01, double 7.000000e+01, double 8.000000e+01>, <7 x double>* %.sroa.3.sroa.0.0..sroa.3.0..sroa_cast.sroa_cast, align 8
%.sroa.3.sroa.2.sroa.0.0..sroa.3.sroa.2.0..sroa.3.0..sroa_cast.sroa_cast.sroa_idx = getelementptr inbounds { { <8 x double>, { double, [2 x double] } }, double }, { { <8 x double>, { double, [2 x double] } }, double }* %0, i64 0, i32 0, i32 1, i32 0
store double %1, double* %.sroa.3.sroa.2.sroa.0.0..sroa.3.sroa.2.0..sroa.3.0..sroa_cast.sroa_cast.sroa_idx, align 64
%.sroa.3.sroa.2.sroa.2.0..sroa.3.sroa.2.0..sroa.3.0..sroa_cast.sroa_cast.sroa_idx11 = getelementptr inbounds { { <8 x double>, { double, [2 x double] } }, double }, { { <8 x double>, { double, [2 x double] } }, double }* %0, i64 0, i32 0, i32 1, i32 1, i64 0
%2 = bitcast double* %.sroa.3.sroa.2.sroa.2.0..sroa.3.sroa.2.0..sroa.3.0..sroa_cast.sroa_cast.sroa_idx11 to <2 x double>*
store <2 x double> <double 1.000000e+06, double 1.000000e+09>, <2 x double>* %2, align 8
ret void
}
To break this up a bit:
%0
is the return value; %retvalue
%1
is the argument; %arg
%retvalue[0][0][0][0] = %arg
%retvalue[0][0][0][1:7] = <20, 30, 40, 50, 60, 70, 80>
%retvalue[0][0][1][0] = %arg
%retvalue[0][0][1][1][0:1] = (1e6,1e9)
Which matches what we get when we run the code, even down to the fact that the store of arg
to the last element of the tuple is missing and we are getting random memory.
I should mention that the generated IR looks fine to me and that LLVM trunk still has this issue https://godbolt.org/z/x79YbH
Oh no, I misread something:
%19 = getelementptr inbounds { { <8 x double>, { double, [2 x double] } }, double }, { { <8 x double>, { double, [2 x double] } }, double }* %4, i32 0, i32 0
%20 = bitcast { <8 x double>, { double, [2 x double] } }* %19 to double*
store double %1, double* %20
This is probably meant to be be the store (x, a)
, but instead it is overwriting the first element of the vector. So it is a codegen issue.
It boils down to an alignment issue in https://github.com/JuliaLang/julia/blob/0aa59a0ccdcd47626c2c1be43ebc00f0b36740cb/src/cgutils.cpp#L2528-L2529
Julia believes that the second field is at offset 96 and that the struct has alignment 16, whereas LLVM believes that the offset of the field is 128 and that the struct alignment is 64.
FWIW, I think the relevant assert was disabled in https://github.com/JuliaLang/julia/pull/21980, while this issue should be addressed by https://github.com/JuliaLang/julia/pull/21959 — they, coincidentally, being by the same author
Is it possible for me to pay someone to fix this?
If so, how, and how much is it likely to cost?
Not as nice as paying vchuravy or any developers directly, but I did just donate some money to Julia through NumFocus.
I'm guessing that this is because of enabling the assertion that Julia and LLVM use the same alignment https://github.com/JuliaLang/julia/pull/33597#issuecomment-543323575
julia> @noinline function foo(a)
v = ntuple(Val(8)) do w Core.VecElement(Float64(10w)) end
a, (v, (a,(1e6,1e9)))
end
foo (generic function with 1 method)
julia> foo(-4.0)
julia: /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:514: unsigned int convert_struct_offset(llvm::Type*, unsigned int): Assertion `SL->getElementOffset(idx) == byte_offset' failed.
signal (6): Aborted
in expression starting at REPL[2]:1
gsignal at /usr/lib64/haswell/libc.so.6 (unknown line)
abort at /usr/lib64/haswell/libc.so.6 (unknown line)
unknown function (ip: 0x7f165d8bd776)
__assert_fail at /usr/lib64/haswell/libc.so.6 (unknown line)
convert_struct_offset at /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:514 [inlined]
convert_struct_offset at /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:509
convert_struct_offset at /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:520 [inlined]
emit_new_struct at /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:2619
emit_builtin_call at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:2594
emit_call at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3329
emit_expr at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:4101
emit_ssaval_assign at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3805
emit_stmtpos at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3998 [inlined]
emit_function at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:6544
jl_compile_linfo at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:1230
jl_compile_method_internal at /home/chriselrod/Documents/languages/julia/src/gf.c:1879
_jl_invoke at /home/chriselrod/Documents/languages/julia/src/gf.c:2135 [inlined]
jl_apply_generic at /home/chriselrod/Documents/languages/julia/src/gf.c:2300
jl_apply at /home/chriselrod/Documents/languages/julia/src/julia.h:1647 [inlined]
do_call at /home/chriselrod/Documents/languages/julia/src/interpreter.c:328
eval_value at /home/chriselrod/Documents/languages/julia/src/interpreter.c:417
eval_stmt_value at /home/chriselrod/Documents/languages/julia/src/interpreter.c:368 [inlined]
eval_body at /home/chriselrod/Documents/languages/julia/src/interpreter.c:760
jl_interpret_toplevel_thunk_callback at /home/chriselrod/Documents/languages/julia/src/interpreter.c:888
Lenter_interpreter_frame_start_val at /home/chriselrod/Documents/languages/julia/usr/bin/../lib/libjulia.so.1 (unknown line)
jl_interpret_toplevel_thunk at /home/chriselrod/Documents/languages/julia/src/interpreter.c:897
jl_toplevel_eval_flex at /home/chriselrod/Documents/languages/julia/src/toplevel.c:814
jl_toplevel_eval_flex at /home/chriselrod/Documents/languages/julia/src/toplevel.c:764
jl_toplevel_eval at /home/chriselrod/Documents/languages/julia/src/toplevel.c:823 [inlined]
jl_toplevel_eval_in at /home/chriselrod/Documents/languages/julia/src/toplevel.c:843
eval at ./boot.jl:331
eval_user_input at /home/chriselrod/Documents/languages/julia/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
run_backend at (null):0
#80 at (null):0
jl_apply at /home/chriselrod/Documents/languages/julia/src/julia.h:1647 [inlined]
start_task at /home/chriselrod/Documents/languages/julia/src/task.c:687
unknown function (ip: (nil))
Allocations: 9469636 (Pool: 9467308; Big: 2328); GC: 11
fish: “/home/chriselrod/Documents/lang…” terminated by signal SIGABRT (Abort)
Definitely an improvement over silently corrupting answers.
I also get an assertion about incorrect alignment in the simpler case of just returning a NTuple{N,Core.VecElement{T}}
:
julia> foo() = ntuple(i -> Core.VecElement{Float64}(i), Val(8))
foo (generic function with 1 method)
julia> foo()
(VecElement{Float64}(1.0), VecElement{Float64}(2.0), VecElement{Float64}(3.0), VecElement{Float64}(4.0), VecElement{Float64}(5.0), VecElement{Float64}(6.0), VecElement{Float64}(7.0), VecElement{Float64}(8.0))
julia> using BenchmarkTools
julia> @benchmark foo()
julia: /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:514: unsigned int convert_struct_offset(llvm::Type*, unsigned int): Assertion `SL->getElementOffset(idx) == byte_offset' failed.
signal (6): Aborted
in expression starting at REPL[4]:1
gsignal at /usr/lib64/haswell/libc.so.6 (unknown line)
abort at /usr/lib64/haswell/libc.so.6 (unknown line)
unknown function (ip: 0x7f8d89cba776)
__assert_fail at /usr/lib64/haswell/libc.so.6 (unknown line)
convert_struct_offset at /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:514 [inlined]
convert_struct_offset at /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:509
convert_struct_offset at /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:520 [inlined]
emit_new_struct at /home/chriselrod/Documents/languages/julia/src/cgutils.cpp:2619
emit_builtin_call at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:2594
emit_call at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3329
emit_expr at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:4101
emit_ssaval_assign at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3805
emit_stmtpos at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3998 [inlined]
emit_function at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:6544
jl_compile_linfo at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:1230
emit_invoke at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3275
emit_expr at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:4092
emit_ssaval_assign at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3805
emit_stmtpos at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3998 [inlined]
emit_function at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:6544
jl_compile_linfo at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:1230
emit_invoke at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3275
emit_expr at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:4092
emit_ssaval_assign at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3805
emit_stmtpos at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:3998 [inlined]
emit_function at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:6544
jl_compile_linfo at /home/chriselrod/Documents/languages/julia/src/codegen.cpp:1230
jl_compile_method_internal at /home/chriselrod/Documents/languages/julia/src/gf.c:1879
_jl_invoke at /home/chriselrod/Documents/languages/julia/src/gf.c:2135 [inlined]
jl_apply_generic at /home/chriselrod/Documents/languages/julia/src/gf.c:2300
jl_apply at /home/chriselrod/Documents/languages/julia/src/julia.h:1647 [inlined]
do_apply at /home/chriselrod/Documents/languages/julia/src/builtins.c:630
jl_f__apply at /home/chriselrod/Documents/languages/julia/src/builtins.c:642 [inlined]
jl_f__apply_latest at /home/chriselrod/Documents/languages/julia/src/builtins.c:678
#invokelatest#1 at (null):0 [inlined]
invokelatest##kw at ./essentials.jl:710 [inlined]
#run_result#37 at /home/chriselrod/.julia/packages/BenchmarkTools/7aqwe/src/execution.jl:32 [inlined]
run_result##kw at /home/chriselrod/.julia/packages/BenchmarkTools/7aqwe/src/execution.jl:32 [inlined]
#run#39 at /home/chriselrod/.julia/packages/BenchmarkTools/7aqwe/src/execution.jl:46
run##kw at (null):0 [inlined]
run##kw at /home/chriselrod/.julia/packages/BenchmarkTools/7aqwe/src/execution.jl:46 [inlined]
#warmup#42 at /home/chriselrod/.julia/packages/BenchmarkTools/7aqwe/src/execution.jl:79 [inlined]
warmup at /home/chriselrod/.julia/packages/BenchmarkTools/7aqwe/src/execution.jl:79
jl_apply at /home/chriselrod/Documents/languages/julia/src/julia.h:1647 [inlined]
do_call at /home/chriselrod/Documents/languages/julia/src/interpreter.c:328
eval_value at /home/chriselrod/Documents/languages/julia/src/interpreter.c:417
eval_stmt_value at /home/chriselrod/Documents/languages/julia/src/interpreter.c:368 [inlined]
eval_body at /home/chriselrod/Documents/languages/julia/src/interpreter.c:760
jl_interpret_toplevel_thunk_callback at /home/chriselrod/Documents/languages/julia/src/interpreter.c:888
Lenter_interpreter_frame_start_val at /home/chriselrod/Documents/languages/julia/usr/bin/../lib/libjulia.so.1 (unknown line)
jl_interpret_toplevel_thunk at /home/chriselrod/Documents/languages/julia/src/interpreter.c:897
jl_toplevel_eval_flex at /home/chriselrod/Documents/languages/julia/src/toplevel.c:814
jl_toplevel_eval_flex at /home/chriselrod/Documents/languages/julia/src/toplevel.c:764
jl_toplevel_eval at /home/chriselrod/Documents/languages/julia/src/toplevel.c:823 [inlined]
jl_toplevel_eval_in at /home/chriselrod/Documents/languages/julia/src/toplevel.c:843
eval at ./boot.jl:331
eval_user_input at /home/chriselrod/Documents/languages/julia/usr/share/julia/stdlib/v1.4/REPL/src/REPL.jl:86
run_backend at (null):0
#80 at (null):0
jl_apply at /home/chriselrod/Documents/languages/julia/src/julia.h:1647 [inlined]
start_task at /home/chriselrod/Documents/languages/julia/src/task.c:687
unknown function (ip: (nil))
Allocations: 12974179 (Pool: 12971223; Big: 2956); GC: 15
fish: “/home/chriselrod/Documents/lang…” terminated by signal SIGABRT (Abort)
Most helpful comment
It boils down to an alignment issue in https://github.com/JuliaLang/julia/blob/0aa59a0ccdcd47626c2c1be43ebc00f0b36740cb/src/cgutils.cpp#L2528-L2529
Julia believes that the second field is at offset 96 and that the struct has alignment 16, whereas LLVM believes that the offset of the field is 128 and that the struct alignment is 64.