[Arm64] Add emitIns_S_S_R_R
authorSteve MacLean, Qualcomm Datacenter Technologies, Inc <sdmaclea@qti.qualcomm.com>
Mon, 1 May 2017 17:44:19 +0000 (17:44 +0000)
committerSteve MacLean, Qualcomm Datacenter Technologies, Inc <sdmaclea@qti.qualcomm.com>
Mon, 8 May 2017 16:23:44 +0000 (16:23 +0000)
Commit migrated from https://github.com/dotnet/coreclr/commit/494f00cb881cc68426718b32a62464d4c7c48007

src/coreclr/src/jit/codegenarm64.cpp
src/coreclr/src/jit/codegenarmarch.cpp
src/coreclr/src/jit/emit.h
src/coreclr/src/jit/emitarm64.cpp
src/coreclr/src/jit/emitarm64.h

index 8c87bc1..0e536cd 100644 (file)
@@ -2638,9 +2638,7 @@ void CodeGen::genCodeForStorePairOffset(regNumber src, regNumber src2, GenTree*
         if (base->gtOper == GT_LCL_FLD_ADDR)
             offset += base->gtLclFld.gtLclOffs;
 
-        // TODO-ARM64-CQ: Implement support for using a stp instruction with a varNum (see emitIns_S_R)
-        emit->emitIns_S_R(INS_str, EA_8BYTE, src, base->gtLclVarCommon.gtLclNum, offset);
-        emit->emitIns_S_R(INS_str, EA_8BYTE, src2, base->gtLclVarCommon.gtLclNum, offset + REGSIZE_BYTES);
+        emit->emitIns_S_S_R_R(INS_stp, EA_8BYTE, EA_8BYTE, src, src2, base->gtLclVarCommon.gtLclNum, offset);
     }
     else
     {
index aea4327..61b9309 100644 (file)
@@ -735,10 +735,9 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
                                           INS_OPTS_NONE, emitTypeSize(type0));
                 }
 
-                // Emit two store instructions to store the two registers into the outgoing argument area
-                emit->emitIns_S_R(ins_Store(type0), emitTypeSize(type0), loReg, varNumOut, argOffsetOut);
-                emit->emitIns_S_R(ins_Store(type1), emitTypeSize(type1), hiReg, varNumOut,
-                                  argOffsetOut + TARGET_POINTER_SIZE);
+                // Emit stp instruction to store the two registers into the outgoing argument area
+                emit->emitIns_S_S_R_R(INS_stp, emitTypeSize(type0), emitTypeSize(type1), loReg, hiReg, varNumOut,
+                                      argOffsetOut);
                 argOffsetOut += (2 * TARGET_POINTER_SIZE); // We stored 16-bytes of the struct
                 assert(argOffsetOut <= argOffsetMax);      // We can't write beyound the outgoing area area
 
index a63242f..c09953f 100644 (file)
@@ -885,14 +885,16 @@ protected:
         void checkSizes();
 
         union idAddrUnion {
-            // TODO-Cleanup: We should really add a DEBUG-only tag to this union so we can add asserts
-            // about reading what we think is here, to avoid unexpected corruption issues.
+// TODO-Cleanup: We should really add a DEBUG-only tag to this union so we can add asserts
+// about reading what we think is here, to avoid unexpected corruption issues.
 
+#ifndef _TARGET_ARM64_
             emitLclVarAddr iiaLclVar;
-            BasicBlock*    iiaBBlabel;
-            insGroup*      iiaIGlabel;
-            BYTE*          iiaAddr;
-            emitAddrMode   iiaAddrMode;
+#endif
+            BasicBlock*  iiaBBlabel;
+            insGroup*    iiaIGlabel;
+            BYTE*        iiaAddr;
+            emitAddrMode iiaAddrMode;
 
             CORINFO_FIELD_HANDLE iiaFieldHnd; // iiaFieldHandle is also used to encode
                                               // an offset into the JIT data constant area
@@ -923,11 +925,12 @@ protected:
 
             struct
             {
-                regNumber _idReg3 : REGNUM_BITS;
-                regNumber _idReg4 : REGNUM_BITS;
 #ifdef _TARGET_ARM64_
-                unsigned _idReg3Scaled : 1; // Reg3 is scaled by idOpSize bits
+                emitLclVarAddr iiaLclVar;
+                unsigned       _idReg3Scaled : 1; // Reg3 is scaled by idOpSize bits
 #endif
+                regNumber _idReg3 : REGNUM_BITS;
+                regNumber _idReg4 : REGNUM_BITS;
             };
 #elif defined(_TARGET_XARCH_) && !defined(LEGACY_BACKEND)
             struct
@@ -2020,6 +2023,9 @@ public:
 
     // Returns true if the instruction may write to more than one register.
     bool emitInsMayWriteMultipleRegs(instrDesc* id);
+
+    // Returns "true" if instruction "id->idIns()" writes to a LclVar stack slot pair.
+    bool emitInsWritesToLclVarStackLocPair(instrDesc* id);
 #endif // _TARGET_ARMARCH_
 
     /************************************************************************/
index e2a1ae1..c979c24 100644 (file)
@@ -883,6 +883,26 @@ bool emitter::emitInsWritesToLclVarStackLoc(instrDesc* id)
     }
 }
 
+bool emitter::emitInsWritesToLclVarStackLocPair(instrDesc* id)
+{
+    if (!id->idIsLclVar())
+        return false;
+
+    instruction ins = id->idIns();
+
+    // This list is related to the list of instructions used to store local vars in emitIns_S_S_R_R().
+    // We don't accept writing to float local vars.
+
+    switch (ins)
+    {
+        case INS_stnp:
+        case INS_stp:
+            return true;
+        default:
+            return false;
+    }
+}
+
 bool emitter::emitInsMayWriteMultipleRegs(instrDesc* id)
 {
     instruction ins = id->idIns();
@@ -6220,6 +6240,101 @@ void emitter::emitIns_S_R(instruction ins, emitAttr attr, regNumber reg1, int va
 
 /*****************************************************************************
  *
+ *  Add an instruction referencing consecutive stack-based local variable slots and two registers
+ */
+void emitter::emitIns_S_S_R_R(
+    instruction ins, emitAttr attr1, emitAttr attr2, regNumber reg1, regNumber reg2, int varx, int offs)
+{
+    assert((ins == INS_stp) || (ins == INS_stnp));
+    assert(EA_8BYTE == EA_SIZE(attr1));
+    assert(EA_8BYTE == EA_SIZE(attr2));
+    assert(isGeneralRegisterOrZR(reg1));
+    assert(isGeneralRegisterOrZR(reg2));
+    assert(offs >= 0);
+
+    emitAttr       size  = EA_SIZE(attr1);
+    insFormat      fmt   = IF_LS_3B;
+    int            disp  = 0;
+    const unsigned scale = 3;
+
+    /* Figure out the variable's frame position */
+    int  base;
+    bool FPbased;
+
+    base = emitComp->lvaFrameAddress(varx, &FPbased);
+    disp = base + offs;
+
+    // TODO-ARM64-CQ: with compLocallocUsed, should we use REG_SAVED_LOCALLOC_SP instead?
+    regNumber reg3 = FPbased ? REG_FPBASE : REG_SPBASE;
+    reg3           = encodingSPtoZR(reg3);
+
+    bool    useRegForAdr = true;
+    ssize_t imm          = disp;
+    ssize_t mask         = (1 << scale) - 1; // the mask of low bits that must be zero to encode the immediate
+    if (imm == 0)
+    {
+        useRegForAdr = false;
+    }
+    else
+    {
+        if ((imm & mask) == 0)
+        {
+            ssize_t immShift = imm >> scale; // The immediate is scaled by the size of the ld/st
+
+            if ((immShift >= -64) && (immShift <= 63))
+            {
+                fmt          = IF_LS_3C;
+                useRegForAdr = false;
+                imm          = immShift;
+            }
+        }
+    }
+
+    if (useRegForAdr)
+    {
+        regNumber rsvd = codeGen->rsGetRsvdReg();
+        emitIns_R_R_Imm(INS_add, EA_8BYTE, rsvd, reg3, imm);
+        reg3 = rsvd;
+        imm  = 0;
+    }
+
+    assert(fmt != IF_NONE);
+
+    instrDesc* id = emitNewInstrCns(attr1, imm);
+
+    id->idIns(ins);
+    id->idInsFmt(fmt);
+    id->idInsOpt(INS_OPTS_NONE);
+
+    if (EA_IS_GCREF(attr2))
+    {
+        /* A special value indicates a GCref pointer value */
+
+        id->idGCrefReg2(GCT_GCREF);
+    }
+    else if (EA_IS_BYREF(attr2))
+    {
+        /* A special value indicates a Byref pointer value */
+
+        id->idGCrefReg2(GCT_BYREF);
+    }
+
+    id->idReg1(reg1);
+    id->idReg2(reg2);
+    id->idReg3(reg3);
+    id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs);
+    id->idSetIsLclVar();
+
+#ifdef DEBUG
+    id->idDebugOnlyInfo()->idVarRefOffs = emitVarRefOffs;
+#endif
+
+    dispIns(id);
+    appendToCurIG(id);
+}
+
+/*****************************************************************************
+ *
  *  Add an instruction referencing stack-based local variable and an immediate
  */
 void emitter::emitIns_S_I(instruction ins, emitAttr attr, int varx, int offs, int val)
@@ -9369,7 +9484,7 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
 
     // Now we determine if the instruction has written to a (local variable) stack location, and either written a GC
     // ref or overwritten one.
-    if (emitInsWritesToLclVarStackLoc(id))
+    if (emitInsWritesToLclVarStackLoc(id) || emitInsWritesToLclVarStackLocPair(id))
     {
         int      varNum = id->idAddr()->iiaLclVar.lvaVarNum();
         unsigned ofs    = AlignDown(id->idAddr()->iiaLclVar.lvaOffset(), sizeof(size_t));
@@ -9396,6 +9511,31 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
             if (vt == TYP_REF || vt == TYP_BYREF)
                 emitGCvarDeadUpd(adr + ofs, dst);
         }
+        if (emitInsWritesToLclVarStackLocPair(id))
+        {
+            unsigned ofs2 = ofs + sizeof(size_t);
+            if (id->idGCrefReg2() != GCT_NONE)
+            {
+                emitGCvarLiveUpd(adr + ofs2, varNum, id->idGCrefReg2(), dst);
+            }
+            else
+            {
+                // If the type of the local is a gc ref type, update the liveness.
+                var_types vt;
+                if (varNum >= 0)
+                {
+                    // "Regular" (non-spill-temp) local.
+                    vt = var_types(emitComp->lvaTable[varNum].lvType);
+                }
+                else
+                {
+                    TempDsc* tmpDsc = emitComp->tmpFindNum(varNum);
+                    vt              = tmpDsc->tdTempType();
+                }
+                if (vt == TYP_REF || vt == TYP_BYREF)
+                    emitGCvarDeadUpd(adr + ofs2, dst);
+            }
+        }
     }
 
 #ifdef DEBUG
index 8dcb328..87caa22 100644 (file)
@@ -758,6 +758,9 @@ void emitIns_S(instruction ins, emitAttr attr, int varx, int offs);
 
 void emitIns_S_R(instruction ins, emitAttr attr, regNumber ireg, int varx, int offs);
 
+void emitIns_S_S_R_R(
+    instruction ins, emitAttr attr, emitAttr attr2, regNumber ireg, regNumber ireg2, int varx, int offs);
+
 void emitIns_R_S(instruction ins, emitAttr attr, regNumber ireg, int varx, int offs);
 
 void emitIns_S_I(instruction ins, emitAttr attr, int varx, int offs, int val);