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

SIGBUS on AArch64 when Proc returns large extern struct by value #14322

Closed
HertzDevil opened this issue Feb 25, 2024 · 0 comments · Fixed by #14323
Closed

SIGBUS on AArch64 when Proc returns large extern struct by value #14322

HertzDevil opened this issue Feb 25, 2024 · 0 comments · Fixed by #14323

Comments

@HertzDevil
Copy link
Contributor

This example from spec/compiler/codegen/extern_spec.cr, which currently runs only on x86-64 targets, crashes on AArch64 (both macOS and Alpine):

lib LibMylib
  struct Struct
    x : Int32
    y : Int32
    z : Int32
    w : Int32
    a : Int32
  end
end

f = ->{
  s = LibMylib::Struct.new
  s.x = 1
  s.y = 2
  s.z = 3
  s.w = 4
  s.a = 5
  s
}

s = f.call
s.x &+ s.y &+ s.z &+ s.w &+ s.a

LibMylib::Struct must be passed indirectly according to the AArch64 ABI. The generated LLVM IR shows how this is done:

define i32 @__crystal_main(i32 %argc, ptr %argv) {
  ; ...
  %6 = load %"->", ptr %f, align 8
  %7 = extractvalue %"->" %6, 0
  %8 = extractvalue %"->" %6, 1
  %9 = icmp eq ptr %8, null
  br i1 %9, label %ctx_is_null, label %ctx_is_not_null

ctx_is_null:                                      ; preds = %entry
  call void %7(ptr %1)
  br label %exit

ctx_is_not_null:                                  ; preds = %entry
  %10 = call %"struct.LibMylib::Struct" %7(ptr %8)
  store %"struct.LibMylib::Struct" %10, ptr %2, align 4
  br label %exit

exit:                                             ; preds = %ctx_is_not_null, %ctx_is_null
  %11 = phi ptr [ %1, %ctx_is_null ], [ %2, %ctx_is_not_null ]
  %12 = load %"struct.LibMylib::Struct", ptr %11, align 4
  store %"struct.LibMylib::Struct" %12, ptr %s, align 4
  ; ...
}

define void @"~procProc(LibMylib::Struct)@test.cr:11"(ptr sret(%"struct.LibMylib::Struct") %0) #0 {
alloca:
  %s = alloca %"struct.LibMylib::Struct", align 8
  %1 = alloca %"struct.LibMylib::Struct", align 8
  br label %entry

entry:                                            ; preds = %alloca
  %2 = call %"struct.LibMylib::Struct" @"*struct.LibMylib::Struct::new:struct.LibMylib::Struct"()
  store %"struct.LibMylib::Struct" %2, ptr %1, align 4
  %3 = load %"struct.LibMylib::Struct", ptr %1, align 4
  store %"struct.LibMylib::Struct" %3, ptr %s, align 4
  %4 = getelementptr inbounds %"struct.LibMylib::Struct", ptr %s, i32 0, i32 0
  store i32 1, ptr %4, align 4
  %5 = getelementptr inbounds %"struct.LibMylib::Struct", ptr %s, i32 0, i32 1
  store i32 2, ptr %5, align 4
  %6 = getelementptr inbounds %"struct.LibMylib::Struct", ptr %s, i32 0, i32 2
  store i32 3, ptr %6, align 4
  %7 = getelementptr inbounds %"struct.LibMylib::Struct", ptr %s, i32 0, i32 3
  store i32 4, ptr %7, align 4
  %8 = getelementptr inbounds %"struct.LibMylib::Struct", ptr %s, i32 0, i32 4
  store i32 5, ptr %8, align 4
  %9 = load %"struct.LibMylib::Struct", ptr %s, align 4
  store %"struct.LibMylib::Struct" %9, ptr %0, align 4
  ret void
}

Apparently, the store %"struct.LibMylib::Struct" %9, ptr %0 instruction is reading %0 from the wrong register, but everything works if the call is adjusted like this:

ctx_is_null:                                      ; preds = %entry
  call void %7(ptr sret(%"struct.LibMylib::Struct") %1)
  br label %exit

That is, the sret at the call argument should match the sret at the function parameter.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant