Stream: t-compiler

Topic: rust calling convention and LLVM


Jake Goulding (Oct 11 2018 at 14:26, on Zulip):

How does the concept of the "Rust" calling convention make its way into LLVM? LLVM has built-in concepts for other types of calling conventions / ABIs like "C" or "stdcall", but not one for "Rust".

nagisa (Oct 11 2018 at 14:27, on Zulip):

its just "unspecified", which in LLVM case can translate into any of its calling conventions

nagisa (Oct 11 2018 at 14:28, on Zulip):

but what also matters is types of arguments

nagisa (Oct 11 2018 at 14:28, on Zulip):

which in Rust cc are just unspecified and chosen fairly arbitrarily

simulacrum (Oct 11 2018 at 14:29, on Zulip):

hm, https://github.com/rust-lang/rust/blob/fddcd316af98583ebebfc40f6a25bec3f4e5fccc/src/librustc_codegen_llvm/abi.rs#L338-L340 seems to indicate it's "C" to LLVM?

simulacrum (Oct 11 2018 at 14:29, on Zulip):

(but maybe that's the wrong place to look)

nagisa (Oct 11 2018 at 14:29, on Zulip):

its just "unspecified", which in LLVM case can translate into any of its calling conventions

Well let me correct myself. If the calling convention in LLVM is unspecified, it is usually just the prevailing calling convention for the platform.

nagisa (Oct 11 2018 at 14:29, on Zulip):

but like I said, how arguments are passed also matters. To get actual "C", the arguments should be passed around in a very specific manner. This does not apply to the rust calling convention

nagisa (Oct 11 2018 at 14:30, on Zulip):

which is how it becomes unspecified and arbitrary.

Jake Goulding (Oct 11 2018 at 14:38, on Zulip):

This is in the context of the AVR port, where people are working on the actual LLVM side of it as well, FWIW.

nagisa (Oct 11 2018 at 14:39, on Zulip):

Well yeah, then whatever AVR does with "unspecified" calling convention on its side is correct.

nagisa (Oct 11 2018 at 14:39, on Zulip):

which can be compatible with C (and will most likely be) for all its worth

Jake Goulding (Oct 11 2018 at 14:39, on Zulip):

So it seems likely that "Rust" will set up some concoction of arguments and hand that off to LLVM, which is most likely going to try to align with what GCC outputs

nagisa (Oct 11 2018 at 14:40, on Zulip):

possibly. It is however a case that Rust often gives LLVM arguments in a manner that C would never do

nagisa (Oct 11 2018 at 14:41, on Zulip):

passing large arrays by value is one example of such case.

Jake Goulding (Oct 11 2018 at 14:41, on Zulip):

Is there any concrete thing (that wouldn't take too much of your time to explain) you could show as an example of how the arguments...

nagisa (Oct 11 2018 at 14:42, on Zulip):

oh, hmm, apparently we now pass larger arrays by reference…

nagisa (Oct 11 2018 at 14:43, on Zulip):

@Jake Goulding in that case a good example is probably 2-element tuples and fat pointers

nagisa (Oct 11 2018 at 14:43, on Zulip):

which Rust will split into two separate arguments in some cases, despite it really being just one

nagisa (Oct 11 2018 at 14:43, on Zulip):

that’s probably the best example to give as it is most commonplace too.

nagisa (Oct 11 2018 at 14:44, on Zulip):

then there’s also i128, which C on avr will definitely not support, and thus won’t ever pass as an argument :slight_smile:

nagisa (Oct 11 2018 at 14:45, on Zulip):

(it usually ends up becoming a literal i128 in LLVM)

nagisa (Oct 11 2018 at 14:45, on Zulip):

(and very likely a pointer with some sort of platform-specific ABI in C)

Jake Goulding (Oct 11 2018 at 14:45, on Zulip):

Whereas Rust-on-AVR will support i128 (we will just generate some code that uses up all of your registers, most likely)

Jake Goulding (Oct 11 2018 at 14:46, on Zulip):

we now pass larger arrays by reference

And that's part of what makes it the "Rust" ABI as well, the fact that these things can change without warning.

nagisa (Oct 11 2018 at 14:48, on Zulip):

definitely.

Jake Goulding (Oct 11 2018 at 14:49, on Zulip):
pub extern "Rust" fn foo(_: (i32, i32)) {}
// "Rust" => define void @_ZN10playground3foo17hb3200a9d2844f1a1E(i32, i32)
// "C"    => define void @_ZN10playground3foo17h740a1913e9510d11E(i64)

Is that what you were talking about? (I'd bet that using a tuple in extern "C" is invalid, but it demonstrates it)

nagisa (Oct 11 2018 at 14:52, on Zulip):

Yes, this is one of the scenarios. It will end up using different number of registers etc.

Jake Goulding (Oct 11 2018 at 14:56, on Zulip):

So the C frontend would see the equivalent of void foo(i128 bar) and emit LLVM like define void foo(* i128).

Rust would see fn foo(bar: i128) and emit define void foo( i128), but if it saw extern "C" fn foo(bar: i128), it would emit define void foo(* i128), as C did.

Jake Goulding (Oct 11 2018 at 14:56, on Zulip):

Is this the difference between a calling convention and an ABI?

nagisa (Oct 11 2018 at 14:57, on Zulip):

I guess, yes. It would be more appropriate to call those "convention" annotations on top of LLVM functions as influencing ABI instead

nagisa (Oct 11 2018 at 14:58, on Zulip):

and then compilers are responsible ensuring that both ABI and the way the arguments are passed match.

Jake Goulding (Oct 11 2018 at 15:12, on Zulip):

Thank you both very much! It's always nice to learn that I was wrong and now I can correct myself going forward.

Last update: Nov 16 2019 at 01:05UTC