Skip to content

Arm64: Use carry instructions for 128bit arithmetic #113070

Open
@a74nh

Description

@a74nh

Consider the addition operator for Int128:

public static Int128 operator +(Int128 left, Int128 right)

        /// <inheritdoc cref="IAdditionOperators{TSelf, TOther, TResult}.op_Addition(TSelf, TOther)" />
        public static Int128 operator +(Int128 left, Int128 right)
        {
            // For unsigned addition, we can detect overflow by checking `(x + y) < x`
            // This gives us the carry to add to upper to compute the correct result

            ulong lower = left._lower + right._lower;
            ulong carry = (lower < left._lower) ? 1UL : 0UL;

            ulong upper = left._upper + right._upper + carry;
            return new Int128(upper, lower);
        }

This compiles to:

ldp     x0, x1, [fp, #0x18]	// [V42 tmp41], [V43 tmp42]
ldp     x20, x21, [fp, #0x28]	// [V34 tmp33], [V35 tmp34]
add     x19, x20, x0
cmp     x19, x20
cset    x0, lo
mov     w0, w0
add     x1, x21, x1
add     x20, x1, x0
movz    x0, #0xA610
movk    x0, #0x75F3 LSL #16
movk    x0, #0xECE6 LSL #32
bl      CORINFO_HELP_NEWSFAST
stp     x19, x20, [x0, #0x08]

Instead add with carry should be used to remove the compare:

ldp     x0, x1, [fp, #0x18]	// [V42 tmp41], [V43 tmp42]
ldp     x20, x21, [fp, #0x28]	// [V34 tmp33], [V35 tmp34]
adds    x19, x20, x0
addc    x20, x21, x1
movz    x0, #0xA610
movk    x0, #0x75F3 LSL #16
movk    x0, #0xECE6 LSL #32
bl      CORINFO_HELP_NEWSFAST
stp     x19, x20, [x0, #0x08]

This should be expanded for uses of SUBC and NEGC.

This could either be done by matching a standard IR pattern or if it's too big/fragile, then via intrinsics. There is an initial discussion here: #68028 (comment)

Metadata

Metadata

Assignees

Labels

arch-arm64area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions