Odin
Odin copied to clipboard
arm32 homogeneous aggregates ABI issues
Context
- Operating System & Odin Version:
Odin: dev-2024-05:e1c4b9b06
OS: Debian GNU/Linux 12 (bookworm), Linux 6.1.0-21-armmp-lpae
CPU: ARM
RAM: 992 MiB
Backend: LLVM 14.0.6
Expected Behavior
Correct values are passed to, and returned from, foreign functions.
Current Behavior
Argument values are incorrect when printed in foreign functions and return values are incorrect when printed on the odin-side.
Failure Information (for bugs)
The Odin compiler is currently not conforming to the arm32 ABI homogeneous aggregates section.
There are 3 issues that need fixing:
- Homogeneous aggregates passed as an argument where the aggregate doesn't exceed 4 elements.
- Homogeneous aggregates returned from a procedure where the aggregate doesn't exceed 4 elements.
- Homogeneous aggregates passed as an argument where the aggregate EXCEEDS 4 elements.
For the first and the second points, the same issue was happening on arm64 and was reported in #2561 and fixed by https://github.com/odin-lang/odin/commit/f3a463000d4d777cf255bfebda3f51fba4ce8fcc . I've confirmed that same code fixes 1 and 2 but 3 still happens, I haven't been able to test to see if it's an issue on arm64 too but I suspect it is.
Steps to Reproduce
Simple C code compiled with gcc -c -o main.o main.c and made into a static lib with ar rcs libodintest.a main.o
#include <stdio.h>
typedef struct Vector2 {
float x;
float y;
} Vector2;
typedef struct Camera2D {
Vector2 offset;
Vector2 target;
float rotation;
float zoom;
} Camera2D;
void homogenous_arg_test(Vector2 pos) {
printf("homogenous_arg_test result: ");
printf("Pos: {%f, %f}\n", pos.x, pos.y);
}
Vector2 homogenous_return_test() {
printf("homogenous_return_test result: ");
Vector2 pos = (Vector2){45, 90};
printf("Returning {%f, %f}\n", pos.x, pos.y);
return pos;
}
void homogenous_arg_more_than_4_members(Vector2 pos, Camera2D cam) {
printf("homogenous_arg_more_than_4_members result: ");
printf("Pos: {%f, %f}\n", pos.x, pos.y);
printf("Camera Offset: {%f, %f}\n", cam.offset.x, cam.offset.y);
printf("Camera Target: {%f, %f}\n", cam.target.x, cam.target.y);
printf("Camera Rotation: %f\n", cam.rotation);
printf("Camera Zoom: %f\n", cam.zoom);
}
And the odin code that calls the C library
package main
import "core:fmt"
foreign import ext "libodintest.a"
Vector2 :: [2]f32
Camera2D :: struct {
offset: Vector2,
target: Vector2,
rotation: f32,
zoom: f32,
}
foreign ext {
homogenous_arg_test :: proc(pos: Vector2) ---
homogenous_return_test :: proc() -> Vector2 ---
homogenous_arg_more_than_4_members :: proc(pos: Vector2, cam: Camera2D) ---
}
main :: proc() {
homogenous_arg_test({15, 30})
ret := homogenous_return_test()
fmt.println("Return on odin side is:", ret)
cam := Camera2D{
offset = {99, 100},
target = {74, 43},
rotation = 5.0,
zoom = 3.0,
}
homogenous_arg_more_than_4_members({33, 66}, cam)
}
And this is what gets printed to the terminal
homogenous_arg_test result: Pos: {0.000000, 0.000000}
homogenous_return_test result: Returning {45.000000, 90.000000}
Return on odin side is: [0, 0]
homogenous_arg_more_than_4_members result: Pos: {17644434555492938809344.000000, 12710676290573701508590102118400.000000}
Camera Offset: {33.000000, 66.000000}
Camera Target: {99.000000, 100.000000}
Camera Rotation: 74.000000
Camera Zoom: 43.000000
Failure Logs
N/A
The current ABI for arm32 is mostly a placeholder, and will need correcting for aggregates. The problem is reverse engineering how LLVM does things more than anything.
https://github.com/odin-lang/Odin/blob/master/src/llvm_abi.cpp#L1392-L1472