Go: `uintptr` support?
dgryski opened this issue · 12 comments
https://golang.org/pkg/builtin/#uintptr
It seems to be this can map directly to uintptr_t
?
diff --git a/peachpy/x86_64/function.py b/peachpy/x86_64/function.py
index f4614d6..00c81b9 100644
--- a/peachpy/x86_64/function.py
+++ b/peachpy/x86_64/function.py
@@ -130,6 +130,8 @@ class Function:
return "boolean"
elif c_type.is_size_integer:
return "int" if c_type.is_signed_integer else "uint"
+ elif c_type.is_pointer_integer:
+ return "uintptr"
elif c_type.is_signed_integer:
return {
1: "int8",
My rationale for wanting this is to make it easier to have slices and structs as arguments.
A slice header ( https://golang.org/pkg/reflect/#SliceHeader ) can be simulated by having three arguments,
s = Argument(ptr(uintptr_t))
s_len = Argument(int64_t)
s_cap = Argument(int64_t)
(I know the documentation says to use ptrdiff_t
to get Go's int
, but that really makes the code confusing... In my code I'm using int64 explicitly because I know I'm on 64-bit platform.)
However, due to the lack of mapping for uintptr, this fails when trying to generate the commented function header -- I have to put an explicit integer type for s
.
Similarly if I want to pass a pointer to a struct, using uintptr_t
seems the only sane option.
Would size_t
work for you? Its an equivalent of Go's uint
I think my goal is to find an integer type that would make semantic sense rather than just one which generates the correct assembly instructions. Reading the peachpy code and seeing size_t
for a pointer and a ptrdiff_t
for the length and capacity is confusing. There is already a uintptr_t
type both in Go and C -- it just needs to be mapped.
I absolutely agree that one should use the type that makes semantic sense; I just don't think that uintptr_t
is such type.
- Semantically,
size_t
is the right type for index, length and capacity. E.g. in C++ bothstd::vector<T>::size()
andstd::vector<T>::capacity()
returnsize_t
.sizeof
operator also returns asize_t
. uintptr_t
is the right type to do integer arithmetic on pointers. E.g. if one wants to round a pointer to the next 16-byte boundary - using operators which are not supported for pointer types - one would cast the pointer touintptr_t
, do the arithmetics, and then cast back to pointer types.- There are platforms (albeit AFAIK none of them is supported by Go toolchain) where
size_t
anduintptr_t
are different in C ABI. E.g. on some 16-bit platforms with segments arrays must fit into a single segment, limited in size by 64K; however, pointers are 32-bit wide. On such platformssize_t
is be 16-bit wide (enough to hold length, capacity or index into any array), butuintptr_t
is 32-bit wide (enough to cast a pointer).
Ah, so this is where my Go-centricity is confusing: It seems like the uintrptr
type in Go (which is used in the runtime to represent an arbitrary pointer) doesn't quite match with uintptr_t
in C.
I agree about size_t
being semantically correct for length and capacity, but unfortunately size_t
is unsigned and len/cap are both signed :( ( While negative lengths and capacity don't make sense, having them as regular Go int
s make the code that uses them much simpler.)
Not sure there's any straight-forward fix here then :(
There is ssize_t
as well (signed size_t
), which should map to Go's int
. The only cons is that ssize_t
is a POSIX type, rather than C type, but if you plan to only use it with Go, this shouldn't be a problem.
This came out of me starting to write down a blog post with examples on integrating Go and PeachPy. It will probably have a large audience. I'd like to make suggestions that make sense both from the Go side and the PeachPy side. ssize_t
seems like it solves the problem for length and capacity (although PeachPy seems to only use it in the types_map
in Types.as_ctypes_type
as the mapping for the ptrdiff_t
type. It doesn't look like I can use s_len = Argument(ssize_t)
.
Right, I thought I implemented ssize_t
in PeachPy, but I didn't. I suggest to use size_t
for cap and length of the slice
arguments. As they are always positive, the difference between signed/unsigned is not important.
So, to summarize:
- To pass capacity and length components of
slice
, use PeachPy'ssize_t
type - To pass other arguments of Go's
int
type, use PeachPy'sptrdiff_t
type - To pass arguments of Go's
uint
type, use PeachPy'ssize_t
type - To pass generic pointers (
void*
in C), use PeachPy'sptr()
type
To pass generic pointers (void* in C), use PeachPy's ptr() type
Aha! This was the piece I was missing. When I was poking around previously I think I missed that you could just have a raw ptr()
without having a pointer to something. This absolutely solves my need for the uintrptr
type.
I'll use size_t
for the length and capacity and ignore the signed/unsigned differences. Although I do have a slight (unfounded?) concern that PeachPy might generate the wrong instruction somewhere?
I do notice that if I use a plain ptr()
, I don't get a function prototype comment in front of the generated assembly function. (Not vital to fix..) In Go, this is where the uintptr
type would be used.
PeachPy uses argument types for two purposes:
- To figure out how arguments are passed. In case of Golang, arguments are always passed on stack, and the stack offset is determined by type size. As
uint
andint
have the same size, signedness doesn't affect their offset on stack. - To generate C header, if you invoke PeachPy with
--emit-c-header
option. Apparently, this is not your use-case.
Maybe
diff --git a/peachpy/x86_64/function.py b/peachpy/x86_64/function.py
index f4614d6..3e21117 100644
--- a/peachpy/x86_64/function.py
+++ b/peachpy/x86_64/function.py
@@ -126,6 +126,8 @@ class Function:
assert isinstance(c_type, peachpy.Type)
if c_type.is_pointer and c_type.base is not None:
return "*" + c_to_go_type(c_type.base)
+ elif c_type.is_pointer:
+ return "uintptr"
elif c_type.is_bool:
return "boolean"
elif c_type.is_size_integer:
to add uintptr
as the Go-type for raw pointers with no base type?
Thanks!