On Sun, Jul 14, 2013 at 11:08 AM, minux wrote:
On Mon, Jul 15, 2013 at 12:36 AM, Kevin Klues wrote:

I'm currently in the process of porting go to a new OS for 386/amd64
and, in doing so, find myself writing go code that needs to be called
just curious, what's the target OS?
The OS is called Akaros. It's part of my Ph.D. work at Berkeley.
from c code under various circumstances. In doing so, I've come
across an error that may or may not be a compiler bug depending on
whether or not this type of operation is supported.

Glancing through the code base, I've found a few places where go
already gest called directly from c (most notably main·main() in
pkg/runtime/proc.c), but these calls appear to be far and few between.
Since this sort of operation doesn't seem to be documented anywhere
and its use is not very ubiquitous throughout the code base, it's
actually not clear to me whether this practice is encouraged or even
supported at all.
it's supported (in the sense that it's supported by the toolchain), 6c/8c
just can't get regular Go function return values, so you must define your
C-callable Go function like this:
func CCallableFunction(args uintptr /* or others, prefers pointer for
anything larger
than uintptr */, retval *uintptr) { /* blah blah */ }
I was originally doing things this way, but was hoping there was a way
to get at the return values of existing go functions without modifying
them. I guess I can always wrap them in a go function of a slightly
different name and get at them that way.

That said, from what I can tell by looking at the x86 assembly
generated for go functions vs. 8c/6c functions, it seems that it
should be OK to just directly call go functions from c, so long as the
go function only returns a single value (or void), and I get the
function declaration right when I extern it in, e.g..
go: func go_func(n int32) int32 { return n }
c: extern int32 go_func(int32 n);
it won't always work, as C expects the return value in some register (AX for
R0 for ARM), Go always set return value on stack, so actually what you got
for the
n here is the last value that is stored into that register in the Go
function, and it might
not be the actual return value.
Yeah, I wasn't sure if it was just dumb luck that the return value was
also in EAX or if that was done on purpose by the compiler so that c
code could call it as if it was calling a regular c function.
Probably safer to always pass it as a parameter.

So long as I adhere to these guidelines, this has worked well for me
in almost every case I've encountered -- with one notable exception.
If I call a go function from within a c function and the c function
has the following properties, I get a page fault in my executing
page fault? you mean SIGSEGV/SIGBUS?
In Akaros, we report it differently, but yeah, faulting on trying to
access an unmapped address, e.g. NULL.

1) The c function does not take any arguments
2) The c function does not have any local variables
3) The c function makes a tail call to return the value of a go remember
function, rather than explicitly consuming the go functions return
value and then returning that,
return pkg·func();
// rather than ...
n = pkg.func();
return n;

Unlike in C, go functions appear to pass their return values both on
the stack (I assume to accommodate support for multiple return values)
as well as in eax in the case of having a single return value (as is
i think this is not guaranteed, it might just that the register allocator
to use AX to save the return value to stack.
so to be safe, you should treat the return value as an argument in the C
for example:
// in Go
func GoFunc(int) int
// in C
extern void GoFunc(intgo, intgo);

as i said above, in C you can't treat the GoFunc as returning one intgo
the common practice in standard c). So long as some stack space has
been reserved for ANY purpose within the body of the c function, a
tail call to a go function will benignly overwrite some (now unusable)
local variable, and the return value will be propagated to its caller
through eax (just as in standard c). Assembly output for such a
scenario can be seen below:
// Check if we need to grow our linked stack
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
ja <c_func+0x14>
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>
// Reserve some stack space for local operations
sub $0xc,%esp
// Do some local operations
// Make a tail call to return the value of go_func()
call <go_func>
// Restore esp to the value it had upon entering
add $0xc,%esp

However, if the conditions listed above are satisfied, then assembly
such as the following ends up being generated (on 32-bit x86):
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
ja <c_func+0x14>
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>
// No stack reservation
call <global_func_1>
call <global_func_2>
call <go_func>
// No stack restore

Since this c function takes no arguments and has no local variables,
then no stack space is reserved that is able to consume the return
value from the tail call to the go function, and instead, the return
value of the c function residing at 0(%esp) gets overwritten by the go
function, and the c function returns to some garbage location.

A full example can be constructed as follows:
extern int32 go_pkg·go_func();
static int32 global_data = 10;
static int32 change_global() { global_data += 1; }
static int32 buggy_func() {
return go_pkg·go_func();

Which generates the following assembly:
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
ja <buggy_func+0x14>
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>
call <change_global>
call <change_global>
call <change_global>
call <go_pkg.go_func>


As a side note, is there any particular reason that the preamble to
each function is of the form:
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
ja <func+0x14>
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>
// Actual function body at <func+0x14>

Rather than:
mov %gs:0xffffffdc,%ecx
cmp (%ecx),%esp
jnbe <stack_stuff>
// Actual function body
xor %edi,%edi
xor %eax,%eax
call <runtime.morestack>

The second one is slightly more efficient since (in the common case)
the linked stacks will not need to grow and a jump will be avoided
(saving 12 cycles per function call).
have you measured the difference? i think it might not be that big since
the pc difference is pretty small, and also that for the actual function
to execute, we must wait for the result for last cmp instruction, so i don't
think there could be that big difference for inverting the comparison
I haven't measured the difference, though intuitively it seems like it
would be slower since a call to ja/jnbe take 16 cycles if a jump
occurs and only 4 cycles if it does not. That said, I'm not an expert
on the intricacies of branch prediction, out of order processing, etc.
so it very well could be a negligible difference when these sort of
cpu optimizations are taken into account.


You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Search Discussions

Discussion Posts


Related Discussions

Discussion Navigation
viewthread | post
posts ‹ prev | 4 of 4 | next ›
Discussion Overview
groupgolang-nuts @
postedJul 14, '13 at 5:33p
activeJul 15, '13 at 2:11a

2 users in discussion

Kevin Klues: 3 posts Minux: 1 post



site design / logo © 2022 Grokbase