[RISC-V] Fix constant preparing for shuffle stub (#87708)
authort-mustafin <66252296+t-mustafin@users.noreply.github.com>
Fri, 23 Jun 2023 06:04:39 +0000 (09:04 +0300)
committerGitHub <noreply@github.com>
Fri, 23 Jun 2023 06:04:39 +0000 (23:04 -0700)
src/coreclr/vm/riscv64/cgencpu.h
src/coreclr/vm/riscv64/stubs.cpp

index 0280015..4f695de 100644 (file)
@@ -365,6 +365,8 @@ public:
 
     void EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value);
     void EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value);
+    void EmitSllImm(IntReg Xd, IntReg Xn, unsigned int value);
+    void EmitLuImm(IntReg Xd, unsigned int value);
 
     void EmitLoadStoreRegPairImm(DWORD flags, IntReg Xt1, IntReg Xt2, IntReg Xn, int offset=0);
     void EmitLoadStoreRegPairImm(DWORD flags, FloatReg Ft1, FloatReg Ft2, IntReg Xn, int offset=0);
index 1db7530..1d6fee6 100644 (file)
@@ -1009,55 +1009,76 @@ BOOL GetAnyThunkTarget (T_CONTEXT *pctx, TADDR *pTarget, TADDR *pTargetMethodDes
 // StubLinkerCPU methods
 // ----------------------------------------------------------------
 
-void StubLinkerCPU::EmitMovConstant(IntReg target, UINT64 constant)
+void StubLinkerCPU::EmitMovConstant(IntReg reg, UINT64 imm)
 {
-    if (0 == ((constant + 0x800) >> 32)) {
-        if (((constant + 0x800) >> 12) != 0)
-        {
-            Emit32((DWORD)(0x00000037 | (((constant + 0x800) >> 12) << 12) | (target << 7))); // lui target, (constant + 0x800) >> 12
-            if ((constant & 0xFFF) != 0)
-            {
-                Emit32((DWORD)(0x00000013 | (constant & 0xFFF) << 20 | (target << 7) | (target << 15))); // addi target, target, constant
-            }
-        }
-        else
-        {
-            Emit32((DWORD)(0x00000013 | (constant & 0xFFF) << 20 | (target << 7))); // addi target, x0, constant
-        }
+    // Adaptation of emitLoadImmediate
+
+    if (isValidSimm12(imm))
+    {
+        EmitAddImm(reg, 0 /* zero register */, imm & 0xFFF);
+        return;
+    }
+
+    // TODO-RISCV64: maybe optimized via emitDataConst(), check #86790
+
+    UINT32 msb;
+    UINT32 high31;
+
+    BitScanReverse64(&msb, imm);
+
+    if (msb > 30)
+    {
+        high31 = (imm >> (msb - 30)) & 0x7FffFFff;
     }
     else
     {
-        UINT32 upper = constant >> 32;
-        if (((upper + 0x800) >> 12) != 0)
-        {
-            Emit32((DWORD)(0x00000037 | (((upper + 0x800) >> 12) << 12) | (target << 7))); // lui target, (upper + 0x800) >> 12
-            if ((upper & 0xFFF) != 0)
-            {
-                Emit32((DWORD)(0x00000013 | (upper & 0xFFF) << 20 | (target << 7) | (target << 15))); // addi target, target, upper 
-            }
-        }
-        else
-        {
-            Emit32((DWORD)(0x00000013 | (upper & 0xFFF) << 20 | (target << 7))); // addi target, x0, upper 
-        }
-        UINT32 lower = (constant << 32) >> 32;
-        UINT32 shift = 0;
-        for (int i = 32; i >= 0; i -= 11)
-        {
-            shift += i > 11 ? 11 : i;
-            UINT32 current = lower >> (i < 11 ? 0 : i - 11);
-            if (current != 0)
-            {
-                Emit32((DWORD)(0x00001013 | (shift << 20) | (target << 7) | (target << 15))); // slli target, target, shift
-                Emit32((DWORD)(0x00000013 | (current & 0x7FF) << 20 | (target << 7) | (target << 15))); // addi target, target, current
-                shift = 0;
-            }
-        }
-        if (shift)
+        high31 = imm & 0x7FffFFff;
+    }
+
+    // Since ADDIW use sign extension for immediate
+    // we have to adjust higher 19 bit loaded by LUI
+    // for case when low part is bigger than 0x800.
+    UINT32 high19 = (high31 + 0x800) >> 12;
+
+    EmitLuImm(reg, high19);
+    if (high31 & 0x800)
+    {
+        // EmitAddImm does not allow negative immediate values, so use EmitSubImm.
+        EmitSubImm(reg, reg, ~high31 + 1 & 0xFFF);
+    }
+    else
+    {
+        EmitAddImm(reg, reg, high31 & 0x7FF);
+    }
+
+    // And load remaining part by batches of 11 bits size.
+    INT32 remainingShift = msb - 30;
+
+    // shiftAccumulator usage is an optimization allows to exclude `slli addi` iteration
+    // if immediate bits `low11` for this iteration are zero.
+    UINT32 shiftAccumulator = 0;
+
+    while (remainingShift > 0)
+    {
+        UINT32 shift = remainingShift >= 11 ? 11 : remainingShift % 11;
+        UINT32 mask = 0x7ff >> (11 - shift);
+        remainingShift -= shift;
+        UINT32 low11 = (imm >> remainingShift) & mask;
+        shiftAccumulator += shift;
+
+        if (low11)
         {
-            Emit32((DWORD)(0x00001013 | (shift << 20) | (target << 7) | (target << 15))); // slli target, target, shift
+            EmitSllImm(reg, reg, shiftAccumulator);
+            shiftAccumulator = 0;
+
+            EmitAddImm(reg, reg, low11);
         }
     }
+
+    if (shiftAccumulator)
+    {
+        EmitSllImm(reg, reg, shiftAccumulator);
+    }
 }
 
 void StubLinkerCPU::EmitCmpImm(IntReg reg, int imm)
@@ -1161,16 +1182,28 @@ void StubLinkerCPU::EmitMovReg(IntReg Xd, IntReg Xm)
 
 void StubLinkerCPU::EmitSubImm(IntReg Xd, IntReg Xn, unsigned int value)
 {
-    _ASSERTE((0 <= value) && (value <= 0x7FF));
+    _ASSERTE(value <= 0x800);
     Emit32((DWORD)(0x00000013 | (((~value + 0x1) & 0xFFF) << 20) | (Xn << 15) | (Xd << 7))); // addi Xd, Xn, (~value + 0x1) & 0xFFF
 }
 
 void StubLinkerCPU::EmitAddImm(IntReg Xd, IntReg Xn, unsigned int value)
 {
-    _ASSERTE((0 <= value) && (value <= 0x7FF));
+    _ASSERTE(value <= 0x7FF);
     Emit32((DWORD)(0x00000013 | (value << 20) | (Xn << 15) | (Xd << 7))); // addi Xd, Xn, value
 }
 
+void StubLinkerCPU::EmitSllImm(IntReg Xd, IntReg Xn, unsigned int value)
+{
+    _ASSERTE(value <= 0x3F);
+    Emit32((DWORD)(0x00001013 | (value << 20) | (Xn << 15) | (Xd << 7))); // sll Xd, Xn, value
+}
+
+void StubLinkerCPU::EmitLuImm(IntReg Xd, unsigned int value)
+{
+    _ASSERTE(value <= 0xFFFFF);
+    Emit32((DWORD)(0x00000037 | (value << 12) | (Xd << 7))); // lui Xd, value
+}
+
 void StubLinkerCPU::EmitCallRegister(IntReg reg)
 {
     _ASSERTE(!"RISCV64: not implementation on riscv64!!!");