odin-lang / Odin

Odin Programming Language
https://odin-lang.org
BSD 3-Clause "New" or "Revised" License
6.1k stars 549 forks source link

arm32 homogeneous aggregates ABI issues #3626

Open IllusionMan1212 opened 1 month ago

IllusionMan1212 commented 1 month ago

Context

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:

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

gingerBill commented 1 month ago

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