Skip to content
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

Go: uintptr support? #55

Closed
dgryski opened this issue Nov 5, 2016 · 12 comments
Closed

Go: uintptr support? #55

dgryski opened this issue Nov 5, 2016 · 12 comments

Comments

@dgryski
Copy link

dgryski commented Nov 5, 2016

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.

@Maratyszcza
Copy link
Owner

Would size_t work for you? Its an equivalent of Go's uint

@dgryski
Copy link
Author

dgryski commented Nov 6, 2016

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.

@Maratyszcza
Copy link
Owner

Maratyszcza commented Nov 6, 2016

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++ both std::vector<T>::size() and std::vector<T>::capacity() return size_t. sizeof operator also returns a size_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 to uintptr_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 and uintptr_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 platforms size_t is be 16-bit wide (enough to hold length, capacity or index into any array), but uintptr_t is 32-bit wide (enough to cast a pointer).

@dgryski
Copy link
Author

dgryski commented Nov 6, 2016

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 ints make the code that uses them much simpler.)

Not sure there's any straight-forward fix here then :(

@Maratyszcza
Copy link
Owner

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.

@dgryski
Copy link
Author

dgryski commented Nov 6, 2016

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).

@Maratyszcza
Copy link
Owner

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.

@Maratyszcza
Copy link
Owner

So, to summarize:

  • To pass capacity and length components of slice, use PeachPy's size_t type
  • To pass other arguments of Go's int type, use PeachPy's ptrdiff_t type
  • To pass arguments of Go's uint type, use PeachPy's size_t type
  • To pass generic pointers (void* in C), use PeachPy's ptr() type

@dgryski
Copy link
Author

dgryski commented Nov 6, 2016

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.

@dgryski dgryski closed this as completed Nov 6, 2016
@Maratyszcza
Copy link
Owner

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 and int 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.

@dgryski
Copy link
Author

dgryski commented Nov 6, 2016

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?

@dgryski
Copy link
Author

dgryski commented Nov 6, 2016

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants