| Deutsch English Français Italiano |
|
<vk204q$310pu$1@dont-email.me> View for Bookmarking (what is this?) Look up another Usenet article |
Path: ...!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: BGB <cr88192@gmail.com> Newsgroups: comp.lang.c Subject: Re: transpiling to low level C Date: Thu, 19 Dec 2024 14:36:37 -0600 Organization: A noiseless patient Spider Lines: 345 Message-ID: <vk204q$310pu$1@dont-email.me> References: <vjlh19$8j4k$1@dont-email.me> <vjn9g5$n0vl$1@raubtier-asyl.eternal-september.org> <vjnhsq$oh1f$1@dont-email.me> <vjnq5s$pubt$1@dont-email.me> <vjp2f3$13k4m$2@dont-email.me> <vjr7np$1j57r$2@dont-email.me> <vjsdum$1rfp2$1@dont-email.me> <vjse6l$1rfp2$2@dont-email.me> <vjsf6g$1rlkq$1@dont-email.me> <vjsgdr$1rrvs$1@dont-email.me> <vjsi61$1rlkq$2@dont-email.me> <vjsk7q$1rrvs$2@dont-email.me> <vjv5ir$2ds8r$2@dont-email.me> <vjv8lv$2edrv$1@dont-email.me> <vjvp9g$2h6ck$1@dont-email.me> <vjvpos$2gsil$3@dont-email.me> <vk0bvf$2nn4a$1@dont-email.me> <vk0vu7$2qr2c$1@dont-email.me> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Injection-Date: Thu, 19 Dec 2024 21:36:43 +0100 (CET) Injection-Info: dont-email.me; posting-host="76877d6a1adb46369aecf81b3ea48ff6"; logging-data="3179326"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/b84GgJDLm75ofY5wigVTtx1rY2C+hD58=" User-Agent: Mozilla Thunderbird Cancel-Lock: sha1:v4vb93UdbHsa8oRZlCCBQ6yEYrs= Content-Language: en-US In-Reply-To: <vk0vu7$2qr2c$1@dont-email.me> Bytes: 14345 On 12/19/2024 5:27 AM, bart wrote: > On 19/12/2024 05:46, BGB wrote: >> On 12/18/2024 6:35 PM, bart wrote: >>> On 19/12/2024 00:27, BGB wrote: > >>>> By-Value Structs smaller than 16 bytes are passed as-if they were a >>>> 64 or 128 bit integer type (as a single register or as a register >>>> pair, with a layout matching their in-memory representation). >>>> >>>> ... >>>> >>>> >>>> But, yeah, at the IL level, one could potentially eliminate structs >>>> and arrays as a separate construct, and instead have bare pointers >>>> and a generic "reserve a blob of bytes in the frame and initialize >>>> this pointer to point to it" operator (with the business end of this >>>> operator happening in the function prolog). >>> >>> The problem with this, that I mentioned elsewhere, is how well it >>> would work with SYS V ABI, since the rules for structs are complex, >>> and apparently recursive. >>> >>> Having just a block of bytes might not be enough. >> >> In my case, I am not bothering with the SysV style ABI's (well, along >> with there not being any x86 or x86-64 target...). > > I'd imagine it's worse with ARM targets as there are so many more > registers to try and deconstruct structs into. > Not messed much with the ARM64 ABI or similar, but I will draw the line in the sand somewhere. Struct passing/return is enough of an edge case that one can just sort of declare it "no go" between compilers with "mostly but not strictly compatible" ABIs. >> >> For my ISA, it is a custom ABI, but follows mostly similar rules to >> some of the other "Microsoft style" ABIs (where, I have noted that >> across multiple targets, MS tools have tended to use similar ABI >> designs). > > When you do your own thing, it's easy. > > In the 1980s, I didn't need to worry about call conventions used for > other software, since there /was/ no other software! I had to write > everything, save for the odd calls to DOS which used some form of SYSCALL. > > Then, arrays and structs were actually passed and returned by value (not > via hidden references), by copying the data to and from the stack. > > However, I don't recall ever using the feature, as I considered it > efficient. I always used explicit references in my code. > Most of the time, one is passing/returning structures as pointers, and not by value. By value structures are usually small. When a structure is not small, it is both simpler to implement, and usually faster, to internally pass it by reference. If you pass a large structure to a function by value, via an on-stack copy, and the function assigns it to another location (say, a global variable): Pass by reference: Only a single copy operation is needed; Pass by value on-stack: At least two copy operations are needed. One also needs to reserve enough space in the function arguments list to hold any structures passed, which could be bad if they are potentially large. But, on my ISA, ABI is sort of like: R4 ..R7 : Arg0 ..Arg3 R20..R23: Arg4 ..Arg7 R36..R39: Arg8 ..Arg11 (optional) R52..R55: Arg12..Arg15 (optional) Return Value: R2, R3:R2 (128 bit) R2 is also used to pass in the return value pointer. 'this': Generally passed in either R3 or R18, depending on ABI variant. Where, callee-save: R8 ..R14, R24..R31, R40..R47, R56..R63 R15=SP Non-saved scratch: R2 ..R7 , R16..R23, R32..R39, R48..R55 Arguments beyond the first 8/16 register arguments are passed on stack. In this case, a spill space for the first 8/16 arguments (64 or 128 bytes) is provided on stack before the first non-register argument. If the function accepts a fixed number of arguments and the number of argument registers is 8 or less, spill space need only be provided for the first 8 arguments (calling vararg functions will always reserve space for 16 registers in the 16-register ABI). This spill space effectively belongs to the callee rather than the caller. Structures (by value): 1.. 8 bytes: Passed in a single register 9..16 bytes: Passed in a pair, padded to the next even pair 17+: Pass as a reference. Things like 128-bit types are also passed/returned in register pairs. Contrast, RV ABI: X10..X17 are used for arguments; No spill space is provided; ... My variant uses similar rules to my own ABI for passing/returning structures, with: X28, structure return pointer X29, 'this' Normal return values go into X10 or X11:X10. Note that in both ABI's, passing 'this' in a register would mean that class instances and COM objects are not equivalent (COM object methods always pass 'this' as the first argument). The 'this' register is implicitly also used by lambdas to pass in the pointer to the captured bindings area (which mostly resembles a structure containing each variable captured by the lambda). Can note though that in this case, capturing a binding by reference means the lambda is limited to automatic lifetime (non-automatic lambdas may only capture by value). In this case, capture by value is the default. >> For my compiler targeting RISC-V, it uses a variation of RV's ABI rules. >> Argument passing is basically similar, but struct pass/return is >> different; and it passes floating-point values in GPRs (and, in my own >> ISA, all floating-point values use GPRs, as there are no FPU >> registers; though FPU registers do exist for RISC-V). > > Supporting C's variadic functions, which is needed for many languages > when calling C across an FFI, usually requires different rules. On Win64 > ABI for example, by passing low variadic arguments in both GPRs and FPU > registers. > I simplified things by assuming only GPRs are used. > /Implementing/ variadic functions (which only occurs if implementing C) > is another headache if it has to work with the ABI (which can be assumed > for a non-static function). > > I barely have a working solution for Win64 ABI, which needs to be done > via stdarg.h, but wouldn't have a clue how to do it for SYS V. > > (Even Win64 has problems, as it assumes a downward-growing stack; in my ========== REMAINDER OF ARTICLE TRUNCATED ==========