Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> This is missing the caller/called save distinction and makes the usual error of assigning a subset of the input registers to output.

Out of curiosity, what's so problematic about using some input registers as output registers? On the caller's side, you'd want to vacate the output registers between any two function calls regardless. And it occurs pretty widely in syscall conventions, to my binary-golfing detriment.

Is it for the ease of the callee, so that it can set up the output values while keeping the input values in place? That would suggest trying to avoid overlap (by placing the output registers at the end of the input sequence), but I don't see how it would totally contraindicate any overlap.



You should use all the input registers as output registers, unless your arch is doing some sliding window thing. The x64 proposal linked uses six to pass arguments in and three to return results. So returning six integers means three in registers, three on the stack, with three registers that were free to use containing nothing in particular.


The LLVM calling conventions for x86 only allow returning 3 integer registers, 4 vector registers, and 2 x87 floating point registers (er, stack slots technically because x87 is weird).


Sure. That would be an instance of the "usual error". The argument registers are usually caller save, where any unused ones get treated as scratch in the callee, in which case making them all available for returning data as well is zero cost.

There's no reason not to, other than C makes returning multiple things awkward and splitting a struct across multiple registers is slightly annoying for the compiler.


Limiting a newly designed Rust ABI to whatever LLVM happens to support at the moment seems unnecessarily limiting. Yeah, you'd need to write some C++ to implement it, but that's not the end of the world, especially compared to getting stuck with arbitrary limits in your ABI for the next decade or two.


This sort of thing is why integer division by 0 is UB in rust on targets where it's not UB, because it's UB in LLVM :)


I stared at this really hard, and I eventually couldn't figure out what you mean here.

Obviously naively just dividing integers by zero in Rust will panic, because that's what is defined to happen.

So you have to be thinking about a specific case where it's defined not to panic. But, what case? There isn't an unchecked_div defined on the integers. The wrapping and saturating variants panic for division by zero, as do the various edge cases like div_floor

What case are you thinking of where "integer division by 0 is UB in Rust" ?


The poster is both correct and incorrect. It definitely is true that LLVM only has two instructions to deal with division, udiv and sdiv specifically, and it used to be the case that Rust as a consequence had UB when encountering division by zero as a result as those two instructions consider that operation UB.

But Rust has solved this problem by inserting a check before every division that reasonably could get a division by zero (might even be all operations, I don't know the specifics), which checks for zero and defines the consequences.

So as a result divisions aren't just divisions in Rust, they come with an additional check as overhead, but they aren't UB either.


Oh, I see, yes obviously if you know your value isn't zero, that's what the NonZero types are for, and these of course don't emit a check because it's unnecessary.


Sure, and if you actually want a branchless integer division for an arbitrary input, which is defined for the entire input domain on x64, then to get it you'll have to pull some trick like reinterpreting a zeroable type as a nonzero one, heading straight through LLVM IR UB on your way to the defined behavior on x64.


By the way: Don't actually do this. The LLVM IR is not defined to do what you wanted, and even if it works today, and it worked yesterday it might just stop working tomorrow, or on a different CPU model or with different optimisation settings.

If what you want is "Whatever happens when I execute this CPU instruction" you can literally write that in Rust today and that will do what you wanted. Invoking UB because you're sure you know better is how you end up with mysterious bugs.

This reminds me of people writing very crazy unsafe Rust to try to reproduce the "Quake fast inverse square root" even though um, you can just write that exact routine in safe Rust and it's guaranteed to do exactly what you meant with the IEEE re-interpretation as integer etc., safely and emitting essentially the same machine code on x86 - not even mentioning that's not how to calculate an inverse square root quickly today because Quake was a long time ago and your CPU is much better today than the ones Carmack wrote that code for.


I agree, one should put asm inside of the unsafe block instead. Because of the unfortunate fact about LLVM IR mentioned many comments above, the raw div in LLVM IR is allowed to be treated the same as if it was preceded with `if (divisor == 0) unreachable;`, which is a path to disaster.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: