Semantics of Generic MIR overflowing operations?

Hi,

I was looking at the overflowing operations because I was trying to deal with the legalization and selection of wide adds, and I wasn’t sure on the finer points of some of the semantics.
I’m specifically looking at the addition/subtraction operations:

G_UADDO, G_SADDO, G_USUBO, G_SSUBO
G_UADDE, G_SADDE, G_USUBE, G_SSUBE

Is it correct that the S* operations set the carry-out value when they have a signed overflow, and the U* operations when they have an unsigned overflow?
Particularly, is there any difference between them if the carry-out isn’t used by anything?
I would expect that a decomposed wide add would have a sequence like the following:

G_UADDO
G_UADDE

Would that just need to end with a G_SADDE when implementing an operation like Rust’s i128::checked_add()?

Finally, Is there a reason that G_SADDE and G_SSUBE aren’t legal in the AArch64 backend? (Particularly while G_SADDO and G_SSUBO are.)
They seem like they have the potential to produce bad code if used in the middle of a sequence like that, but I don’t know if that’s a reason to make them illegal so I was wondering if there was something else I was missing.

Thanks,
— Cassie

Hi,

I was looking at the overflowing operations because I was trying to deal with the legalization and selection of wide adds, and I wasn’t sure on the finer points of some of the semantics.
I’m specifically looking at the addition/subtraction operations:

G_UADDO, G_SADDO, G_USUBO, G_SSUBO
G_UADDE, G_SADDE, G_USUBE, G_SSUBE

Is it correct that the S* operations set the carry-out value when they have a signed overflow, and the U* operations when they have an unsigned overflow?

Yes, they’re directly mapped from the IR intrinsics: https://llvm.org/docs/LangRef.html#llvm-sadd-with-overflow-intrinsics

Particularly, is there any difference between them if the carry-out isn’t used by anything?

If by carry-out you mean the i1 result value, then yes I think the actual addition part is the same.

I would expect that a decomposed wide add would have a sequence like the following:

G_UADDO
G_UADDE

Would that just need to end with a G_SADDE when implementing an operation like Rust’s i128::checked_add()?

Not sure why there would be a mixing of unsigned/signed, but for splitting of wide addition, we use G_UADDO + G_UADDE in LegalizerHelper::narrowScalar() with the G_ADD case.

Finally, Is there a reason that G_SADDE and G_SSUBE aren’t legal in the AArch64 backend? (Particularly while G_SADDO and G_SSUBO are.)
They seem like they have the potential to produce bad code if used in the middle of a sequence like that, but I don’t know if that’s a reason to make them illegal so I was wondering if there was something else I was missing.

Anything illegal will just be due to the fact that we haven’t yet implemented support for it. Some operations pop up in IR for C++/ObjC more often than others, so those operations have higher priority when implementing.

Thanks,
Amara