Skip to content

Commit

Permalink
All: Support float arguments with CGO_ENABLED=1 on Linux
Browse files Browse the repository at this point in the history
  • Loading branch information
jwijenbergh committed Sep 22, 2023
1 parent 63ba036 commit e23c163
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 199 deletions.
90 changes: 0 additions & 90 deletions callback_float_test.go

This file was deleted.

81 changes: 80 additions & 1 deletion callback_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors

//go:build darwin || linux
//go:build darwin || (linux && (!cgo || arm64 || amd64))

package purego_test

Expand Down Expand Up @@ -48,3 +48,82 @@ func TestCallGoFromSharedLib(t *testing.T) {
}
}
}

func TestNewCallbackFloat64(t *testing.T) {
// This tests the maximum number of arguments a function to NewCallback can take
const (
expectCbTotal = -3
expectedCbTotalF = float64(36)
)
var cbTotal int
var cbTotalF float64
imp := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int,
f1, f2, f3, f4, f5, f6, f7, f8 float64) {
cbTotal = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9
cbTotalF = f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8
})
var fn func(a1, a2, a3, a4, a5, a6, a7, a8, a9 int,
f1, f2, f3, f4, f5, f6, f7, f8 float64)
purego.RegisterFunc(&fn, imp)
fn(1, 2, -3, 4, -5, 6, -7, 8, -9,
1, 2, 3, 4, 5, 6, 7, 8)

if cbTotal != expectCbTotal {
t.Errorf("cbTotal not correct got %d but wanted %d", cbTotal, expectCbTotal)
}
if cbTotalF != expectedCbTotalF {
t.Errorf("cbTotalF not correct got %f but wanted %f", cbTotalF, expectedCbTotalF)
}
}

func TestNewCallbackFloat32(t *testing.T) {
// This tests the maximum number of float32 arguments a function to NewCallback can take
const (
expectCbTotal = 6
expectedCbTotalF = float32(45)
)
var cbTotal int
var cbTotalF float32
imp := purego.NewCallback(func(a1, a2, a3, a4, a5, a6, a7, a8 int,
f1, f2, f3, f4, f5, f6, f7, f8, f9 float32) {
cbTotal = a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8
cbTotalF = f1 + f2 + f3 + f4 + f5 + f6 + f7 + f8 + f9
})
var fn func(a1, a2, a3, a4, a5, a6, a7, a8 int,
f1, f2, f3, f4, f5, f6, f7, f8, f9 float32)
purego.RegisterFunc(&fn, imp)
fn(1, 2, -3, 4, -5, 6, -7, 8,
1, 2, 3, 4, 5, 6, 7, 8, 9)

if cbTotal != expectCbTotal {
t.Errorf("cbTotal not correct got %d but wanted %d", cbTotal, expectCbTotal)
}
if cbTotalF != expectedCbTotalF {
t.Errorf("cbTotalF not correct got %f but wanted %f", cbTotalF, expectedCbTotalF)
}
}

func TestNewCallbackFloat32AndFloat64(t *testing.T) {
// This tests that calling a function with a mix of float32 and float64 arguments works
const (
expectedCbTotalF32 = float32(30)
expectedCbTotalF64 = float64(15)
)
var cbTotalF32 float32
var cbTotalF64 float64
imp := purego.NewCallback(func(f1, f2, f3 float32, f4, f5, f6 float64, f7, f8, f9 float32) {
cbTotalF32 = f1 + f2 + f3 + f7 + f8 + f9
cbTotalF64 = f4 + f5 + f6

})
var fn func(f1, f2, f3 float32, f4, f5, f6 float64, f7, f8, f9 float32)
purego.RegisterFunc(&fn, imp)
fn(1, 2, 3, 4, 5, 6, 7, 8, 9)

if cbTotalF32 != expectedCbTotalF32 {
t.Errorf("cbTotalF32 not correct got %f but wanted %f", cbTotalF32, expectedCbTotalF32)
}
if cbTotalF64 != expectedCbTotalF64 {
t.Errorf("cbTotalF64 not correct got %f but wanted %f", cbTotalF64, expectedCbTotalF64)
}
}
2 changes: 1 addition & 1 deletion dlfcn_nocgo_linux.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors

//go:build !cgo
//go:build !cgo || arm64 || amd64

package purego

Expand Down
2 changes: 1 addition & 1 deletion dlfcn_stubs.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors

//go:build darwin || freebsd || (linux && !cgo)
//go:build darwin || freebsd || (linux && (!cgo || arm64 || amd64))

#include "textflag.h"

Expand Down
3 changes: 0 additions & 3 deletions func.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,6 @@ func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
// This means that using arg ...interface{} is like a cast to the function with the arguments inside arg.
// This is not the same as C variadic.
//
// There is one limitation when using RegisterFunc on Linux: There is no support for float32 and float64 arguments/return values with CGO_ENABLED=1.
// Linux otherwise has the same feature parity as Darwin.
//
// # Memory
//
// In general it is not possible for purego to guarantee the lifetimes of objects returned or received from
Expand Down
71 changes: 70 additions & 1 deletion sys_amd64.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors

//go:build darwin || freebsd || (!cgo && linux)
//go:build darwin || freebsd || linux

#include "textflag.h"
#include "abi_amd64.h"
Expand Down Expand Up @@ -72,3 +72,72 @@ TEXT syscall9X(SB), NOSPLIT|NOFRAME, $0
MOVQ BP, SP
POPQ BP
RET

TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
// remove return address from stack, we are not returning to callbackasm, but to its caller.
MOVQ 0(SP), AX
ADDQ $8, SP

MOVQ 0(SP), R10 // get the return SP so that we can align register args with stack args

// make space for first six int and 8 float arguments below the frame
ADJSP $14*8, SP
MOVSD X0, (1*8)(SP)
MOVSD X1, (2*8)(SP)
MOVSD X2, (3*8)(SP)
MOVSD X3, (4*8)(SP)
MOVSD X4, (5*8)(SP)
MOVSD X5, (6*8)(SP)
MOVSD X6, (7*8)(SP)
MOVSD X7, (8*8)(SP)
MOVQ DI, (9*8)(SP)
MOVQ SI, (10*8)(SP)
MOVQ DX, (11*8)(SP)
MOVQ CX, (12*8)(SP)
MOVQ R8, (13*8)(SP)
MOVQ R9, (14*8)(SP)
LEAQ 8(SP), R8 // R8 = address of args vector

MOVQ R10, 0(SP) // push the stack pointer below registers

// determine index into runtime·cbs table
MOVQ $callbackasm(SB), DX
SUBQ DX, AX
MOVQ $0, DX
MOVQ $5, CX // divide by 5 because each call instruction in ·callbacks is 5 bytes long
DIVL CX
SUBQ $1, AX // subtract 1 because return PC is to the next slot

// Switch from the host ABI to the Go ABI.
PUSH_REGS_HOST_TO_ABI0()

// Create a struct callbackArgs on our stack to be passed as
// the "frame" to cgocallback and on to callbackWrap.
// $24 to make enough room for the arguments to runtime.cgocallback
SUBQ $(24+callbackArgs__size), SP
MOVQ AX, (24+callbackArgs_index)(SP) // callback index
MOVQ R8, (24+callbackArgs_args)(SP) // address of args vector
MOVQ $0, (24+callbackArgs_result)(SP) // result
LEAQ 24(SP), AX // take the address of callbackArgs

// Call cgocallback, which will call callbackWrap(frame).
MOVQ ·callbackWrap_call(SB), DI // Get the ABIInternal function pointer
MOVQ (DI), DI // without <ABIInternal> by using a closure.
MOVQ AX, SI // frame (address of callbackArgs)
MOVQ $0, CX // context

CALL crosscall2(SB) // runtime.cgocallback(fn, frame, ctxt uintptr)

// Get callback result.
MOVQ (24+callbackArgs_result)(SP), AX
ADDQ $(24+callbackArgs__size), SP // remove callbackArgs struct

POP_REGS_HOST_TO_ABI0()

MOVQ 0(SP), R10 // get the SP back

ADJSP $-14*8, SP // remove arguments

MOVQ R10, 0(SP)

RET
2 changes: 1 addition & 1 deletion sys_arm64.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors

//go:build darwin || freebsd || (!cgo && linux) || windows
//go:build darwin || freebsd || linux || windows

#include "textflag.h"
#include "go_asm.h"
Expand Down
78 changes: 0 additions & 78 deletions sys_unix_amd64.s

This file was deleted.

Loading

0 comments on commit e23c163

Please sign in to comment.