Crystal: C ABI: passing a struct after numerous other args works wrong

Created on 20 Jun 2020  路  7Comments  路  Source: crystal-lang/crystal

The following function definition generates wrong calling code in x86_64 ABI:

lib Lib
  struct Vec
    x : Int64
    y : Int64
  end

  fun foo(Int64, Int64, Int64, Int64, Int64, Vec)
end

Lib.foo(1, 2, 3, 4, 5, Lib::Vec.new(x: 6, y: 7))

The C code sees this data:

void foo(long a, long b, long c, long d, long e, Vec v) {
    printf("%ld %ld %ld %ld %ld %ld %ld\n", a, b, c, d, e, v.x, v.y);
    // 1 2 3 4 5 7 139713085455776
}

The last two should be 6 7, not 7 <pointer to v>. And that's just the basic case, I'm sure you could get it to crash instead.


Compare the calling code generated by Crystal:

call void @foo(i64 1, i64 2, i64 3, i64 4, i64 5, { i64, i64 } %9)

to an analogous call generated from C by Clang (thanks, @jhass):

%struct.Vec = type { i64, i64 }
[...]
call void @foo(i64 1, i64 2, i64 3, i64 4, i64 5, %struct.Vec* byval(%struct.Vec) align 8 %1)

As I pointed out that this starts happening only beyond a certain number of arguments, @jhass found this very likely lead – that there can be "not enough registers".


Indeed, if we remove one of the integer args, the behaviors converge (without any change on Crystal's part):

call void @foo(i64 1, i64 2, i64 3, i64 4, { i64, i64 } %9)

vs Clang:

call void @foo(i64 1, i64 2, i64 3, i64 4, i64 %5, i64 %7)

This leads us to the conclusion that Crystal probably just doesn't have any code handling the "overflow" arguments, but it should...

bug platform

All 7 comments

After I ported the code from Rust, Rust kept changing and tweaking things. We need to update the code to reflect the latest Rust (or any other) language. If anyone is up for a challenge, then please do so!

There's another issue I already reported where I mention the ABI isn't working well in some cases, so this is almost a duplicate.

I found this issue while fixing something for Win64 ABI (#9520). I added a spec for the previously failing case, and it turned out that it fails on x86_64 too! Presumably for completely different reasons.

So if someone wants to try fixing this, they can enable that spec and see.
Also some pointers in chat.

@asterite I see, that would probably be https://github.com/crystal-lang/crystal/issues/4890.

Well, at least I think my report is better isolated and with some further digging on the root cause.

An interesting tidbit is how cgo deals with handling the C ABI: https://github.com/golang/go/blob/master/src/runtime/cgocall.go#L5

@jhass tl;dr? :-)

Or what's the relevant part?

No relevant part and no TL;DR, just interesting that Google's solution to the whole C ABI problem is "yeah nope, let's avoid re-implementing that and reuse the C compiler's implementation if possible in any way". Also interesting read on how they did so.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

oprypin picture oprypin  路  3Comments

pbrusco picture pbrusco  路  3Comments

lgphp picture lgphp  路  3Comments

asterite picture asterite  路  3Comments

relonger picture relonger  路  3Comments