janet-lang / janet

A dynamic language and bytecode vm
https://janet-lang.org
MIT License
3.38k stars 217 forks source link

Add support for the AAPCS64 calling convention #1468

Closed SyrupThinker closed 1 week ago

SyrupThinker commented 2 weeks ago

This change adds support for the AAPCS64 calling convention, targeting Linux and macOS AARCH64 systems.

This was developed on a macOS target, which has "looser", Apple specific, alignment requirements than the base standard used by Linux.

Support for the latter has only been tested against a QEMU VM running Arch Linux ARM, so tests against actual hardware would be appreciated.

In general my use-case is pretty simple, so there might be bugs with more complex function signatures. I've added two tests for stack spilling and alignment differences to at least catch rudimentary errors here, but again, tests against more complex libraries would be appreciated.

Reading over the Windows Overview of ARM64 ABI conventions suggests, that there are no relevant modifications to the base convention as with Linux. Thus this change might be compatible with Windows as well. I did not enable it for Windows as I could not test this.

Small example of a Raylib binding running on macOS (M1) ![raylib](https://github.com/janet-lang/janet/assets/7753242/b89dc09d-5c4d-44e3-8883-a82609ad3cdf)
main.janet ```janet (import /stav/ffi/raylib :as rl) (defmacro with-drawing [& body] ~(do (,rl/BeginDrawing) ,;body (,rl/EndDrawing))) (defmacro with-mode-2d [cam & body] ~(do (,rl/BeginMode2D ,cam) ,;body (,rl/EndMode2D))) (rl/InitWindow 800 600 "Raylib Janet Demo") (rl/SetTargetFPS 30) (var t 0) (var cam @[[400 300] [0 0] 0 1]) (while (not (rl/WindowShouldClose)) (with-drawing (rl/ClearBackground [0 0 0 0]) (rl/DrawText (string/format "%i" t) 10 10 10 [0 255 0 255]) (with-mode-2d cam (rl/DrawCircle (math/trunc (* (math/cos t) 100)) (math/trunc (* (math/sin t) 50)) 10 [(% t 256) 0 255 255]))) (++ t) (++ (cam 2))) (rl/CloseWindow) ```
stav/ffi/raylib.janet ```janet (ffi/context "/opt/homebrew/lib/libraylib.dylib") # Structs (def Color (ffi/struct :u8 :u8 :u8 :u8)) (def Vector2 (ffi/struct :float :float)) (def Vector3 (ffi/struct :float :float :float)) (def Vector4 (ffi/struct :float :float :float :float)) (def Rectangle (ffi/struct :float :float :float :float)) (def Camera2D (ffi/struct Vector2 Vector2 :float :float)) (def Camera3D (ffi/struct Vector3 Vector3 Vector3 :float :int)) (def AudioStream (ffi/struct :pointer :pointer :uint :uint :uint)) (def Sound (ffi/struct AudioStream :uint)) (def Texture (ffi/struct :uint :int :int :int :int)) # Keyboard key mapping (def key-map { :null 0 :apostrophe 39 :comma 44 :minus 45 :period 46 :slash 47 :zero 48 :one 49 :two 50 :three 51 :four 52 :five 53 :six 54 :seven 55 :eight 56 :nine 57 :semicolon 59 :equal 61 :a 65 :b 66 :c 67 :d 68 :e 69 :f 70 :g 71 :h 72 :i 73 :j 74 :k 75 :l 76 :m 77 :n 78 :o 79 :p 80 :q 81 :r 82 :s 83 :t 84 :u 85 :v 86 :w 87 :x 88 :y 89 :z 90 :left-bracket 91 :backslash 92 :right-bracket 93 :grave 96 :space 32 :escape 256 :enter 257 :tab 258 :backspace 259 :insert 260 :delete 261 :right 262 :left 263 :down 264 :up 265 :page-up 266 :page-down 267 :home 268 :end 269 :caps-lock 280 :scroll-lock 281 :num-lock 282 :print-screen 283 :pause 284 :f1 290 :f2 291 :f3 292 :f4 293 :f5 294 :f6 295 :f7 296 :f8 297 :f9 298 :f10 299 :f11 300 :f12 301 :left-shift 340 :left-control 341 :left-alt 342 :left-super 343 :right-shift 344 :right-control 345 :right-alt 346 :right-super 347 :kb-menu 348 :kp-0 320 :kp-1 321 :kp-2 322 :kp-3 323 :kp-4 324 :kp-5 325 :kp-6 326 :kp-7 327 :kp-8 328 :kp-9 329 :kp-decimal 330 :kp-divide 331 :kp-multiply 332 :kp-subtract 333 :kp-add 334 :kp-enter 335 :kp-equal 336 }) # Window-related functions (ffi/defbind InitWindow :void [width :int height :int title :string]) (ffi/defbind CloseWindow :void []) (ffi/defbind WindowShouldClose :bool []) (ffi/defbind IsWindowReady :bool []) (ffi/defbind IsWindowFullscreen :bool []) (ffi/defbind ToggleFullscreen :void []) (ffi/defbind SetWindowTitle :void [title :string]) (ffi/defbind GetScreenWidth :int []) (ffi/defbind GetScreenHeight :int []) # Drawing-related functions (ffi/defbind BeginDrawing :void []) (ffi/defbind EndDrawing :void []) (ffi/defbind ClearBackground :void [color Color]) (ffi/defbind BeginMode2D :void [camera Camera2D]) (ffi/defbind EndMode2D :void []) (ffi/defbind BeginMode3D :void [camera Camera3D]) (ffi/defbind EndMode3D :void []) # Basic shapes drawing functions (ffi/defbind DrawPixel :void [posX :int posY :int color Color]) (ffi/defbind DrawLine :void [startPosX :int startPosY :int endPosX :int endPosY :int color Color]) (ffi/defbind DrawCircle :void [centerX :int centerY :int radius :float color Color]) (ffi/defbind DrawRectangle :void [posX :int posY :int width :int height :int color Color]) (ffi/defbind DrawTriangle :void [v1 Vector2 v2 Vector2 v3 Vector2 color Color]) # Text drawing functions (ffi/defbind DrawText :void [text :string posX :int posY :int fontSize :int color Color]) (ffi/defbind DrawFPS :void [posX :int posY :int]) # Texture loading and drawing functions (ffi/defbind LoadTexture Texture [fileName :string]) (ffi/defbind UnloadTexture :void [texture Texture]) (ffi/defbind DrawTexture :void [texture Texture posX :int posY :int tint Color]) # Input-related functions (ffi/defbind IsKeyPressed :bool [key :int]) (ffi/defbind IsKeyDown :bool [key :int]) (ffi/defbind IsMouseButtonPressed :bool [button :int]) (ffi/defbind GetMousePosition Vector2 []) # Audio-related functions (ffi/defbind InitAudioDevice :void []) (ffi/defbind CloseAudioDevice :void []) (ffi/defbind LoadSound Sound [fileName :string]) (ffi/defbind PlaySound :void [sound Sound]) (ffi/defbind StopSound :void [sound Sound]) (ffi/defbind UnloadSound :void [sound Sound]) # 3D-related functions (ffi/defbind DrawCube :void [position Vector3 width :float height :float length :float color Color]) (ffi/defbind DrawSphere :void [centerPos Vector3 radius :float color Color]) # Timing-related functions (ffi/defbind SetTargetFPS :void [fps :int]) (ffi/defbind GetFrameTime :float []) ```
bakpakin commented 2 weeks ago

Wow, thanks so much @SyrupThinker , I had been procastinating on adding AARCH64 support to FFI for a while, this is very helpful!

By my naive understanding, aarch64 ABIs are much more uniform than the x86 world, with macos only slightly different than linux and windows. I have an old raspberry pi 2 or 3 somewhere that I should be able to test on "real" hardware, although I think QEMU should be fairly representative for this use.

welcome-linja commented 1 week ago

By my naive understanding, aarch64 ABIs are much more uniform than the x86 world, with macos only slightly different than linux and windows.

@bakpakin's understanding matches mine, sans one blind spot: the register that holds the syscall number. I don't recall seeing this specified in the Arm ABI docs. I know Linux uses x8, but I have a vague idea that FreeBSD uses something different? I'd check but it's buried in the source and I can't recall what file it's in. Then again, maybe FFI doesn't touch it, so it doesn't matter.

GrayJack commented 3 days ago

I know Linux uses x8, but I have a vague idea that FreeBSD uses something different? I'd check but it's buried in the source and I can't recall what file it's in. Then again, maybe FFI doesn't touch it, so it doesn't matter.

FreeBSD (and iirc, all BSD that has support for aarch64) uses x8 for the syscall number. The file that you were looking for is lib/libsys/aarch64/SYS.h on FreeBSD source

sogaiu commented 2 days ago

May be this line in the mentioned file is of interest.