-
Notifications
You must be signed in to change notification settings - Fork 196
Native ARM64 Implementation Using Assembler #295
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
base: master
Are you sure you want to change the base?
Changes from all commits
43a0d7c
153bf5a
656543a
e286965
e8e0350
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,378 @@ | ||||||
| { ARM64 runtime call bridge (FPC + Linux/macOS SysV only) | ||||||
|
|
||||||
| This include contains the Pascal-side marshalling logic used by TPSExec.InnerfuseCall | ||||||
| on AArch64 targets. The low-level indirect call is delegated to arm64call in | ||||||
| arm64sysv.S. | ||||||
|
|
||||||
| High-level flow: | ||||||
| 1) Convert TPSVariant arguments to ABI-ready integer/float register values. | ||||||
| 2) Spill overflow arguments to an aligned stack buffer. | ||||||
| 3) Prepare hidden-result arguments/registers for managed/indirect result types. | ||||||
| 4) Call arm64call, which performs the actual register+stack setup and BLR. | ||||||
| 5) Copy return values back into Pascal Script result storage. | ||||||
| } | ||||||
|
|
||||||
| {$IFDEF CPUAARCH64} | ||||||
| {$IFDEF DARWIN} | ||||||
| {$L arm64sysv-macos.o} | ||||||
| {$ELSE} | ||||||
| {$L arm64sysv-linux.o} | ||||||
| {$ENDIF} | ||||||
| {$ENDIF} | ||||||
|
|
||||||
|
|
||||||
| type | ||||||
| TARM64Registers = packed record | ||||||
| X1, X2, X3, X4, X5, X6, X7: IPointer; | ||||||
| X8: IPointer; // indirect result register for AArch64 ABI | ||||||
| D1, D2, D3, D4, D5, D6, D7: Double; | ||||||
| FloatBits: Integer; // bits 0..7 = single args in D0..D7, bit 8 = single result | ||||||
| end; | ||||||
|
|
||||||
| const EmptyPChar:PChar=''; | ||||||
|
|
||||||
| procedure arm64call( | ||||||
| Address: Pointer; | ||||||
| out _X0: IPointer; | ||||||
| var _D0: Double; | ||||||
| constref Registers: TARM64Registers; | ||||||
| Stack: Pointer; | ||||||
| Items: NativeInt); external {$IFDEF DARWIN}name '_arm64call'{$ENDIF}; | ||||||
|
||||||
| Items: NativeInt); external {$IFDEF DARWIN}name '_arm64call'{$ENDIF}; | |
| Items: NativeInt); external {$IF DEFINED(DARWIN) OR DEFINED(MACOS)}name '_arm64call'{$IFEND}; |
Copilot
AI
Mar 25, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EmptyPChar is declared as a PChar, but the code passes @EmptyPChar when a btPChar argument is nil. That passes the address of the pointer variable (PPChar) rather than a pointer to a null-terminated char buffer, which will produce an invalid PChar for callees. Match the existing x86/x64 approach (e.g., define EmptyPchar as an array[0..0] of Char/#0) or pass the pointer value (EmptyPChar) rather than its address.
| StoreIntReg(IPointer(@EmptyPChar)) | |
| StoreIntReg(IPointer(EmptyPChar)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
arm64call reads the initial value of _X0 to populate x0 (first argument), but the parameter is declared as out. Using an out parameter as input is unsafe/misleading and can allow uninitialized reads if the caller changes. Declare _X0 as var (in/out) to reflect that it is both an input (arg x0) and an output (return x0).