summaryrefslogtreecommitdiff
path: root/src/jit/emitarm64.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/emitarm64.cpp')
-rw-r--r--src/jit/emitarm64.cpp303
1 files changed, 289 insertions, 14 deletions
diff --git a/src/jit/emitarm64.cpp b/src/jit/emitarm64.cpp
index 0328cb6712..4097b662f0 100644
--- a/src/jit/emitarm64.cpp
+++ b/src/jit/emitarm64.cpp
@@ -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();
@@ -3858,6 +3878,26 @@ void emitter::emitIns_R_R(
fmt = IF_DV_2M;
break;
+ case INS_ldar:
+ case INS_stlr:
+ assert(isValidGeneralDatasize(size));
+
+ __fallthrough;
+
+ case INS_ldarb:
+ case INS_ldarh:
+ case INS_stlrb:
+ case INS_stlrh:
+ assert(isValidGeneralLSDatasize(size));
+ assert(isGeneralRegisterOrZR(reg1));
+ assert(isGeneralRegisterOrSP(reg2));
+ assert(insOptsNone(opt));
+
+ reg2 = encodingSPtoZR(reg2);
+
+ fmt = IF_LS_2A;
+ break;
+
case INS_ldr:
case INS_ldrb:
case INS_ldrh:
@@ -5072,7 +5112,8 @@ void emitter::emitIns_R_R_R_I(instruction ins,
regNumber reg2,
regNumber reg3,
ssize_t imm,
- insOpts opt /* = INS_OPTS_NONE */)
+ insOpts opt /* = INS_OPTS_NONE */,
+ emitAttr attrReg2 /* = EA_UNKNOWN */)
{
emitAttr size = EA_SIZE(attr);
emitAttr elemsize = EA_UNKNOWN;
@@ -5347,6 +5388,22 @@ void emitter::emitIns_R_R_R_I(instruction ins,
id->idReg2(reg2);
id->idReg3(reg3);
+ // Record the attribute for the second register in the pair
+ id->idGCrefReg2(GCT_NONE);
+ if (attrReg2 != EA_UNKNOWN)
+ {
+ // Record the attribute for the second register in the pair
+ assert((fmt == IF_LS_3B) || (fmt == IF_LS_3C));
+ if (EA_IS_GCREF(attrReg2))
+ {
+ id->idGCrefReg2(GCT_GCREF);
+ }
+ else if (EA_IS_BYREF(attrReg2))
+ {
+ id->idGCrefReg2(GCT_BYREF);
+ }
+ }
+
dispIns(id);
appendToCurIG(id);
}
@@ -6072,6 +6129,102 @@ void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber reg1, int va
/*****************************************************************************
*
+ * Add an instruction referencing two register and consectutive stack-based local variable slots.
+ */
+void emitter::emitIns_R_R_S_S(
+ instruction ins, emitAttr attr1, emitAttr attr2, regNumber reg1, regNumber reg2, int varx, int offs)
+{
+ assert((ins == INS_ldp) || (ins == INS_ldnp));
+ 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);
+
+ // Record the attribute for the second register in the pair
+ if (EA_IS_GCREF(attr2))
+ {
+ id->idGCrefReg2(GCT_GCREF);
+ }
+ else if (EA_IS_BYREF(attr2))
+ {
+ id->idGCrefReg2(GCT_BYREF);
+ }
+ else
+ {
+ id->idGCrefReg2(GCT_NONE);
+ }
+
+ 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 a stack-based local variable and a register
*/
void emitter::emitIns_S_R(instruction ins, emitAttr attr, regNumber reg1, int varx, int offs)
@@ -6202,6 +6355,102 @@ 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);
+
+ // Record the attribute for the second register in the pair
+ if (EA_IS_GCREF(attr2))
+ {
+ id->idGCrefReg2(GCT_GCREF);
+ }
+ else if (EA_IS_BYREF(attr2))
+ {
+ id->idGCrefReg2(GCT_BYREF);
+ }
+ else
+ {
+ id->idGCrefReg2(GCT_NONE);
+ }
+
+ 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)
@@ -9324,33 +9573,34 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
// for stores, but we ignore those cases here.)
if (emitInsMayWriteToGCReg(id)) // True if "id->idIns()" writes to a register than can hold GC ref.
{
- // If we ever generate instructions that write to multiple registers,
- // then we'd need to more work here to ensure that changes in the status of GC refs are
- // tracked properly.
- if (emitInsMayWriteMultipleRegs(id))
+ // We assume that "idReg1" is the primary destination register for all instructions
+ if (id->idGCref() != GCT_NONE)
{
- // INS_ldp etc...
- // We assume that "idReg1" and "idReg2" are the destination register for all instructions
- emitGCregDeadUpd(id->idReg1(), dst);
- emitGCregDeadUpd(id->idReg2(), dst);
+ emitGCregLiveUpd(id->idGCref(), id->idReg1(), dst);
}
else
{
- // We assume that "idReg1" is the destination register for all instructions
- if (id->idGCref() != GCT_NONE)
+ emitGCregDeadUpd(id->idReg1(), dst);
+ }
+
+ if (emitInsMayWriteMultipleRegs(id))
+ {
+ // INS_ldp etc...
+ // "idReg2" is the secondary destination register
+ if (id->idGCrefReg2() != GCT_NONE)
{
- emitGCregLiveUpd(id->idGCref(), id->idReg1(), dst);
+ emitGCregLiveUpd(id->idGCrefReg2(), id->idReg2(), dst);
}
else
{
- emitGCregDeadUpd(id->idReg1(), dst);
+ emitGCregDeadUpd(id->idReg2(), dst);
}
}
}
// 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));
@@ -9377,6 +9627,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