Skip to content

Arm64: ands & cmp shifted register instructions are not generated when a shift value greater than 63 is used #111889

Open
@jonathandavies-arm

Description

@jonathandavies-arm

These 2 instructions were said to be completed in #68028 but I writing tests for them and found the following issue. See #111796 for the full tests.

Compare these 2 c# functions taken from the tests in the PR above. Both of them take 2 numbers shift 1 of them and compare it to the other or the negative in the CmnLargeShift64Bit case. If they are equal return true else return false.

static bool CmnLargeShift64Bit(long a, long b)
{
    if (a == -(b<<247))
    {
        return true;
    }
    return false;
}
static bool CmpLargeShift64Bit(long a, long b)
{
    if (a == (b<<119))
    {
        return true;
    }
    return false;
}

The disassembly of both functions respectively.

cmn     x0, x1,  LSL #55
cset    x0, eq
lsl     x1, x1, #55
cmp     x0, x1
cset    x0, eq

Section of nodes just before lowering for each function respectively.

N003 (  3,  4) [000003] -----+-----                    t3 = *  LSH       long   $140
                                                            /--*  t3     long   
N004 (  4,  5) [000004] -----+-----                    t4 = *  NEG       long   $82
N005 (  1,  1) [000000] -----+-----                    t0 =    LCL_VAR   long   V00 arg0         u:1 (last use) $80
                                                            /--*  t4     long   
                                                            +--*  t0     long   
N006 (  6,  7) [000005] N----+-N-U-                    t5 = *  NE        int    $180
N004 (  3,  4) [000003] -----+-----                    t3 = *  LSH       long   $140
                                                            /--*  t0     long   
                                                            +--*  t3     long   
N005 (  5,  6) [000004] N----+-N-U-                    t4 = *  NE        int    $180

After the lowering phase the LSH in CmnLargeShift64Bit has been contained and will produce the shifted register version of cmn.
In CmpLargeShift64Bit the LSH is not contained.

In lowerarmarch.cpp function IsContainableUnaryOrBinaryOp line 272 we have this check

const ssize_t shiftAmount = shiftAmountNode->AsIntCon()->IconValue();
const ssize_t maxShift    = (static_cast<ssize_t>(genTypeSize(parentNode)) * BITS_PER_BYTE) - 1;

if ((shiftAmount < 0x01) || (shiftAmount > maxShift))
{
    // Cannot contain if the shift amount is less than 1 or greater than maxShift
    return false;
}

Which says if the shift amount is greater than the max shift return false. When checking the LSH Cmp the parent node is the NE whereas Cmn's parent node is NEG. The type size of NE node is 4 bytes and the NEG node is 8 bytes which gets a maxShift of 31 & 63 for NE & NEG.
In the Cmp case the shift amount of 55 is > 31 and it returns false.
In the Cmn case the shift amount of 55 is not > 63 and it continues the function and further down returns true to say it is containable.

The same thing happens for ands.

Metadata

Metadata

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