Hi, I've prodded around at this topic with PRs and discussions to do with calling conventions generally and the SwiftABIInfo in clang, but I realised I didn't really understand it properly. I wanted to ask some basic questions to try and make sure I get it properly as I realised there are subtleties. To get my head straight, I wanted to split the questions into three separate scenarios:
- pure clang building machine code sample back end (in my case AVR) from normal C code (e.g. header files/C files)
- swift building machine code for various back ends (in my case AVR) where it calls other swift functions
- swift calling C code for various back ends (in my case AVR)
Note: one thing to bear in mind is the AVR back end is rather crude compared to the major architectures, it only recently became mainstream and has had orders of magnitude less work done on it than back ends such as ARM/x86. It is definitely not guaranteed bug free. We will improve it but it's baby steps really, given our community size.
- In the first case, when clang/llvm is implementing the de-facto standard AVR ABI (avr-gcc - GCC Wiki), I realised I was missing subtleties. I'm familiar with the llvm AVR back end code, but that's only part of the picture. The AVR GCC ABI in its section on "calling convention" describes how to allocate registers and structure the stack for calls but with terms like "the first argument", "the second argument", "the return value", etc. it's talking about the end to end process from C call sites to machine code. In reality clang does some of that and LLVM does some. The part myself, Ben, Dylan, Ayke and others are familiar with is the code in llvm-project/llvm/lib/Target/AVR such as AVRTargetLowering::LowerCall. These actually lower from LLVM IR to machine code (register assignments, stack handling, call frame creation). To state the obvious, clang does the lowering from C code call sites to LLVM IR function definitions. I think I have always envisaged that as a one to one mapping, but it doesn't have to be. For complex calling conventions and/or types, clang might change the LLVM IR emitted somewhat.
From what I can see, the clang code does very little modification to the arguments as lowered from C and mostly passes them to LLVM unchanged. Also it doesn't seem to respect any calling convention flags, which probably accounts for some of the confused conversations between our team and the experienced clang/llvm people. Most of the code that decides on things like register allocations, pass on the stack, etc. is hard coded in the AVR Target layer, and largely ignores calling conventions, sticking to the de-facto standard calling convention for AVR (the GCC one from 20 years ago).
- By contrast, swift calling swift has a great deal of latitude choosing what LLVM IR to emit, when lowering complex structures, it is free to change the structure of the IR in varied ways. The AVR back end won't know this has happened and will dutifully lower the LLVM IR arguments it is passed, following the AVR-GCC convention on the simple LLVM IR types passed and returned.
To emphasise, this may not be efficient or ideal but it's probably the best we can do for now. We can definitely improve it as time goes on.
- Where Swift calls C, I expect things to get a bit more sensitive. It's important that argument types match. This is where I could see some issues sometimes. If our platform doesn't support the swift call convention and the clang importer imports a header file that has functions attributed with swift call and assumes it's working, that might cause an issue?
Anyway... I'm just trying to understand if the above is all correct. Given that our platform basically doesn't support any calling convention other than the standard AVR calling convention, I wonder if I should be adding SwiftABIInfo to the AVR parts of clang at all? I think I did it at one point to resolve a crash but it might not be needed at all at this time?