summaryrefslogtreecommitdiff
path: root/src/jit/codegenarm64.cpp
diff options
context:
space:
mode:
authorJiyoung Yun <jy910.yun@samsung.com>2017-04-13 14:17:19 +0900
committerJiyoung Yun <jy910.yun@samsung.com>2017-04-13 14:17:19 +0900
commita56e30c8d33048216567753d9d3fefc2152af8ac (patch)
tree7e5d979695fc4a431740982eb1cfecc2898b23a5 /src/jit/codegenarm64.cpp
parent4b11dc566a5bbfa1378d6266525c281b028abcc8 (diff)
downloadcoreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.gz
coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.bz2
coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.zip
Imported Upstream version 2.0.0.11353upstream/2.0.0.11353
Diffstat (limited to 'src/jit/codegenarm64.cpp')
-rw-r--r--src/jit/codegenarm64.cpp1824
1 files changed, 158 insertions, 1666 deletions
diff --git a/src/jit/codegenarm64.cpp b/src/jit/codegenarm64.cpp
index 71c6dd1162..7f98221df8 100644
--- a/src/jit/codegenarm64.cpp
+++ b/src/jit/codegenarm64.cpp
@@ -1265,67 +1265,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
*/
-/*****************************************************************************
- *
- * Generate code that will set the given register to the integer constant.
- */
-
-void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFlags flags)
-{
- // Reg cannot be a FP reg
- assert(!genIsValidFloatReg(reg));
-
- // The only TYP_REF constant that can come this path is a managed 'null' since it is not
- // relocatable. Other ref type constants (e.g. string objects) go through a different
- // code path.
- noway_assert(type != TYP_REF || val == 0);
-
- instGen_Set_Reg_To_Imm(emitActualTypeSize(type), reg, val, flags);
-}
-
-/*****************************************************************************
- *
- * Generate code to check that the GS cookie wasn't thrashed by a buffer
- * overrun. On ARM64 we always use REG_TMP_0 and REG_TMP_1 as temp registers
- * and this works fine in the case of tail calls
- * Implementation Note: pushReg = true, in case of tail calls.
- */
-void CodeGen::genEmitGSCookieCheck(bool pushReg)
-{
- noway_assert(compiler->gsGlobalSecurityCookieAddr || compiler->gsGlobalSecurityCookieVal);
-
- // Make sure that the return register is reported as live GC-ref so that any GC that kicks in while
- // executing GS cookie check will not collect the object pointed to by REG_INTRET (R0).
- if (!pushReg && (compiler->info.compRetType == TYP_REF))
- gcInfo.gcRegGCrefSetCur |= RBM_INTRET;
-
- regNumber regGSConst = REG_TMP_0;
- regNumber regGSValue = REG_TMP_1;
-
- if (compiler->gsGlobalSecurityCookieAddr == nullptr)
- {
- // load the GS cookie constant into a reg
- //
- genSetRegToIcon(regGSConst, compiler->gsGlobalSecurityCookieVal, TYP_I_IMPL);
- }
- else
- {
- // Ngen case - GS cookie constant needs to be accessed through an indirection.
- instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, regGSConst, (ssize_t)compiler->gsGlobalSecurityCookieAddr);
- getEmitter()->emitIns_R_R_I(ins_Load(TYP_I_IMPL), EA_PTRSIZE, regGSConst, regGSConst, 0);
- }
- // Load this method's GS value from the stack frame
- getEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, regGSValue, compiler->lvaGSSecurityCookie, 0);
- // Compare with the GC cookie constant
- getEmitter()->emitIns_R_R(INS_cmp, EA_PTRSIZE, regGSConst, regGSValue);
-
- BasicBlock* gsCheckBlk = genCreateTempLabel();
- emitJumpKind jmpEqual = genJumpKindForOper(GT_EQ, CK_SIGNED);
- inst_JMP(jmpEqual, gsCheckBlk);
- genEmitHelperCall(CORINFO_HELP_FAIL_FAST, 0, EA_UNKNOWN);
- genDefineTempLabel(gsCheckBlk);
-}
-
BasicBlock* CodeGen::genCallFinally(BasicBlock* block)
{
// Generate a call to the finally, like this:
@@ -1532,14 +1471,14 @@ void CodeGen::genCodeForMulHi(GenTreeOp* treeNode)
var_types targetType = treeNode->TypeGet();
emitter *emit = getEmitter();
emitAttr size = emitTypeSize(treeNode);
- GenTree *op1 = treeNode->gtOp.gtOp1;
- GenTree *op2 = treeNode->gtOp.gtOp2;
+ GenTree *op1 = treeNode->gtOp1;
+ GenTree *op2 = treeNode->gtOp2;
// to get the high bits of the multiply, we are constrained to using the
// 1-op form: RDX:RAX = RAX * rm
// The 3-op form (Rx=Ry*Rz) does not support it.
- genConsumeOperands(treeNode->AsOp());
+ genConsumeOperands(treeNode);
GenTree* regOp = op1;
GenTree* rmOp = op2;
@@ -1883,8 +1822,8 @@ void CodeGen::genReturn(GenTreePtr treeNode)
if (movRequired)
{
- emitAttr movSize = EA_ATTR(genTypeSize(targetType));
- getEmitter()->emitIns_R_R(INS_mov, movSize, retReg, op1->gtRegNum);
+ emitAttr attr = emitTypeSize(targetType);
+ getEmitter()->emitIns_R_R(INS_mov, attr, retReg, op1->gtRegNum);
}
}
@@ -1923,8 +1862,7 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
if (compiler->verbose)
{
unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
- printf("Generating: ");
- compiler->gtDispTree(treeNode, nullptr, nullptr, true);
+ compiler->gtDispLIRNode(treeNode, "Generating: ");
}
#endif // DEBUG
@@ -2721,7 +2659,7 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
break;
case GT_CALL:
- genCallInstruction(treeNode);
+ genCallInstruction(treeNode->AsCall());
break;
case GT_JMP:
@@ -2905,77 +2843,6 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
}
}
-//----------------------------------------------------------------------------------
-// genMultiRegCallStoreToLocal: store multi-reg return value of a call node to a local
-//
-// Arguments:
-// treeNode - Gentree of GT_STORE_LCL_VAR
-//
-// Return Value:
-// None
-//
-// Assumption:
-// The child of store is a multi-reg call node.
-// genProduceReg() on treeNode is made by caller of this routine.
-//
-void CodeGen::genMultiRegCallStoreToLocal(GenTreePtr treeNode)
-{
- assert(treeNode->OperGet() == GT_STORE_LCL_VAR);
-
- // Structs of size >=9 and <=16 are returned in two return registers on ARM64 and HFAs.
- assert(varTypeIsStruct(treeNode));
-
- // Assumption: current ARM64 implementation requires that a multi-reg struct
- // var in 'var = call' is flagged as lvIsMultiRegRet to prevent it from
- // being struct promoted.
- unsigned lclNum = treeNode->AsLclVarCommon()->gtLclNum;
- LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]);
- noway_assert(varDsc->lvIsMultiRegRet);
-
- GenTree* op1 = treeNode->gtGetOp1();
- GenTree* actualOp1 = op1->gtSkipReloadOrCopy();
- GenTreeCall* call = actualOp1->AsCall();
- assert(call->HasMultiRegRetVal());
-
- genConsumeRegs(op1);
-
- ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc();
- unsigned regCount = pRetTypeDesc->GetReturnRegCount();
-
- if (treeNode->gtRegNum != REG_NA)
- {
- // Right now the only enregistrable structs supported are SIMD types.
- assert(varTypeIsSIMD(treeNode));
- NYI("GT_STORE_LCL_VAR of a SIMD enregisterable struct");
- }
- else
- {
- // Stack store
- int offset = 0;
- for (unsigned i = 0; i < regCount; ++i)
- {
- var_types type = pRetTypeDesc->GetReturnRegType(i);
- regNumber reg = call->GetRegNumByIdx(i);
- if (op1->IsCopyOrReload())
- {
- // GT_COPY/GT_RELOAD will have valid reg for those positions
- // that need to be copied or reloaded.
- regNumber reloadReg = op1->AsCopyOrReload()->GetRegNumByIdx(i);
- if (reloadReg != REG_NA)
- {
- reg = reloadReg;
- }
- }
-
- assert(reg != REG_NA);
- getEmitter()->emitIns_S_R(ins_Store(type), emitTypeSize(type), reg, lclNum, offset);
- offset += genTypeSize(type);
- }
-
- varDsc->lvRegNum = REG_STK;
- }
-}
-
/***********************************************************************************************
* Generate code for localloc
*/
@@ -3331,7 +3198,6 @@ BAILOUT:
// b) The size of the struct to initialize is smaller than INITBLK_UNROLL_LIMIT bytes.
void CodeGen::genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode)
{
-#if 0
// Make sure we got the arguments of the initblk/initobj operation in the right registers
unsigned size = initBlkNode->Size();
GenTreePtr dstAddr = initBlkNode->Addr();
@@ -3341,57 +3207,57 @@ void CodeGen::genCodeForInitBlkUnroll(GenTreeBlk* initBlkNode)
initVal = initVal->gtGetOp1();
}
- assert(!dstAddr->isContained());
- assert(!initVal->isContained());
+ assert(dstAddr->isUsedFromReg());
+ assert(initVal->isUsedFromReg() && !initVal->IsIntegralConst(0) || initVal->IsIntegralConst(0));
assert(size != 0);
assert(size <= INITBLK_UNROLL_LIMIT);
- assert(initVal->gtSkipReloadOrCopy()->IsCnsIntOrI());
- emitter *emit = getEmitter();
+ emitter* emit = getEmitter();
genConsumeOperands(initBlkNode);
- // If the initVal was moved, or spilled and reloaded to a different register,
- // get the original initVal from below the GT_RELOAD, but only after capturing the valReg,
- // which needs to be the new register.
- regNumber valReg = initVal->gtRegNum;
- initVal = initVal->gtSkipReloadOrCopy();
-#else // !0
- NYI("genCodeForInitBlkUnroll");
-#endif // !0
-}
+ regNumber valReg = initVal->IsIntegralConst(0) ? REG_ZR : initVal->gtRegNum;
-// Generates code for InitBlk by calling the VM memset helper function.
-// Preconditions:
-// a) The size argument of the InitBlk is not an integer constant.
-// b) The size argument of the InitBlk is >= INITBLK_STOS_LIMIT bytes.
-void CodeGen::genCodeForInitBlk(GenTreeBlk* initBlkNode)
-{
- // Make sure we got the arguments of the initblk operation in the right registers
- unsigned size = initBlkNode->Size();
- GenTreePtr dstAddr = initBlkNode->Addr();
- GenTreePtr initVal = initBlkNode->Data();
- if (initVal->OperIsInitVal())
- {
- initVal = initVal->gtGetOp1();
- }
+ assert(!initVal->IsIntegralConst(0) || (valReg == REG_ZR));
- assert(!dstAddr->isContained());
- assert(!initVal->isContained());
- assert(initBlkNode->gtRsvdRegs == RBM_ARG_2);
+ unsigned offset = 0;
-// TODO-ARM64-CQ: When initblk loop unrolling is implemented
-// put this assert back on.
-#if 0
- if (size != 0)
+ // Perform an unroll using stp.
+ if (size >= 2 * REGSIZE_BYTES)
{
- assert(blockSize >= INITBLK_UNROLL_LIMIT);
- }
-#endif // 0
+ // Determine how many 16 byte slots
+ size_t slots = size / (2 * REGSIZE_BYTES);
- genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
+ while (slots-- > 0)
+ {
+ emit->emitIns_R_R_R_I(INS_stp, EA_8BYTE, valReg, valReg, dstAddr->gtRegNum, offset);
+ offset += (2 * REGSIZE_BYTES);
+ }
+ }
- genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN);
+ // Fill the remainder (15 bytes or less) if there's any.
+ if ((size & 0xf) != 0)
+ {
+ if ((size & 8) != 0)
+ {
+ emit->emitIns_R_R_I(INS_str, EA_8BYTE, valReg, dstAddr->gtRegNum, offset);
+ offset += 8;
+ }
+ if ((size & 4) != 0)
+ {
+ emit->emitIns_R_R_I(INS_str, EA_4BYTE, valReg, dstAddr->gtRegNum, offset);
+ offset += 4;
+ }
+ if ((size & 2) != 0)
+ {
+ emit->emitIns_R_R_I(INS_strh, EA_2BYTE, valReg, dstAddr->gtRegNum, offset);
+ offset += 2;
+ }
+ if ((size & 1) != 0)
+ {
+ emit->emitIns_R_R_I(INS_strb, EA_1BYTE, valReg, dstAddr->gtRegNum, offset);
+ }
+ }
}
// Generate code for a load from some address + offset
@@ -3413,13 +3279,34 @@ void CodeGen::genCodeForLoadOffset(instruction ins, emitAttr size, regNumber dst
}
}
+// Generate code for a load pair from some address + offset
+// base: tree node which can be either a local address or arbitrary node
+// offset: distance from the base from which to load
+void CodeGen::genCodeForLoadPairOffset(regNumber dst, regNumber dst2, GenTree* base, unsigned offset)
+{
+ emitter* emit = getEmitter();
+
+ if (base->OperIsLocalAddr())
+ {
+ if (base->gtOper == GT_LCL_FLD_ADDR)
+ offset += base->gtLclFld.gtLclOffs;
+
+ // TODO-ARM64-CQ: Implement support for using a ldp instruction with a varNum (see emitIns_R_S)
+ emit->emitIns_R_S(INS_ldr, EA_8BYTE, dst, base->gtLclVarCommon.gtLclNum, offset);
+ emit->emitIns_R_S(INS_ldr, EA_8BYTE, dst2, base->gtLclVarCommon.gtLclNum, offset + REGSIZE_BYTES);
+ }
+ else
+ {
+ emit->emitIns_R_R_R_I(INS_ldp, EA_8BYTE, dst, dst2, base->gtRegNum, offset);
+ }
+}
+
// Generate code for a store to some address + offset
// base: tree node which can be either a local address or arbitrary node
// offset: distance from the base from which to load
void CodeGen::genCodeForStoreOffset(instruction ins, emitAttr size, regNumber src, GenTree* base, unsigned offset)
{
-#if 0
- emitter *emit = getEmitter();
+ emitter* emit = getEmitter();
if (base->OperIsLocalAddr())
{
@@ -3429,11 +3316,30 @@ void CodeGen::genCodeForStoreOffset(instruction ins, emitAttr size, regNumber sr
}
else
{
- emit->emitIns_AR_R(ins, size, src, base->gtRegNum, offset);
+ emit->emitIns_R_R_I(ins, size, src, base->gtRegNum, offset);
+ }
+}
+
+// Generate code for a store pair to some address + offset
+// base: tree node which can be either a local address or arbitrary node
+// offset: distance from the base from which to load
+void CodeGen::genCodeForStorePairOffset(regNumber src, regNumber src2, GenTree* base, unsigned offset)
+{
+ emitter* emit = getEmitter();
+
+ if (base->OperIsLocalAddr())
+ {
+ 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);
+ }
+ else
+ {
+ emit->emitIns_R_R_R_I(INS_stp, EA_8BYTE, src, src2, base->gtRegNum, offset);
}
-#else // !0
- NYI("genCodeForStoreOffset");
-#endif // !0
}
// Generates CpBlk code by performing a loop unroll
@@ -3442,80 +3348,96 @@ void CodeGen::genCodeForStoreOffset(instruction ins, emitAttr size, regNumber sr
// This may seem small but covers >95% of the cases in several framework assemblies.
void CodeGen::genCodeForCpBlkUnroll(GenTreeBlk* cpBlkNode)
{
-#if 0
// Make sure we got the arguments of the cpblk operation in the right registers
unsigned size = cpBlkNode->Size();
GenTreePtr dstAddr = cpBlkNode->Addr();
GenTreePtr source = cpBlkNode->Data();
- noway_assert(source->gtOper == GT_IND);
- GenTreePtr srcAddr = source->gtGetOp1();
+ GenTreePtr srcAddr = nullptr;
- assert((size != 0 ) && (size <= CPBLK_UNROLL_LIMIT));
+ assert((size != 0) && (size <= CPBLK_UNROLL_LIMIT));
- emitter *emit = getEmitter();
+ emitter* emit = getEmitter();
- if (!srcAddr->isContained())
- genConsumeReg(srcAddr);
+ if (source->gtOper == GT_IND)
+ {
+ srcAddr = source->gtGetOp1();
+ if (srcAddr->isUsedFromReg())
+ {
+ genConsumeReg(srcAddr);
+ }
+ }
+ else
+ {
+ noway_assert(source->IsLocal());
+ // TODO-Cleanup: Consider making the addrForm() method in Rationalize public, e.g. in GenTree.
+ // OR: transform source to GT_IND(GT_LCL_VAR_ADDR)
+ if (source->OperGet() == GT_LCL_VAR)
+ {
+ source->SetOper(GT_LCL_VAR_ADDR);
+ }
+ else
+ {
+ assert(source->OperGet() == GT_LCL_FLD);
+ source->SetOper(GT_LCL_FLD_ADDR);
+ }
+ srcAddr = source;
+ }
- if (!dstAddr->isContained())
+ if (dstAddr->isUsedFromReg())
+ {
genConsumeReg(dstAddr);
+ }
unsigned offset = 0;
- // If the size of this struct is larger than 16 bytes
- // let's use SSE2 to be able to do 16 byte at a time
- // loads and stores.
- if (size >= XMM_REGSIZE_BYTES)
+ // Grab the integer temp register to emit the loads and stores.
+ regMaskTP tmpMask = genFindLowestBit(cpBlkNode->gtRsvdRegs & RBM_ALLINT);
+ regNumber tmpReg = genRegNumFromMask(tmpMask);
+
+ if (size >= 2 * REGSIZE_BYTES)
{
- assert(cpBlkNode->gtRsvdRegs != RBM_NONE);
- assert(genCountBits(cpBlkNode->gtRsvdRegs) == 1);
- regNumber xmmReg = genRegNumFromMask(cpBlkNode->gtRsvdRegs);
- assert(genIsValidFloatReg(xmmReg));
- size_t slots = size / XMM_REGSIZE_BYTES;
+ regMaskTP tmp2Mask = cpBlkNode->gtRsvdRegs & RBM_ALLINT & ~tmpMask;
+ regNumber tmp2Reg = genRegNumFromMask(tmp2Mask);
+
+ size_t slots = size / (2 * REGSIZE_BYTES);
while (slots-- > 0)
{
// Load
- genCodeForLoadOffset(INS_movdqu, EA_8BYTE, xmmReg, srcAddr, offset);
+ genCodeForLoadPairOffset(tmpReg, tmp2Reg, srcAddr, offset);
// Store
- genCodeForStoreOffset(INS_movdqu, EA_8BYTE, xmmReg, dstAddr, offset);
- offset += XMM_REGSIZE_BYTES;
+ genCodeForStorePairOffset(tmpReg, tmp2Reg, dstAddr, offset);
+ offset += 2 * REGSIZE_BYTES;
}
}
// Fill the remainder (15 bytes or less) if there's one.
if ((size & 0xf) != 0)
{
- // Grab the integer temp register to emit the remaining loads and stores.
- regNumber tmpReg = genRegNumFromMask(cpBlkNode->gtRsvdRegs & RBM_ALLINT);
-
if ((size & 8) != 0)
{
- genCodeForLoadOffset(INS_mov, EA_8BYTE, tmpReg, srcAddr, offset);
- genCodeForStoreOffset(INS_mov, EA_8BYTE, tmpReg, dstAddr, offset);
+ genCodeForLoadOffset(INS_ldr, EA_8BYTE, tmpReg, srcAddr, offset);
+ genCodeForStoreOffset(INS_str, EA_8BYTE, tmpReg, dstAddr, offset);
offset += 8;
}
if ((size & 4) != 0)
{
- genCodeForLoadOffset(INS_mov, EA_4BYTE, tmpReg, srcAddr, offset);
- genCodeForStoreOffset(INS_mov, EA_4BYTE, tmpReg, dstAddr, offset);
+ genCodeForLoadOffset(INS_ldr, EA_4BYTE, tmpReg, srcAddr, offset);
+ genCodeForStoreOffset(INS_str, EA_4BYTE, tmpReg, dstAddr, offset);
offset += 4;
}
if ((size & 2) != 0)
{
- genCodeForLoadOffset(INS_mov, EA_2BYTE, tmpReg, srcAddr, offset);
- genCodeForStoreOffset(INS_mov, EA_2BYTE, tmpReg, dstAddr, offset);
+ genCodeForLoadOffset(INS_ldrh, EA_2BYTE, tmpReg, srcAddr, offset);
+ genCodeForStoreOffset(INS_strh, EA_2BYTE, tmpReg, dstAddr, offset);
offset += 2;
}
if ((size & 1) != 0)
{
- genCodeForLoadOffset(INS_mov, EA_1BYTE, tmpReg, srcAddr, offset);
- genCodeForStoreOffset(INS_mov, EA_1BYTE, tmpReg, dstAddr, offset);
+ genCodeForLoadOffset(INS_ldrb, EA_1BYTE, tmpReg, srcAddr, offset);
+ genCodeForStoreOffset(INS_strb, EA_1BYTE, tmpReg, dstAddr, offset);
}
}
-#else // !0
- NYI("genCodeForCpBlkUnroll");
-#endif // !0
}
// Generate code for CpObj nodes wich copy structs that have interleaved
@@ -3587,22 +3509,28 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)
unsigned slots = cpObjNode->gtSlots;
emitter* emit = getEmitter();
+ BYTE* gcPtrs = cpObjNode->gtGcPtrs;
+
// If we can prove it's on the stack we don't need to use the write barrier.
if (dstOnStack)
{
// TODO-ARM64-CQ: Consider using LDP/STP to save codesize.
- while (slots > 0)
+ for (unsigned i = 0; i < slots; ++i)
{
- emit->emitIns_R_R_I(INS_ldr, EA_8BYTE, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
+ emitAttr attr = EA_8BYTE;
+ if (gcPtrs[i] == GCT_GCREF)
+ attr = EA_GCREF;
+ else if (gcPtrs[i] == GCT_BYREF)
+ attr = EA_BYREF;
+
+ emit->emitIns_R_R_I(INS_ldr, attr, tmpReg, REG_WRITE_BARRIER_SRC_BYREF, TARGET_POINTER_SIZE,
INS_OPTS_POST_INDEX);
- emit->emitIns_R_R_I(INS_str, EA_8BYTE, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE,
+ emit->emitIns_R_R_I(INS_str, attr, tmpReg, REG_WRITE_BARRIER_DST_BYREF, TARGET_POINTER_SIZE,
INS_OPTS_POST_INDEX);
- slots--;
}
}
else
{
- BYTE* gcPtrs = cpObjNode->gtGcPtrs;
unsigned gcPtrCount = cpObjNode->gtGcPtrCount;
unsigned i = 0;
@@ -3619,8 +3547,9 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)
break;
default:
- // We have a GC pointer, call the memory barrier.
+ // In the case of a GC-Pointer we'll call the ByRef write barrier helper
genEmitHelperCall(CORINFO_HELP_ASSIGN_BYREF, 0, EA_PTRSIZE);
+
gcPtrCount--;
break;
}
@@ -3635,30 +3564,6 @@ void CodeGen::genCodeForCpObj(GenTreeObj* cpObjNode)
gcInfo.gcMarkRegSetNpt(RBM_WRITE_BARRIER_SRC_BYREF | RBM_WRITE_BARRIER_DST_BYREF);
}
-// Generate code for a CpBlk node by the means of the VM memcpy helper call
-// Preconditions:
-// a) The size argument of the CpBlk is not an integer constant
-// b) The size argument is a constant but is larger than CPBLK_MOVS_LIMIT bytes.
-void CodeGen::genCodeForCpBlk(GenTreeBlk* cpBlkNode)
-{
- // Make sure we got the arguments of the cpblk operation in the right registers
- unsigned blockSize = cpBlkNode->Size();
- GenTreePtr dstAddr = cpBlkNode->Addr();
- assert(!dstAddr->isContained());
-
- genConsumeBlockOp(cpBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
-
-#if 0
- // Enable this when we support cpblk loop unrolling.
- if (blockSize != 0)
- {
- assert(blockSize->gtIntCon.gtIconVal >= CPBLK_UNROLL_LIMIT);
- }
-#endif // 0
-
- genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN);
-}
-
// generate code do a switch statement based on a table of ip-relative offsets
void CodeGen::genTableBasedSwitch(GenTree* treeNode)
{
@@ -3779,239 +3684,6 @@ void CodeGen::genLockedInstructions(GenTreeOp* treeNode)
#endif // !0
}
-// generate code for BoundsCheck nodes
-void CodeGen::genRangeCheck(GenTreePtr oper)
-{
-#ifdef FEATURE_SIMD
- noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK || oper->OperGet() == GT_SIMD_CHK);
-#else // !FEATURE_SIMD
- noway_assert(oper->OperGet() == GT_ARR_BOUNDS_CHECK);
-#endif // !FEATURE_SIMD
-
- GenTreeBoundsChk* bndsChk = oper->AsBoundsChk();
-
- GenTreePtr arrLen = bndsChk->gtArrLen;
- GenTreePtr arrIndex = bndsChk->gtIndex;
- GenTreePtr arrRef = NULL;
- int lenOffset = 0;
-
- GenTree * src1, *src2;
- emitJumpKind jmpKind;
-
- genConsumeRegs(arrIndex);
- genConsumeRegs(arrLen);
-
- if (arrIndex->isContainedIntOrIImmed())
- {
- // To encode using a cmp immediate, we place the
- // constant operand in the second position
- src1 = arrLen;
- src2 = arrIndex;
- jmpKind = genJumpKindForOper(GT_LE, CK_UNSIGNED);
- }
- else
- {
- src1 = arrIndex;
- src2 = arrLen;
- jmpKind = genJumpKindForOper(GT_GE, CK_UNSIGNED);
- }
-
- GenTreeIntConCommon* intConst = nullptr;
- if (src2->isContainedIntOrIImmed())
- {
- intConst = src2->AsIntConCommon();
- }
-
- if (intConst != nullptr)
- {
- getEmitter()->emitIns_R_I(INS_cmp, EA_4BYTE, src1->gtRegNum, intConst->IconValue());
- }
- else
- {
- getEmitter()->emitIns_R_R(INS_cmp, EA_4BYTE, src1->gtRegNum, src2->gtRegNum);
- }
-
- genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
-}
-
-//------------------------------------------------------------------------
-// genOffsetOfMDArrayLowerBound: Returns the offset from the Array object to the
-// lower bound for the given dimension.
-//
-// Arguments:
-// elemType - the element type of the array
-// rank - the rank of the array
-// dimension - the dimension for which the lower bound offset will be returned.
-//
-// Return Value:
-// The offset.
-// TODO-Cleanup: move to CodeGenCommon.cpp
-
-// static
-unsigned CodeGen::genOffsetOfMDArrayLowerBound(var_types elemType, unsigned rank, unsigned dimension)
-{
- // Note that the lower bound and length fields of the Array object are always TYP_INT, even on 64-bit targets.
- return compiler->eeGetArrayDataOffset(elemType) + genTypeSize(TYP_INT) * (dimension + rank);
-}
-
-//------------------------------------------------------------------------
-// genOffsetOfMDArrayLength: Returns the offset from the Array object to the
-// size for the given dimension.
-//
-// Arguments:
-// elemType - the element type of the array
-// rank - the rank of the array
-// dimension - the dimension for which the lower bound offset will be returned.
-//
-// Return Value:
-// The offset.
-// TODO-Cleanup: move to CodeGenCommon.cpp
-
-// static
-unsigned CodeGen::genOffsetOfMDArrayDimensionSize(var_types elemType, unsigned rank, unsigned dimension)
-{
- // Note that the lower bound and length fields of the Array object are always TYP_INT, even on 64-bit targets.
- return compiler->eeGetArrayDataOffset(elemType) + genTypeSize(TYP_INT) * dimension;
-}
-
-//------------------------------------------------------------------------
-// genCodeForArrIndex: Generates code to bounds check the index for one dimension of an array reference,
-// producing the effective index by subtracting the lower bound.
-//
-// Arguments:
-// arrIndex - the node for which we're generating code
-//
-// Return Value:
-// None.
-//
-
-void CodeGen::genCodeForArrIndex(GenTreeArrIndex* arrIndex)
-{
- emitter* emit = getEmitter();
- GenTreePtr arrObj = arrIndex->ArrObj();
- GenTreePtr indexNode = arrIndex->IndexExpr();
- regNumber arrReg = genConsumeReg(arrObj);
- regNumber indexReg = genConsumeReg(indexNode);
- regNumber tgtReg = arrIndex->gtRegNum;
- noway_assert(tgtReg != REG_NA);
-
- // We will use a temp register to load the lower bound and dimension size values
- //
- regMaskTP tmpRegsMask = arrIndex->gtRsvdRegs; // there will be two bits set
- tmpRegsMask &= ~genRegMask(tgtReg); // remove the bit for 'tgtReg' from 'tmpRegsMask'
-
- regMaskTP tmpRegMask = genFindLowestBit(tmpRegsMask); // set tmpRegMsk to a one-bit mask
- regNumber tmpReg = genRegNumFromMask(tmpRegMask); // set tmpReg from that mask
- noway_assert(tmpReg != REG_NA);
-
- assert(tgtReg != tmpReg);
-
- unsigned dim = arrIndex->gtCurrDim;
- unsigned rank = arrIndex->gtArrRank;
- var_types elemType = arrIndex->gtArrElemType;
- unsigned offset;
-
- offset = genOffsetOfMDArrayLowerBound(elemType, rank, dim);
- emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_8BYTE, tmpReg, arrReg, offset); // a 4 BYTE sign extending load
- emit->emitIns_R_R_R(INS_sub, EA_4BYTE, tgtReg, indexReg, tmpReg);
-
- offset = genOffsetOfMDArrayDimensionSize(elemType, rank, dim);
- emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_8BYTE, tmpReg, arrReg, offset); // a 4 BYTE sign extending load
- emit->emitIns_R_R(INS_cmp, EA_4BYTE, tgtReg, tmpReg);
-
- emitJumpKind jmpGEU = genJumpKindForOper(GT_GE, CK_UNSIGNED);
- genJumpToThrowHlpBlk(jmpGEU, SCK_RNGCHK_FAIL);
-
- genProduceReg(arrIndex);
-}
-
-//------------------------------------------------------------------------
-// genCodeForArrOffset: Generates code to compute the flattened array offset for
-// one dimension of an array reference:
-// result = (prevDimOffset * dimSize) + effectiveIndex
-// where dimSize is obtained from the arrObj operand
-//
-// Arguments:
-// arrOffset - the node for which we're generating code
-//
-// Return Value:
-// None.
-//
-// Notes:
-// dimSize and effectiveIndex are always non-negative, the former by design,
-// and the latter because it has been normalized to be zero-based.
-
-void CodeGen::genCodeForArrOffset(GenTreeArrOffs* arrOffset)
-{
- GenTreePtr offsetNode = arrOffset->gtOffset;
- GenTreePtr indexNode = arrOffset->gtIndex;
- regNumber tgtReg = arrOffset->gtRegNum;
-
- noway_assert(tgtReg != REG_NA);
-
- if (!offsetNode->IsIntegralConst(0))
- {
- emitter* emit = getEmitter();
- regNumber offsetReg = genConsumeReg(offsetNode);
- noway_assert(offsetReg != REG_NA);
- regNumber indexReg = genConsumeReg(indexNode);
- noway_assert(indexReg != REG_NA);
- GenTreePtr arrObj = arrOffset->gtArrObj;
- regNumber arrReg = genConsumeReg(arrObj);
- noway_assert(arrReg != REG_NA);
- regMaskTP tmpRegMask = arrOffset->gtRsvdRegs;
- regNumber tmpReg = genRegNumFromMask(tmpRegMask);
- noway_assert(tmpReg != REG_NA);
- unsigned dim = arrOffset->gtCurrDim;
- unsigned rank = arrOffset->gtArrRank;
- var_types elemType = arrOffset->gtArrElemType;
- unsigned offset = genOffsetOfMDArrayDimensionSize(elemType, rank, dim);
-
- // Load tmpReg with the dimension size
- emit->emitIns_R_R_I(ins_Load(TYP_INT), EA_8BYTE, tmpReg, arrReg, offset); // a 4 BYTE sign extending load
-
- // Evaluate tgtReg = offsetReg*dim_size + indexReg.
- emit->emitIns_R_R_R_R(INS_madd, EA_4BYTE, tgtReg, tmpReg, offsetReg, indexReg);
- }
- else
- {
- regNumber indexReg = genConsumeReg(indexNode);
- if (indexReg != tgtReg)
- {
- inst_RV_RV(INS_mov, tgtReg, indexReg, TYP_INT);
- }
- }
- genProduceReg(arrOffset);
-}
-
-// make a temporary indir we can feed to pattern matching routines
-// in cases where we don't want to instantiate all the indirs that happen
-//
-// TODO-Cleanup: move to CodeGenCommon.cpp
-GenTreeIndir CodeGen::indirForm(var_types type, GenTree* base)
-{
- GenTreeIndir i(GT_IND, type, base, nullptr);
- i.gtRegNum = REG_NA;
- // has to be nonnull (because contained nodes can't be the last in block)
- // but don't want it to be a valid pointer
- i.gtNext = (GenTree*)(-1);
- return i;
-}
-
-// make a temporary int we can feed to pattern matching routines
-// in cases where we don't want to instantiate
-//
-// TODO-Cleanup: move to CodeGenCommon.cpp
-GenTreeIntCon CodeGen::intForm(var_types type, ssize_t value)
-{
- GenTreeIntCon i(type, value);
- i.gtRegNum = REG_NA;
- // has to be nonnull (because contained nodes can't be the last in block)
- // but don't want it to be a valid pointer
- i.gtNext = (GenTree*)(-1);
- return i;
-}
-
instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
{
instruction ins = INS_brk;
@@ -4098,414 +3770,6 @@ instruction CodeGen::genGetInsForOper(genTreeOps oper, var_types type)
return ins;
}
-//------------------------------------------------------------------------
-// genCodeForShift: Generates the code sequence for a GenTree node that
-// represents a bit shift or rotate operation (<<, >>, >>>, rol, ror).
-//
-// Arguments:
-// tree - the bit shift node (that specifies the type of bit shift to perform).
-//
-// Assumptions:
-// a) All GenTrees are register allocated.
-//
-void CodeGen::genCodeForShift(GenTreePtr tree)
-{
- var_types targetType = tree->TypeGet();
- genTreeOps oper = tree->OperGet();
- instruction ins = genGetInsForOper(oper, targetType);
- emitAttr size = emitTypeSize(tree);
-
- assert(tree->gtRegNum != REG_NA);
-
- GenTreePtr operand = tree->gtGetOp1();
- genConsumeOperands(tree->AsOp());
-
- GenTreePtr shiftBy = tree->gtGetOp2();
- if (!shiftBy->IsCnsIntOrI())
- {
- getEmitter()->emitIns_R_R_R(ins, size, tree->gtRegNum, operand->gtRegNum, shiftBy->gtRegNum);
- }
- else
- {
- unsigned immWidth = emitter::getBitWidth(size); // immWidth will be set to 32 or 64
- ssize_t shiftByImm = shiftBy->gtIntCon.gtIconVal & (immWidth - 1);
-
- getEmitter()->emitIns_R_R_I(ins, size, tree->gtRegNum, operand->gtRegNum, shiftByImm);
- }
-
- genProduceReg(tree);
-}
-
-void CodeGen::genRegCopy(GenTree* treeNode)
-{
- assert(treeNode->OperGet() == GT_COPY);
-
- var_types targetType = treeNode->TypeGet();
- regNumber targetReg = treeNode->gtRegNum;
- assert(targetReg != REG_NA);
-
- GenTree* op1 = treeNode->gtOp.gtOp1;
-
- // Check whether this node and the node from which we're copying the value have the same
- // register type.
- // This can happen if (currently iff) we have a SIMD vector type that fits in an integer
- // register, in which case it is passed as an argument, or returned from a call,
- // in an integer register and must be copied if it's in an xmm register.
-
- if (varTypeIsFloating(treeNode) != varTypeIsFloating(op1))
- {
- inst_RV_RV(INS_fmov, targetReg, genConsumeReg(op1), targetType);
- }
- else
- {
- inst_RV_RV(ins_Copy(targetType), targetReg, genConsumeReg(op1), targetType);
- }
-
- if (op1->IsLocal())
- {
- // The lclVar will never be a def.
- // If it is a last use, the lclVar will be killed by genConsumeReg(), as usual, and genProduceReg will
- // appropriately set the gcInfo for the copied value.
- // If not, there are two cases we need to handle:
- // - If this is a TEMPORARY copy (indicated by the GTF_VAR_DEATH flag) the variable
- // will remain live in its original register.
- // genProduceReg() will appropriately set the gcInfo for the copied value,
- // and genConsumeReg will reset it.
- // - Otherwise, we need to update register info for the lclVar.
-
- GenTreeLclVarCommon* lcl = op1->AsLclVarCommon();
- assert((lcl->gtFlags & GTF_VAR_DEF) == 0);
-
- if ((lcl->gtFlags & GTF_VAR_DEATH) == 0 && (treeNode->gtFlags & GTF_VAR_DEATH) == 0)
- {
- LclVarDsc* varDsc = &compiler->lvaTable[lcl->gtLclNum];
-
- // If we didn't just spill it (in genConsumeReg, above), then update the register info
- if (varDsc->lvRegNum != REG_STK)
- {
- // The old location is dying
- genUpdateRegLife(varDsc, /*isBorn*/ false, /*isDying*/ true DEBUGARG(op1));
-
- gcInfo.gcMarkRegSetNpt(genRegMask(op1->gtRegNum));
-
- genUpdateVarReg(varDsc, treeNode);
-
- // The new location is going live
- genUpdateRegLife(varDsc, /*isBorn*/ true, /*isDying*/ false DEBUGARG(treeNode));
- }
- }
- }
- genProduceReg(treeNode);
-}
-
-// Produce code for a GT_CALL node
-void CodeGen::genCallInstruction(GenTreePtr node)
-{
- GenTreeCall* call = node->AsCall();
-
- assert(call->gtOper == GT_CALL);
-
- gtCallTypes callType = (gtCallTypes)call->gtCallType;
-
- IL_OFFSETX ilOffset = BAD_IL_OFFSET;
-
- // all virtuals should have been expanded into a control expression
- assert(!call->IsVirtual() || call->gtControlExpr || call->gtCallAddr);
-
- // Consume all the arg regs
- for (GenTreePtr list = call->gtCallLateArgs; list; list = list->MoveNext())
- {
- assert(list->OperIsList());
-
- GenTreePtr argNode = list->Current();
-
- fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(call, argNode->gtSkipReloadOrCopy());
- assert(curArgTabEntry);
-
- if (curArgTabEntry->regNum == REG_STK)
- continue;
-
- // Deal with multi register passed struct args.
- if (argNode->OperGet() == GT_FIELD_LIST)
- {
- GenTreeArgList* argListPtr = argNode->AsArgList();
- unsigned iterationNum = 0;
- regNumber argReg = curArgTabEntry->regNum;
- for (; argListPtr != nullptr; argListPtr = argListPtr->Rest(), iterationNum++)
- {
- GenTreePtr putArgRegNode = argListPtr->gtOp.gtOp1;
- assert(putArgRegNode->gtOper == GT_PUTARG_REG);
-
- genConsumeReg(putArgRegNode);
-
- if (putArgRegNode->gtRegNum != argReg)
- {
- inst_RV_RV(ins_Move_Extend(putArgRegNode->TypeGet(), putArgRegNode->InReg()), argReg,
- putArgRegNode->gtRegNum);
- }
-
- argReg = genRegArgNext(argReg);
- }
- }
- else
- {
- regNumber argReg = curArgTabEntry->regNum;
- genConsumeReg(argNode);
- if (argNode->gtRegNum != argReg)
- {
- inst_RV_RV(ins_Move_Extend(argNode->TypeGet(), argNode->InReg()), argReg, argNode->gtRegNum);
- }
- }
-
- // In the case of a varargs call,
- // the ABI dictates that if we have floating point args,
- // we must pass the enregistered arguments in both the
- // integer and floating point registers so, let's do that.
- if (call->IsVarargs() && varTypeIsFloating(argNode))
- {
- NYI_ARM64("CodeGen - IsVarargs");
- }
- }
-
- // Insert a null check on "this" pointer if asked.
- if (call->NeedsNullCheck())
- {
- const regNumber regThis = genGetThisArgReg(call);
- getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, REG_ZR, regThis, 0);
- }
-
- // Either gtControlExpr != null or gtCallAddr != null or it is a direct non-virtual call to a user or helper method.
- CORINFO_METHOD_HANDLE methHnd;
- GenTree* target = call->gtControlExpr;
- if (callType == CT_INDIRECT)
- {
- assert(target == nullptr);
- target = call->gtCall.gtCallAddr;
- methHnd = nullptr;
- }
- else
- {
- methHnd = call->gtCallMethHnd;
- }
-
- CORINFO_SIG_INFO* sigInfo = nullptr;
-#ifdef DEBUG
- // Pass the call signature information down into the emitter so the emitter can associate
- // native call sites with the signatures they were generated from.
- if (callType != CT_HELPER)
- {
- sigInfo = call->callSig;
- }
-#endif // DEBUG
-
- // If fast tail call, then we are done. In this case we setup the args (both reg args
- // and stack args in incoming arg area) and call target in IP0. Epilog sequence would
- // generate "br IP0".
- if (call->IsFastTailCall())
- {
- // Don't support fast tail calling JIT helpers
- assert(callType != CT_HELPER);
-
- // Fast tail calls materialize call target either in gtControlExpr or in gtCallAddr.
- assert(target != nullptr);
-
- genConsumeReg(target);
-
- if (target->gtRegNum != REG_IP0)
- {
- inst_RV_RV(INS_mov, REG_IP0, target->gtRegNum);
- }
- return;
- }
-
- // For a pinvoke to unmanged code we emit a label to clear
- // the GC pointer state before the callsite.
- // We can't utilize the typical lazy killing of GC pointers
- // at (or inside) the callsite.
- if (call->IsUnmanaged())
- {
- genDefineTempLabel(genCreateTempLabel());
- }
-
- // Determine return value size(s).
- ReturnTypeDesc* pRetTypeDesc = call->GetReturnTypeDesc();
- emitAttr retSize = EA_PTRSIZE;
- emitAttr secondRetSize = EA_UNKNOWN;
-
- if (call->HasMultiRegRetVal())
- {
- retSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(0));
- secondRetSize = emitTypeSize(pRetTypeDesc->GetReturnRegType(1));
- }
- else
- {
- assert(!varTypeIsStruct(call));
-
- if (call->gtType == TYP_REF || call->gtType == TYP_ARRAY)
- {
- retSize = EA_GCREF;
- }
- else if (call->gtType == TYP_BYREF)
- {
- retSize = EA_BYREF;
- }
- }
-
- // We need to propagate the IL offset information to the call instruction, so we can emit
- // an IL to native mapping record for the call, to support managed return value debugging.
- // We don't want tail call helper calls that were converted from normal calls to get a record,
- // so we skip this hash table lookup logic in that case.
- if (compiler->opts.compDbgInfo && compiler->genCallSite2ILOffsetMap != nullptr && !call->IsTailCall())
- {
- (void)compiler->genCallSite2ILOffsetMap->Lookup(call, &ilOffset);
- }
-
- if (target != nullptr)
- {
- // For Arm64 a call target can not be a contained indirection
- assert(!target->isContainedIndir());
-
- // We have already generated code for gtControlExpr evaluating it into a register.
- // We just need to emit "call reg" in this case.
- //
- assert(genIsValidIntReg(target->gtRegNum));
-
- genEmitCall(emitter::EC_INDIR_R, methHnd,
- INDEBUG_LDISASM_COMMA(sigInfo) nullptr, // addr
- retSize, secondRetSize, ilOffset, genConsumeReg(target));
- }
- else
- {
- // Generate a direct call to a non-virtual user defined or helper method
- assert(callType == CT_HELPER || callType == CT_USER_FUNC);
-
- void* addr = nullptr;
- if (callType == CT_HELPER)
- {
- // Direct call to a helper method.
- CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(methHnd);
- noway_assert(helperNum != CORINFO_HELP_UNDEF);
-
- void* pAddr = nullptr;
- addr = compiler->compGetHelperFtn(helperNum, (void**)&pAddr);
-
- if (addr == nullptr)
- {
- addr = pAddr;
- }
- }
- else
- {
- // Direct call to a non-virtual user function.
- CORINFO_ACCESS_FLAGS aflags = CORINFO_ACCESS_ANY;
- if (call->IsSameThis())
- {
- aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_THIS);
- }
-
- if ((call->NeedsNullCheck()) == 0)
- {
- aflags = (CORINFO_ACCESS_FLAGS)(aflags | CORINFO_ACCESS_NONNULL);
- }
-
- CORINFO_CONST_LOOKUP addrInfo;
- compiler->info.compCompHnd->getFunctionEntryPoint(methHnd, &addrInfo, aflags);
-
- addr = addrInfo.addr;
- }
-#if 0
- // Use this path if you want to load an absolute call target using
- // a sequence of movs followed by an indirect call (blr instruction)
-
- // Load the call target address in x16
- instGen_Set_Reg_To_Imm(EA_8BYTE, REG_IP0, (ssize_t) addr);
-
- // indirect call to constant address in IP0
- genEmitCall(emitter::EC_INDIR_R,
- methHnd,
- INDEBUG_LDISASM_COMMA(sigInfo)
- nullptr, //addr
- retSize,
- secondRetSize,
- ilOffset,
- REG_IP0);
-#else
- // Non-virtual direct call to known addresses
- genEmitCall(emitter::EC_FUNC_TOKEN, methHnd, INDEBUG_LDISASM_COMMA(sigInfo) addr, retSize, secondRetSize,
- ilOffset);
-#endif
- }
-
- // if it was a pinvoke we may have needed to get the address of a label
- if (genPendingCallLabel)
- {
- assert(call->IsUnmanaged());
- genDefineTempLabel(genPendingCallLabel);
- genPendingCallLabel = nullptr;
- }
-
- // Update GC info:
- // All Callee arg registers are trashed and no longer contain any GC pointers.
- // TODO-ARM64-Bug?: As a matter of fact shouldn't we be killing all of callee trashed regs here?
- // For now we will assert that other than arg regs gc ref/byref set doesn't contain any other
- // registers from RBM_CALLEE_TRASH
- assert((gcInfo.gcRegGCrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
- assert((gcInfo.gcRegByrefSetCur & (RBM_CALLEE_TRASH & ~RBM_ARG_REGS)) == 0);
- gcInfo.gcRegGCrefSetCur &= ~RBM_ARG_REGS;
- gcInfo.gcRegByrefSetCur &= ~RBM_ARG_REGS;
-
- var_types returnType = call->TypeGet();
- if (returnType != TYP_VOID)
- {
- regNumber returnReg;
-
- if (call->HasMultiRegRetVal())
- {
- assert(pRetTypeDesc != nullptr);
- unsigned regCount = pRetTypeDesc->GetReturnRegCount();
-
- // If regs allocated to call node are different from ABI return
- // regs in which the call has returned its result, move the result
- // to regs allocated to call node.
- for (unsigned i = 0; i < regCount; ++i)
- {
- var_types regType = pRetTypeDesc->GetReturnRegType(i);
- returnReg = pRetTypeDesc->GetABIReturnReg(i);
- regNumber allocatedReg = call->GetRegNumByIdx(i);
- if (returnReg != allocatedReg)
- {
- inst_RV_RV(ins_Copy(regType), allocatedReg, returnReg, regType);
- }
- }
- }
- else
- {
- if (varTypeIsFloating(returnType))
- {
- returnReg = REG_FLOATRET;
- }
- else
- {
- returnReg = REG_INTRET;
- }
-
- if (call->gtRegNum != returnReg)
- {
- inst_RV_RV(ins_Copy(returnType), call->gtRegNum, returnReg, returnType);
- }
- }
-
- genProduceReg(call);
- }
-
- // If there is nothing next, that means the result is thrown away, so this value is not live.
- // However, for minopts or debuggable code, we keep it live to support managed return value debugging.
- if ((call->gtNext == nullptr) && !compiler->opts.MinOpts() && !compiler->opts.compDbgCode)
- {
- gcInfo.gcMarkRegSetNpt(RBM_INTRET);
- }
-}
-
// Produce code for a GT_JMP node.
// The arguments of the caller needs to be transferred to the callee before exiting caller.
// The actual jump to callee is generated as part of caller epilog sequence.
@@ -4767,13 +4031,12 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
if (lsl > 0)
{
// Generate code to set tmpReg = base + index*scale
- emit->emitIns_R_R_R_I(INS_add, EA_PTRSIZE, tmpReg, memBase->gtRegNum, index->gtRegNum, lsl,
- INS_OPTS_LSL);
+ emit->emitIns_R_R_R_I(INS_add, size, tmpReg, memBase->gtRegNum, index->gtRegNum, lsl, INS_OPTS_LSL);
}
else // no scale
{
// Generate code to set tmpReg = base + index
- emit->emitIns_R_R_R(INS_add, EA_PTRSIZE, tmpReg, memBase->gtRegNum, index->gtRegNum);
+ emit->emitIns_R_R_R(INS_add, size, tmpReg, memBase->gtRegNum, index->gtRegNum);
}
// Then compute target reg from [tmpReg + offset]
@@ -4786,7 +4049,7 @@ void CodeGen::genLeaInstruction(GenTreeAddrMode* lea)
instGen_Set_Reg_To_Imm(EA_PTRSIZE, tmpReg, offset);
// Then add the base register
// rd = rd + base
- emit->emitIns_R_R_R(INS_add, EA_PTRSIZE, tmpReg, tmpReg, memBase->gtRegNum);
+ emit->emitIns_R_R_R(INS_add, size, tmpReg, tmpReg, memBase->gtRegNum);
noway_assert(tmpReg != index->gtRegNum);
@@ -5035,237 +4298,6 @@ void CodeGen::genSetRegToCond(regNumber dstReg, GenTreePtr tree)
}
//------------------------------------------------------------------------
-// genIntToIntCast: Generate code for an integer cast
-// This method handles integer overflow checking casts
-// as well as ordinary integer casts.
-//
-// Arguments:
-// treeNode - The GT_CAST node
-//
-// Return Value:
-// None.
-//
-// Assumptions:
-// The treeNode is not a contained node and must have an assigned register.
-// For a signed convert from byte, the source must be in a byte-addressable register.
-// Neither the source nor target type can be a floating point type.
-//
-// TODO-ARM64-CQ: Allow castOp to be a contained node without an assigned register.
-//
-void CodeGen::genIntToIntCast(GenTreePtr treeNode)
-{
- assert(treeNode->OperGet() == GT_CAST);
-
- GenTreePtr castOp = treeNode->gtCast.CastOp();
- emitter* emit = getEmitter();
-
- var_types dstType = treeNode->CastToType();
- var_types srcType = genActualType(castOp->TypeGet());
- emitAttr movSize = emitActualTypeSize(dstType);
- bool movRequired = false;
-
- regNumber targetReg = treeNode->gtRegNum;
- regNumber sourceReg = castOp->gtRegNum;
-
- // For Long to Int conversion we will have a reserved integer register to hold the immediate mask
- regNumber tmpReg = (treeNode->gtRsvdRegs == RBM_NONE) ? REG_NA : genRegNumFromMask(treeNode->gtRsvdRegs);
-
- assert(genIsValidIntReg(targetReg));
- assert(genIsValidIntReg(sourceReg));
-
- instruction ins = INS_invalid;
-
- genConsumeReg(castOp);
- Lowering::CastInfo castInfo;
-
- // Get information about the cast.
- Lowering::getCastDescription(treeNode, &castInfo);
-
- if (castInfo.requiresOverflowCheck)
- {
-
- emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
-
- if (castInfo.signCheckOnly)
- {
- // We only need to check for a negative value in sourceReg
- emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, 0);
- emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
- noway_assert(genTypeSize(srcType) == 4 || genTypeSize(srcType) == 8);
- // This is only interesting case to ensure zero-upper bits.
- if ((srcType == TYP_INT) && (dstType == TYP_ULONG))
- {
- // cast to TYP_ULONG:
- // We use a mov with size=EA_4BYTE
- // which will zero out the upper bits
- movSize = EA_4BYTE;
- movRequired = true;
- }
- }
- else if (castInfo.unsignedSource || castInfo.unsignedDest)
- {
- // When we are converting from/to unsigned,
- // we only have to check for any bits set in 'typeMask'
-
- noway_assert(castInfo.typeMask != 0);
- emit->emitIns_R_I(INS_tst, cmpSize, sourceReg, castInfo.typeMask);
- emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
- }
- else
- {
- // For a narrowing signed cast
- //
- // We must check the value is in a signed range.
-
- // Compare with the MAX
-
- noway_assert((castInfo.typeMin != 0) && (castInfo.typeMax != 0));
-
- if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, cmpSize))
- {
- emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMax);
- }
- else
- {
- noway_assert(tmpReg != REG_NA);
- instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMax);
- emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
- }
-
- emitJumpKind jmpGT = genJumpKindForOper(GT_GT, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpGT, SCK_OVERFLOW);
-
- // Compare with the MIN
-
- if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, cmpSize))
- {
- emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMin);
- }
- else
- {
- noway_assert(tmpReg != REG_NA);
- instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMin);
- emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
- }
-
- emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
- genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
- }
- ins = INS_mov;
- }
- else // Non-overflow checking cast.
- {
- if (genTypeSize(srcType) == genTypeSize(dstType))
- {
- ins = INS_mov;
- }
- else
- {
- var_types extendType = TYP_UNKNOWN;
-
- // If we need to treat a signed type as unsigned
- if ((treeNode->gtFlags & GTF_UNSIGNED) != 0)
- {
- extendType = genUnsignedType(srcType);
- movSize = emitTypeSize(extendType);
- movRequired = true;
- }
- else
- {
- if (genTypeSize(srcType) < genTypeSize(dstType))
- {
- extendType = srcType;
- if (srcType == TYP_UINT)
- {
- // If we are casting from a smaller type to
- // a larger type, then we need to make sure the
- // higher 4 bytes are zero to gaurentee the correct value.
- // Therefore using a mov with EA_4BYTE in place of EA_8BYTE
- // will zero the upper bits
- movSize = EA_4BYTE;
- movRequired = true;
- }
- }
- else // (genTypeSize(srcType) > genTypeSize(dstType))
- {
- extendType = dstType;
- if (dstType == TYP_INT)
- {
- movSize = EA_8BYTE; // a sxtw instruction requires EA_8BYTE
- }
- }
- }
-
- ins = ins_Move_Extend(extendType, castOp->InReg());
- }
- }
-
- // We should never be generating a load from memory instruction here!
- assert(!emit->emitInsIsLoad(ins));
-
- if ((ins != INS_mov) || movRequired || (targetReg != sourceReg))
- {
- emit->emitIns_R_R(ins, movSize, targetReg, sourceReg);
- }
-
- genProduceReg(treeNode);
-}
-
-//------------------------------------------------------------------------
-// genFloatToFloatCast: Generate code for a cast between float and double
-//
-// Arguments:
-// treeNode - The GT_CAST node
-//
-// Return Value:
-// None.
-//
-// Assumptions:
-// Cast is a non-overflow conversion.
-// The treeNode must have an assigned register.
-// The cast is between float and double or vice versa.
-//
-void CodeGen::genFloatToFloatCast(GenTreePtr treeNode)
-{
- // float <--> double conversions are always non-overflow ones
- assert(treeNode->OperGet() == GT_CAST);
- assert(!treeNode->gtOverflow());
-
- regNumber targetReg = treeNode->gtRegNum;
- assert(genIsValidFloatReg(targetReg));
-
- GenTreePtr op1 = treeNode->gtOp.gtOp1;
- assert(!op1->isContained()); // Cannot be contained
- assert(genIsValidFloatReg(op1->gtRegNum)); // Must be a valid float reg.
-
- var_types dstType = treeNode->CastToType();
- var_types srcType = op1->TypeGet();
- assert(varTypeIsFloating(srcType) && varTypeIsFloating(dstType));
-
- genConsumeOperands(treeNode->AsOp());
-
- // treeNode must be a reg
- assert(!treeNode->isContained());
-
- if (srcType != dstType)
- {
- insOpts cvtOption = (srcType == TYP_FLOAT) ? INS_OPTS_S_TO_D // convert Single to Double
- : INS_OPTS_D_TO_S; // convert Double to Single
-
- getEmitter()->emitIns_R_R(INS_fcvt, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum, cvtOption);
- }
- else if (treeNode->gtRegNum != op1->gtRegNum)
- {
- // If double to double cast or float to float cast. Emit a move instruction.
- getEmitter()->emitIns_R_R(INS_mov, emitTypeSize(treeNode), treeNode->gtRegNum, op1->gtRegNum);
- }
-
- genProduceReg(treeNode);
-}
-
-//------------------------------------------------------------------------
// genIntToFloatCast: Generate code to cast an int/long to float/double
//
// Arguments:
@@ -5543,546 +4575,6 @@ int CodeGenInterface::genCallerSPtoInitialSPdelta()
return callerSPtoSPdelta;
}
-//---------------------------------------------------------------------
-// genIntrinsic - generate code for a given intrinsic
-//
-// Arguments
-// treeNode - the GT_INTRINSIC node
-//
-// Return value:
-// None
-//
-void CodeGen::genIntrinsic(GenTreePtr treeNode)
-{
- // Both operand and its result must be of the same floating point type.
- GenTreePtr srcNode = treeNode->gtOp.gtOp1;
- assert(varTypeIsFloating(srcNode));
- assert(srcNode->TypeGet() == treeNode->TypeGet());
-
- // Right now only Abs/Round/Sqrt are treated as math intrinsics.
- //
- switch (treeNode->gtIntrinsic.gtIntrinsicId)
- {
- case CORINFO_INTRINSIC_Abs:
- genConsumeOperands(treeNode->AsOp());
- getEmitter()->emitInsBinary(INS_fabs, emitTypeSize(treeNode), treeNode, srcNode);
- break;
-
- case CORINFO_INTRINSIC_Round:
- genConsumeOperands(treeNode->AsOp());
- getEmitter()->emitInsBinary(INS_frintn, emitTypeSize(treeNode), treeNode, srcNode);
- break;
-
- case CORINFO_INTRINSIC_Sqrt:
- genConsumeOperands(treeNode->AsOp());
- getEmitter()->emitInsBinary(INS_fsqrt, emitTypeSize(treeNode), treeNode, srcNode);
- break;
-
- default:
- assert(!"genIntrinsic: Unsupported intrinsic");
- unreached();
- }
-
- genProduceReg(treeNode);
-}
-
-//---------------------------------------------------------------------
-// genPutArgStk - generate code for a GT_PUTARG_STK node
-//
-// Arguments
-// treeNode - the GT_PUTARG_STK node
-//
-// Return value:
-// None
-//
-void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
-{
- assert(treeNode->OperGet() == GT_PUTARG_STK);
- var_types targetType = treeNode->TypeGet();
- GenTreePtr source = treeNode->gtOp.gtOp1;
- emitter* emit = getEmitter();
-
- // This is the varNum for our store operations,
- // typically this is the varNum for the Outgoing arg space
- // When we are generating a tail call it will be the varNum for arg0
- unsigned varNumOut;
- unsigned argOffsetMax; // Records the maximum size of this area for assert checks
-
- // This is the varNum for our load operations,
- // only used when we have a multireg struct with a LclVar source
- unsigned varNumInp = BAD_VAR_NUM;
-
- // Get argument offset to use with 'varNumOut'
- // Here we cross check that argument offset hasn't changed from lowering to codegen since
- // we are storing arg slot number in GT_PUTARG_STK node in lowering phase.
- unsigned argOffsetOut = treeNode->AsPutArgStk()->gtSlotNum * TARGET_POINTER_SIZE;
-
-#ifdef DEBUG
- fgArgTabEntryPtr curArgTabEntry = compiler->gtArgEntryByNode(treeNode->AsPutArgStk()->gtCall, treeNode);
- assert(curArgTabEntry);
- assert(argOffsetOut == (curArgTabEntry->slotNum * TARGET_POINTER_SIZE));
-#endif // DEBUG
-
-#if FEATURE_FASTTAILCALL
- bool putInIncomingArgArea = treeNode->AsPutArgStk()->putInIncomingArgArea;
-#else
- const bool putInIncomingArgArea = false;
-#endif
- // Whether to setup stk arg in incoming or out-going arg area?
- // Fast tail calls implemented as epilog+jmp = stk arg is setup in incoming arg area.
- // All other calls - stk arg is setup in out-going arg area.
- if (putInIncomingArgArea)
- {
- varNumOut = getFirstArgWithStackSlot();
- argOffsetMax = compiler->compArgSize;
-#if FEATURE_FASTTAILCALL
- // This must be a fast tail call.
- assert(treeNode->AsPutArgStk()->gtCall->AsCall()->IsFastTailCall());
-
- // Since it is a fast tail call, the existence of first incoming arg is guaranteed
- // because fast tail call requires that in-coming arg area of caller is >= out-going
- // arg area required for tail call.
- LclVarDsc* varDsc = &(compiler->lvaTable[varNumOut]);
- assert(varDsc != nullptr);
-#endif // FEATURE_FASTTAILCALL
- }
- else
- {
- varNumOut = compiler->lvaOutgoingArgSpaceVar;
- argOffsetMax = compiler->lvaOutgoingArgSpaceSize;
- }
- bool isStruct = (targetType == TYP_STRUCT) || (source->OperGet() == GT_FIELD_LIST);
-
- if (!isStruct) // a normal non-Struct argument
- {
- instruction storeIns = ins_Store(targetType);
- emitAttr storeAttr = emitTypeSize(targetType);
-
- // If it is contained then source must be the integer constant zero
- if (source->isContained())
- {
- assert(source->OperGet() == GT_CNS_INT);
- assert(source->AsIntConCommon()->IconValue() == 0);
- emit->emitIns_S_R(storeIns, storeAttr, REG_ZR, varNumOut, argOffsetOut);
- }
- else
- {
- genConsumeReg(source);
- emit->emitIns_S_R(storeIns, storeAttr, source->gtRegNum, varNumOut, argOffsetOut);
- }
- argOffsetOut += EA_SIZE_IN_BYTES(storeAttr);
- assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
- }
- else // We have some kind of a struct argument
- {
- assert(source->isContained()); // We expect that this node was marked as contained in LowerArm64
-
- if (source->OperGet() == GT_FIELD_LIST)
- {
- // Deal with the multi register passed struct args.
- GenTreeFieldList* fieldListPtr = source->AsFieldList();
-
- // Evaluate each of the GT_FIELD_LIST items into their register
- // and store their register into the outgoing argument area
- for (; fieldListPtr != nullptr; fieldListPtr = fieldListPtr->Rest())
- {
- GenTreePtr nextArgNode = fieldListPtr->gtOp.gtOp1;
- genConsumeReg(nextArgNode);
-
- regNumber reg = nextArgNode->gtRegNum;
- var_types type = nextArgNode->TypeGet();
- emitAttr attr = emitTypeSize(type);
-
- // Emit store instructions to store the registers produced by the GT_FIELD_LIST into the outgoing
- // argument area
- emit->emitIns_S_R(ins_Store(type), attr, reg, varNumOut, argOffsetOut);
- argOffsetOut += EA_SIZE_IN_BYTES(attr);
- assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
- }
- }
- else // We must have a GT_OBJ or a GT_LCL_VAR
- {
- noway_assert((source->OperGet() == GT_LCL_VAR) || (source->OperGet() == GT_OBJ));
-
- var_types targetType = source->TypeGet();
- noway_assert(varTypeIsStruct(targetType));
-
- // We will copy this struct to the stack, possibly using a ldp instruction
- // Setup loReg and hiReg from the internal registers that we reserved in lower.
- //
- regNumber loReg = REG_NA;
- regNumber hiReg = REG_NA;
- regNumber addrReg = REG_NA;
-
- // In lowerArm64/TreeNodeInfoInitPutArgStk we have reserved two internal integer registers
- genGetRegPairFromMask(treeNode->gtRsvdRegs, &loReg, &hiReg);
-
- GenTreeLclVarCommon* varNode = nullptr;
- GenTreePtr addrNode = nullptr;
-
- if (source->OperGet() == GT_LCL_VAR)
- {
- varNode = source->AsLclVarCommon();
- }
- else // we must have a GT_OBJ
- {
- assert(source->OperGet() == GT_OBJ);
-
- addrNode = source->gtOp.gtOp1;
-
- // addrNode can either be a GT_LCL_VAR_ADDR or an address expression
- //
- if (addrNode->OperGet() == GT_LCL_VAR_ADDR)
- {
- // We have a GT_OBJ(GT_LCL_VAR_ADDR)
- //
- // We will treat this case the same as above
- // (i.e if we just had this GT_LCL_VAR directly as the source)
- // so update 'source' to point this GT_LCL_VAR_ADDR node
- // and continue to the codegen for the LCL_VAR node below
- //
- varNode = addrNode->AsLclVarCommon();
- addrNode = nullptr;
- }
- }
-
- // Either varNode or addrNOde must have been setup above,
- // the xor ensures that only one of the two is setup, not both
- assert((varNode != nullptr) ^ (addrNode != nullptr));
-
- BYTE gcPtrs[MAX_ARG_REG_COUNT] = {}; // TYPE_GC_NONE = 0
- BYTE* structGcLayout = &gcPtrs[0]; // The GC layout for the struct
- unsigned gcPtrCount; // The count of GC pointers in the struct
- int structSize;
- bool isHfa;
-
- // Setup the structSize, isHFa, and gcPtrCount
- if (varNode != nullptr)
- {
- varNumInp = varNode->gtLclNum;
- assert(varNumInp < compiler->lvaCount);
- LclVarDsc* varDsc = &compiler->lvaTable[varNumInp];
-
- assert(varDsc->lvType == TYP_STRUCT);
- assert(varDsc->lvOnFrame); // This struct also must live in the stack frame
- assert(!varDsc->lvRegister); // And it can't live in a register (SIMD)
-
- structSize = varDsc->lvSize(); // This yields the roundUp size, but that is fine
- // as that is how much stack is allocated for this LclVar
- isHfa = varDsc->lvIsHfa();
- gcPtrCount = varDsc->lvStructGcCount;
- structGcLayout = varDsc->lvGcLayout;
- }
- else // addrNode is used
- {
- assert(addrNode != nullptr);
-
- // Generate code to load the address that we need into a register
- genConsumeAddress(addrNode);
- addrReg = addrNode->gtRegNum;
-
- CORINFO_CLASS_HANDLE objClass = source->gtObj.gtClass;
-
- structSize = compiler->info.compCompHnd->getClassSize(objClass);
- isHfa = compiler->IsHfa(objClass);
- gcPtrCount = compiler->info.compCompHnd->getClassGClayout(objClass, &gcPtrs[0]);
- }
-
- bool hasGCpointers = (gcPtrCount > 0); // true if there are any GC pointers in the struct
-
- // If we have an HFA we can't have any GC pointers,
- // if not then the max size for the the struct is 16 bytes
- if (isHfa)
- {
- noway_assert(gcPtrCount == 0);
- }
- else
- {
- noway_assert(structSize <= 2 * TARGET_POINTER_SIZE);
- }
-
- noway_assert(structSize <= MAX_PASS_MULTIREG_BYTES);
-
- // For a 16-byte structSize with GC pointers we will use two ldr and two str instructions
- // ldr x2, [x0]
- // ldr x3, [x0, #8]
- // str x2, [sp, #16]
- // str x3, [sp, #24]
- //
- // For a 16-byte structSize with no GC pointers we will use a ldp and two str instructions
- // ldp x2, x3, [x0]
- // str x2, [sp, #16]
- // str x3, [sp, #24]
- //
- // For a 32-byte structSize with no GC pointers we will use two ldp and four str instructions
- // ldp x2, x3, [x0]
- // str x2, [sp, #16]
- // str x3, [sp, #24]
- // ldp x2, x3, [x0]
- // str x2, [sp, #32]
- // str x3, [sp, #40]
- //
- // Note that when loading from a varNode we currently can't use the ldp instruction
- // TODO-ARM64-CQ: Implement support for using a ldp instruction with a varNum (see emitIns_R_S)
- //
-
- int remainingSize = structSize;
- unsigned structOffset = 0;
- unsigned nextIndex = 0;
-
- while (remainingSize >= 2 * TARGET_POINTER_SIZE)
- {
- var_types type0 = compiler->getJitGCType(gcPtrs[nextIndex + 0]);
- var_types type1 = compiler->getJitGCType(gcPtrs[nextIndex + 1]);
-
- if (hasGCpointers)
- {
- // We have GC pointers, so use two ldr instructions
- //
- // We must do it this way because we can't currently pass or track
- // two different emitAttr values for a ldp instruction.
-
- // Make sure that the first load instruction does not overwrite the addrReg.
- //
- if (loReg != addrReg)
- {
- if (varNode != nullptr)
- {
- // Load from our varNumImp source
- emit->emitIns_R_S(ins_Load(type0), emitTypeSize(type0), loReg, varNumInp, 0);
- emit->emitIns_R_S(ins_Load(type1), emitTypeSize(type1), hiReg, varNumInp,
- TARGET_POINTER_SIZE);
- }
- else
- {
- // Load from our address expression source
- emit->emitIns_R_R_I(ins_Load(type0), emitTypeSize(type0), loReg, addrReg, structOffset);
- emit->emitIns_R_R_I(ins_Load(type1), emitTypeSize(type1), hiReg, addrReg,
- structOffset + TARGET_POINTER_SIZE);
- }
- }
- else // loReg == addrReg
- {
- assert(varNode == nullptr); // because addrReg is REG_NA when varNode is non-null
- assert(hiReg != addrReg);
- // Load from our address expression source
- emit->emitIns_R_R_I(ins_Load(type1), emitTypeSize(type1), hiReg, addrReg,
- structOffset + TARGET_POINTER_SIZE);
- emit->emitIns_R_R_I(ins_Load(type0), emitTypeSize(type0), loReg, addrReg, structOffset);
- }
- }
- else // our struct has no GC pointers
- {
- if (varNode != nullptr)
- {
- // Load from our varNumImp source, currently we can't use a ldp instruction to do this
- emit->emitIns_R_S(ins_Load(type0), emitTypeSize(type0), loReg, varNumInp, 0);
- emit->emitIns_R_S(ins_Load(type1), emitTypeSize(type1), hiReg, varNumInp, TARGET_POINTER_SIZE);
- }
- else
- {
- // Use a ldp instruction
-
- // Load from our address expression source
- emit->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, loReg, hiReg, addrReg, structOffset);
- }
- }
-
- // 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);
- argOffsetOut += (2 * TARGET_POINTER_SIZE); // We stored 16-bytes of the struct
- assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
-
- remainingSize -= (2 * TARGET_POINTER_SIZE); // We loaded 16-bytes of the struct
- structOffset += (2 * TARGET_POINTER_SIZE);
- nextIndex += 2;
- }
-
- // For a 12-byte structSize we will we will generate two load instructions
- // ldr x2, [x0]
- // ldr w3, [x0, #8]
- // str x2, [sp, #16]
- // str w3, [sp, #24]
- //
- // When the first instruction has a loReg that is the same register as the addrReg,
- // we set deferLoad to true and issue the intructions in the reverse order
- // ldr x3, [x2, #8]
- // ldr x2, [x2]
- // str x2, [sp, #16]
- // str x3, [sp, #24]
- //
-
- var_types nextType = compiler->getJitGCType(gcPtrs[nextIndex]);
- emitAttr nextAttr = emitTypeSize(nextType);
- regNumber curReg = loReg;
-
- bool deferLoad = false;
- var_types deferType = TYP_UNKNOWN;
- emitAttr deferAttr = EA_PTRSIZE;
- int deferOffset = 0;
-
- while (remainingSize > 0)
- {
- if (remainingSize >= TARGET_POINTER_SIZE)
- {
- remainingSize -= TARGET_POINTER_SIZE;
-
- if ((curReg == addrReg) && (remainingSize != 0))
- {
- deferLoad = true;
- deferType = nextType;
- deferAttr = emitTypeSize(nextType);
- deferOffset = structOffset;
- }
- else // the typical case
- {
- if (varNode != nullptr)
- {
- // Load from our varNumImp source
- emit->emitIns_R_S(ins_Load(nextType), nextAttr, curReg, varNumInp, structOffset);
- }
- else
- {
- // Load from our address expression source
- emit->emitIns_R_R_I(ins_Load(nextType), nextAttr, curReg, addrReg, structOffset);
- }
- // Emit a store instruction to store the register into the outgoing argument area
- emit->emitIns_S_R(ins_Store(nextType), nextAttr, curReg, varNumOut, argOffsetOut);
- argOffsetOut += EA_SIZE_IN_BYTES(nextAttr);
- assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
- }
- curReg = hiReg;
- structOffset += TARGET_POINTER_SIZE;
- nextIndex++;
- nextType = compiler->getJitGCType(gcPtrs[nextIndex]);
- nextAttr = emitTypeSize(nextType);
- }
- else // (remainingSize < TARGET_POINTER_SIZE)
- {
- int loadSize = remainingSize;
- remainingSize = 0;
-
- // We should never have to do a non-pointer sized load when we have a LclVar source
- assert(varNode == nullptr);
-
- // the left over size is smaller than a pointer and thus can never be a GC type
- assert(varTypeIsGC(nextType) == false);
-
- var_types loadType = TYP_UINT;
- if (loadSize == 1)
- {
- loadType = TYP_UBYTE;
- }
- else if (loadSize == 2)
- {
- loadType = TYP_USHORT;
- }
- else
- {
- // Need to handle additional loadSize cases here
- noway_assert(loadSize == 4);
- }
-
- instruction loadIns = ins_Load(loadType);
- emitAttr loadAttr = emitAttr(loadSize);
-
- // When deferLoad is false, curReg can be the same as addrReg
- // because the last instruction is allowed to overwrite addrReg.
- //
- noway_assert(!deferLoad || (curReg != addrReg));
-
- emit->emitIns_R_R_I(loadIns, loadAttr, curReg, addrReg, structOffset);
-
- // Emit a store instruction to store the register into the outgoing argument area
- emit->emitIns_S_R(ins_Store(loadType), loadAttr, curReg, varNumOut, argOffsetOut);
- argOffsetOut += EA_SIZE_IN_BYTES(loadAttr);
- assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
- }
- }
-
- if (deferLoad)
- {
- // We should never have to do a deferred load when we have a LclVar source
- assert(varNode == nullptr);
-
- curReg = addrReg;
-
- // Load from our address expression source
- emit->emitIns_R_R_I(ins_Load(deferType), deferAttr, curReg, addrReg, deferOffset);
-
- // Emit a store instruction to store the register into the outgoing argument area
- emit->emitIns_S_R(ins_Store(nextType), nextAttr, curReg, varNumOut, argOffsetOut);
- argOffsetOut += EA_SIZE_IN_BYTES(nextAttr);
- assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
- }
- }
- }
-}
-
-/*****************************************************************************
- *
- * Create and record GC Info for the function.
- */
-void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize,
- unsigned prologSize,
- unsigned epilogSize DEBUGARG(void* codePtr))
-{
- genCreateAndStoreGCInfoX64(codeSize, prologSize DEBUGARG(codePtr));
-}
-
-void CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize DEBUGARG(void* codePtr))
-{
- IAllocator* allowZeroAlloc = new (compiler, CMK_GC) AllowZeroAllocator(compiler->getAllocatorGC());
- GcInfoEncoder* gcInfoEncoder = new (compiler, CMK_GC)
- GcInfoEncoder(compiler->info.compCompHnd, compiler->info.compMethodInfo, allowZeroAlloc, NOMEM);
- assert(gcInfoEncoder != nullptr);
-
- // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32).
- gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize);
-
- // First we figure out the encoder ID's for the stack slots and registers.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS);
-
- // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them).
- gcInfoEncoder->FinalizeSlotIds();
-
- // Now we can actually use those slot ID's to declare live ranges.
- gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK);
-
- if (compiler->opts.compDbgEnC)
- {
- // what we have to preserve is called the "frame header" (see comments in VM\eetwain.cpp)
- // which is:
- // -return address
- // -saved off RBP
- // -saved 'this' pointer and bool for synchronized methods
-
- // 4 slots for RBP + return address + RSI + RDI
- int preservedAreaSize = 4 * REGSIZE_BYTES;
-
- if (compiler->info.compFlags & CORINFO_FLG_SYNCH)
- {
- if (!(compiler->info.compFlags & CORINFO_FLG_STATIC))
- preservedAreaSize += REGSIZE_BYTES;
-
- preservedAreaSize += 1; // bool for synchronized methods
- }
-
- // Used to signal both that the method is compiled for EnC, and also the size of the block at the top of the
- // frame
- gcInfoEncoder->SetSizeOfEditAndContinuePreservedArea(preservedAreaSize);
- }
-
- gcInfoEncoder->Build();
-
- // GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t)
- // let's save the values anyway for debugging purposes
- compiler->compInfoBlkAddr = gcInfoEncoder->Emit();
- compiler->compInfoBlkSize = 0; // not exposed by the GCEncoder interface
-}
-
/*****************************************************************************
* Emit a call to a helper function.
*