summaryrefslogtreecommitdiff
path: root/src/jit/codegenarmarch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/codegenarmarch.cpp')
-rw-r--r--src/jit/codegenarmarch.cpp834
1 files changed, 704 insertions, 130 deletions
diff --git a/src/jit/codegenarmarch.cpp b/src/jit/codegenarmarch.cpp
index c541472284..103ce47625 100644
--- a/src/jit/codegenarmarch.cpp
+++ b/src/jit/codegenarmarch.cpp
@@ -25,6 +25,382 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#include "emit.h"
//------------------------------------------------------------------------
+// genCodeForTreeNode Generate code for a single node in the tree.
+//
+// Preconditions:
+// All operands have been evaluated.
+//
+void CodeGen::genCodeForTreeNode(GenTreePtr treeNode)
+{
+ regNumber targetReg = treeNode->gtRegNum;
+ var_types targetType = treeNode->TypeGet();
+ emitter* emit = getEmitter();
+
+#ifdef DEBUG
+ // Validate that all the operands for the current node are consumed in order.
+ // This is important because LSRA ensures that any necessary copies will be
+ // handled correctly.
+ lastConsumedNode = nullptr;
+ if (compiler->verbose)
+ {
+ unsigned seqNum = treeNode->gtSeqNum; // Useful for setting a conditional break in Visual Studio
+ compiler->gtDispLIRNode(treeNode, "Generating: ");
+ }
+#endif // DEBUG
+
+#ifdef _TARGET_ARM64_ // TODO-ARM: is this applicable to ARM32?
+ // Is this a node whose value is already in a register? LSRA denotes this by
+ // setting the GTF_REUSE_REG_VAL flag.
+ if (treeNode->IsReuseRegVal())
+ {
+ // For now, this is only used for constant nodes.
+ assert((treeNode->OperGet() == GT_CNS_INT) || (treeNode->OperGet() == GT_CNS_DBL));
+ JITDUMP(" TreeNode is marked ReuseReg\n");
+ return;
+ }
+#endif // _TARGET_ARM64_
+
+ // contained nodes are part of their parents for codegen purposes
+ // ex : immediates, most LEAs
+ if (treeNode->isContained())
+ {
+ return;
+ }
+
+ switch (treeNode->gtOper)
+ {
+#ifdef _TARGET_ARM64_
+
+ case GT_START_NONGC:
+ getEmitter()->emitDisableGC();
+ break;
+
+ case GT_PROF_HOOK:
+ // We should be seeing this only if profiler hook is needed
+ noway_assert(compiler->compIsProfilerHookNeeded());
+
+#ifdef PROFILING_SUPPORTED
+ // Right now this node is used only for tail calls. In future if
+ // we intend to use it for Enter or Leave hooks, add a data member
+ // to this node indicating the kind of profiler hook. For example,
+ // helper number can be used.
+ genProfilingLeaveCallback(CORINFO_HELP_PROF_FCN_TAILCALL);
+#endif // PROFILING_SUPPORTED
+ break;
+
+#endif // _TARGET_ARM64_
+
+ case GT_LCLHEAP:
+ genLclHeap(treeNode);
+ break;
+
+ case GT_CNS_INT:
+ case GT_CNS_DBL:
+ genSetRegToConst(targetReg, targetType, treeNode);
+ genProduceReg(treeNode);
+ break;
+
+ case GT_NOT:
+ case GT_NEG:
+ genCodeForNegNot(treeNode);
+ break;
+
+ case GT_MOD:
+ case GT_UMOD:
+ case GT_DIV:
+ case GT_UDIV:
+ genCodeForDivMod(treeNode->AsOp());
+ break;
+
+ case GT_OR:
+ case GT_XOR:
+ case GT_AND:
+ assert(varTypeIsIntegralOrI(treeNode));
+
+ __fallthrough;
+
+#ifdef _TARGET_ARM_
+ case GT_ADD_LO:
+ case GT_ADD_HI:
+ case GT_SUB_LO:
+ case GT_SUB_HI:
+#endif // _TARGET_ARM_
+
+ case GT_ADD:
+ case GT_SUB:
+ case GT_MUL:
+ genConsumeOperands(treeNode->AsOp());
+ genCodeForBinary(treeNode);
+ break;
+
+ case GT_LSH:
+ case GT_RSH:
+ case GT_RSZ:
+ case GT_ROR:
+ genCodeForShift(treeNode);
+ break;
+
+#ifdef _TARGET_ARM_
+
+ case GT_LSH_HI:
+ case GT_RSH_LO:
+ genCodeForShiftLong(treeNode);
+ break;
+
+#endif // _TARGET_ARM_
+
+ case GT_CAST:
+ genCodeForCast(treeNode->AsOp());
+ break;
+
+ case GT_LCL_FLD_ADDR:
+ case GT_LCL_VAR_ADDR:
+ genCodeForLclAddr(treeNode);
+ break;
+
+ case GT_LCL_FLD:
+ genCodeForLclFld(treeNode->AsLclFld());
+ break;
+
+ case GT_LCL_VAR:
+ genCodeForLclVar(treeNode->AsLclVar());
+ break;
+
+ case GT_STORE_LCL_FLD:
+ genCodeForStoreLclFld(treeNode->AsLclFld());
+ break;
+
+ case GT_STORE_LCL_VAR:
+ genCodeForStoreLclVar(treeNode->AsLclVar());
+ break;
+
+ case GT_RETFILT:
+ case GT_RETURN:
+ genReturn(treeNode);
+ break;
+
+ case GT_LEA:
+ // if we are here, it is the case where there is an LEA that cannot
+ // be folded into a parent instruction
+ genLeaInstruction(treeNode->AsAddrMode());
+ break;
+
+ case GT_IND:
+ genCodeForIndir(treeNode->AsIndir());
+ break;
+
+#ifdef _TARGET_ARM64_
+
+ case GT_MULHI:
+ genCodeForMulHi(treeNode->AsOp());
+ break;
+
+ case GT_CKFINITE:
+ genCkfinite(treeNode);
+ break;
+
+ case GT_SWAP:
+ genCodeForSwap(treeNode->AsOp());
+ break;
+
+ case GT_JMP:
+ genJmpMethod(treeNode);
+ break;
+
+#endif // _TARGET_ARM64_
+
+ case GT_INTRINSIC:
+ genIntrinsic(treeNode);
+ break;
+
+#ifdef FEATURE_SIMD
+ case GT_SIMD:
+ genSIMDIntrinsic(treeNode->AsSIMD());
+ break;
+#endif // FEATURE_SIMD
+
+ case GT_EQ:
+ case GT_NE:
+ case GT_LT:
+ case GT_LE:
+ case GT_GE:
+ case GT_GT:
+ genCodeForCompare(treeNode->AsOp());
+ break;
+
+ case GT_JTRUE:
+ genCodeForJumpTrue(treeNode);
+ break;
+
+#ifdef _TARGET_ARM_
+
+ case GT_JCC:
+ genCodeForJcc(treeNode->AsJumpCC());
+ break;
+
+#endif // _TARGET_ARM_
+
+ case GT_RETURNTRAP:
+ genCodeForReturnTrap(treeNode->AsOp());
+ break;
+
+ case GT_STOREIND:
+ genCodeForStoreInd(treeNode->AsStoreInd());
+ break;
+
+ case GT_COPY:
+ // This is handled at the time we call genConsumeReg() on the GT_COPY
+ break;
+
+ case GT_LIST:
+ case GT_FIELD_LIST:
+ case GT_ARGPLACE:
+ // Nothing to do
+ break;
+
+ case GT_PUTARG_STK:
+ genPutArgStk(treeNode->AsPutArgStk());
+ break;
+
+ case GT_PUTARG_REG:
+ genPutArgReg(treeNode->AsOp());
+ break;
+
+ case GT_CALL:
+ genCallInstruction(treeNode->AsCall());
+ break;
+
+ case GT_LOCKADD:
+ case GT_XCHG:
+ case GT_XADD:
+ genLockedInstructions(treeNode->AsOp());
+ break;
+
+ case GT_MEMORYBARRIER:
+ instGen_MemoryBarrier();
+ break;
+
+ case GT_CMPXCHG:
+ NYI("GT_CMPXCHG");
+ break;
+
+ case GT_RELOAD:
+ // do nothing - reload is just a marker.
+ // The parent node will call genConsumeReg on this which will trigger the unspill of this node's child
+ // into the register specified in this node.
+ break;
+
+ case GT_NOP:
+ break;
+
+ case GT_NO_OP:
+ if (treeNode->gtFlags & GTF_NO_OP_NO)
+ {
+ noway_assert(!"GTF_NO_OP_NO should not be set");
+ }
+ else
+ {
+ instGen(INS_nop);
+ }
+ break;
+
+ case GT_ARR_BOUNDS_CHECK:
+#ifdef FEATURE_SIMD
+ case GT_SIMD_CHK:
+#endif // FEATURE_SIMD
+ genRangeCheck(treeNode);
+ break;
+
+ case GT_PHYSREG:
+ genCodeForPhysReg(treeNode->AsPhysReg());
+ break;
+
+ case GT_PHYSREGDST:
+ break;
+
+ case GT_NULLCHECK:
+ genCodeForNullCheck(treeNode->AsOp());
+ break;
+
+ case GT_CATCH_ARG:
+
+ noway_assert(handlerGetsXcptnObj(compiler->compCurBB->bbCatchTyp));
+
+ /* Catch arguments get passed in a register. genCodeForBBlist()
+ would have marked it as holding a GC object, but not used. */
+
+ noway_assert(gcInfo.gcRegGCrefSetCur & RBM_EXCEPTION_OBJECT);
+ genConsumeReg(treeNode);
+ break;
+
+ case GT_PINVOKE_PROLOG:
+ noway_assert(((gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur) & ~fullIntArgRegMask()) == 0);
+
+ // the runtime side requires the codegen here to be consistent
+ emit->emitDisableRandomNops();
+ break;
+
+ case GT_LABEL:
+ genPendingCallLabel = genCreateTempLabel();
+ treeNode->gtLabel.gtLabBB = genPendingCallLabel;
+#if defined(_TARGET_ARM_)
+ emit->emitIns_J_R(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg);
+#elif defined(_TARGET_ARM64_)
+ emit->emitIns_R_L(INS_adr, EA_PTRSIZE, genPendingCallLabel, targetReg);
+#endif
+ break;
+
+ case GT_STORE_OBJ:
+ case GT_STORE_DYN_BLK:
+ case GT_STORE_BLK:
+ genCodeForStoreBlk(treeNode->AsBlk());
+ break;
+
+ case GT_JMPTABLE:
+ genJumpTable(treeNode);
+ break;
+
+ case GT_SWITCH_TABLE:
+ genTableBasedSwitch(treeNode);
+ break;
+
+ case GT_ARR_INDEX:
+ genCodeForArrIndex(treeNode->AsArrIndex());
+ break;
+
+ case GT_ARR_OFFSET:
+ genCodeForArrOffset(treeNode->AsArrOffs());
+ break;
+
+#ifdef _TARGET_ARM_
+
+ case GT_CLS_VAR_ADDR:
+ emit->emitIns_R_C(INS_lea, EA_PTRSIZE, targetReg, treeNode->gtClsVar.gtClsVarHnd, 0);
+ genProduceReg(treeNode);
+ break;
+
+#endif // _TARGET_ARM_
+
+ case GT_IL_OFFSET:
+ // Do nothing; these nodes are simply markers for debug info.
+ break;
+
+ default:
+ {
+#ifdef DEBUG
+ char message[256];
+ _snprintf_s(message, _countof(message), _TRUNCATE, "NYI: Unimplemented node type %s",
+ GenTree::NodeName(treeNode->OperGet()));
+ NYIRAW(message);
+#else
+ NYI("unimplemented node");
+#endif
+ }
+ break;
+ }
+}
+
+//------------------------------------------------------------------------
// genSetRegToIcon: 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)
@@ -51,6 +427,8 @@ void CodeGen::genSetRegToIcon(regNumber reg, ssize_t val, var_types type, insFla
//
void CodeGen::genIntrinsic(GenTreePtr treeNode)
{
+ assert(treeNode->OperIs(GT_INTRINSIC));
+
// Both operand and its result must be of the same floating point type.
GenTreePtr srcNode = treeNode->gtOp.gtOp1;
assert(varTypeIsFloating(srcNode));
@@ -95,7 +473,7 @@ void CodeGen::genIntrinsic(GenTreePtr treeNode)
//
void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
{
- assert(treeNode->OperGet() == GT_PUTARG_STK);
+ assert(treeNode->OperIs(GT_PUTARG_STK));
var_types targetType = treeNode->TypeGet();
GenTreePtr source = treeNode->gtOp1;
emitter* emit = getEmitter();
@@ -284,6 +662,14 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
genConsumeAddress(addrNode);
addrReg = addrNode->gtRegNum;
+ // If addrReg equal to loReg, swap(loReg, hiReg)
+ // This reduces code complexity by only supporting one addrReg overwrite case
+ if (loReg == addrReg)
+ {
+ loReg = hiReg;
+ hiReg = addrReg;
+ }
+
CORINFO_CLASS_HANDLE objClass = source->gtObj.gtClass;
structSize = compiler->info.compCompHnd->getClassSize(objClass);
@@ -291,8 +677,6 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
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)
@@ -306,28 +690,9 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
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
+ // For a >= 16-byte structSize we will generate a ldp and stp instruction each loop
// 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)
- //
+ // stp x2, x3, [sp, #16]
int remainingSize = structSize;
unsigned structOffset = 0;
@@ -338,63 +703,26 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
var_types type0 = compiler->getJitGCType(gcPtrs[nextIndex + 0]);
var_types type1 = compiler->getJitGCType(gcPtrs[nextIndex + 1]);
- if (hasGCpointers)
+ if (varNode != nullptr)
{
- // 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);
- }
+ // Load from our varNumImp source
+ emit->emitIns_R_R_S_S(INS_ldp, emitTypeSize(type0), emitTypeSize(type1), loReg, hiReg, varNumInp,
+ 0);
}
- else // our struct has no GC pointers
+ else
{
- 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
+ // check for case of destroying the addrRegister while we still need it
+ assert(loReg != addrReg);
+ noway_assert((remainingSize == 2 * TARGET_POINTER_SIZE) || (hiReg != addrReg));
- // Load from our address expression source
- emit->emitIns_R_R_R_I(INS_ldp, EA_PTRSIZE, loReg, hiReg, addrReg, structOffset);
- }
+ // Load from our address expression source
+ emit->emitIns_R_R_R_I(INS_ldp, emitTypeSize(type0), loReg, hiReg, addrReg, structOffset,
+ 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
@@ -408,23 +736,9 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
// 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)
{
@@ -432,31 +746,23 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
{
remainingSize -= TARGET_POINTER_SIZE;
- if ((curReg == addrReg) && (remainingSize != 0))
+ if (varNode != nullptr)
{
- deferLoad = true;
- deferType = nextType;
- deferAttr = emitTypeSize(nextType);
- deferOffset = structOffset;
+ // Load from our varNumImp source
+ emit->emitIns_R_S(ins_Load(nextType), nextAttr, loReg, varNumInp, structOffset);
}
- else // the typical case
+ else
{
- 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
+ assert(loReg != addrReg);
+
+ // Load from our address expression source
+ emit->emitIns_R_R_I(ins_Load(nextType), nextAttr, loReg, addrReg, structOffset);
}
- curReg = hiReg;
+ // Emit a store instruction to store the register into the outgoing argument area
+ emit->emitIns_S_R(ins_Store(nextType), nextAttr, loReg, varNumOut, argOffsetOut);
+ argOffsetOut += EA_SIZE_IN_BYTES(nextAttr);
+ assert(argOffsetOut <= argOffsetMax); // We can't write beyound the outgoing area area
+
structOffset += TARGET_POINTER_SIZE;
nextIndex++;
nextType = compiler->getJitGCType(gcPtrs[nextIndex]);
@@ -491,39 +797,52 @@ void CodeGen::genPutArgStk(GenTreePutArgStk* treeNode)
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));
+ assert(loReg != addrReg);
- emit->emitIns_R_R_I(loadIns, loadAttr, curReg, addrReg, structOffset);
+ emit->emitIns_R_R_I(loadIns, loadAttr, loReg, 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);
+ emit->emitIns_S_R(ins_Store(loadType), loadAttr, loReg, 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);
+#endif // _TARGET_ARM64_
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// genPutArgReg - generate code for a GT_PUTARG_REG node
+//
+// Arguments
+// tree - the GT_PUTARG_REG node
+//
+// Return value:
+// None
+//
+void CodeGen::genPutArgReg(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_PUTARG_REG));
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
- curReg = addrReg;
+ // Any TYP_STRUCT register args should have been removed by fgMorphMultiregStructArg
+ assert(targetType != TYP_STRUCT);
- // Load from our address expression source
- emit->emitIns_R_R_I(ins_Load(deferType), deferAttr, curReg, addrReg, deferOffset);
+ // We have a normal non-Struct targetType
- // 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
- }
+ GenTree* op1 = tree->gtOp1;
+ genConsumeReg(op1);
-#endif // _TARGET_ARM64_
- }
+ // If child node is not already in the register we need, move it
+ if (targetReg != op1->gtRegNum)
+ {
+ inst_RV_RV(ins_Copy(targetType), targetReg, op1->gtRegNum, targetType);
}
+
+ genProduceReg(tree);
}
//----------------------------------------------------------------------------------
@@ -646,6 +965,54 @@ void CodeGen::genRangeCheck(GenTreePtr oper)
genJumpToThrowHlpBlk(jmpKind, SCK_RNGCHK_FAIL, bndsChk->gtIndRngFailBB);
}
+//---------------------------------------------------------------------
+// genCodeForPhysReg - generate code for a GT_PHYSREG node
+//
+// Arguments
+// tree - the GT_PHYSREG node
+//
+// Return value:
+// None
+//
+void CodeGen::genCodeForPhysReg(GenTreePhysReg* tree)
+{
+ assert(tree->OperIs(GT_PHYSREG));
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+
+ if (targetReg != tree->gtSrcReg)
+ {
+ inst_RV_RV(ins_Copy(targetType), targetReg, tree->gtSrcReg, targetType);
+ genTransferRegGCState(targetReg, tree->gtSrcReg);
+ }
+
+ genProduceReg(tree);
+}
+
+//---------------------------------------------------------------------
+// genCodeForNullCheck - generate code for a GT_NULLCHECK node
+//
+// Arguments
+// tree - the GT_NULLCHECK node
+//
+// Return value:
+// None
+//
+void CodeGen::genCodeForNullCheck(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_NULLCHECK));
+ assert(!tree->gtOp1->isContained());
+ regNumber addrReg = genConsumeReg(tree->gtOp1);
+
+#ifdef _TARGET_ARM64_
+ regNumber targetReg = REG_ZR;
+#else
+ regNumber targetReg = tree->gtRegNum;
+#endif
+
+ getEmitter()->emitIns_R_R_I(INS_ldr, EA_4BYTE, targetReg, addrReg, 0);
+}
+
//------------------------------------------------------------------------
// genOffsetOfMDArrayLowerBound: Returns the offset from the Array object to the
// lower bound for the given dimension.
@@ -853,6 +1220,137 @@ void CodeGen::genCodeForShift(GenTreePtr tree)
genProduceReg(tree);
}
+//------------------------------------------------------------------------
+// genCodeForCast: Generates the code for GT_CAST.
+//
+// Arguments:
+// tree - the GT_CAST node.
+//
+void CodeGen::genCodeForCast(GenTreeOp* tree)
+{
+ assert(tree->OperIs(GT_CAST));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+
+ // Cast is never contained (?)
+ noway_assert(targetReg != REG_NA);
+
+ if (varTypeIsFloating(targetType) && varTypeIsFloating(tree->gtOp1))
+ {
+ // Casts float/double <--> double/float
+ genFloatToFloatCast(tree);
+ }
+ else if (varTypeIsFloating(tree->gtOp1))
+ {
+ // Casts float/double --> int32/int64
+ genFloatToIntCast(tree);
+ }
+ else if (varTypeIsFloating(targetType))
+ {
+ // Casts int32/uint32/int64/uint64 --> float/double
+ genIntToFloatCast(tree);
+ }
+ else
+ {
+ // Casts int <--> int
+ genIntToIntCast(tree);
+ }
+ // The per-case functions call genProduceReg()
+}
+
+//------------------------------------------------------------------------
+// genCodeForLclAddr: Generates the code for GT_LCL_FLD_ADDR/GT_LCL_VAR_ADDR.
+//
+// Arguments:
+// tree - the node.
+//
+void CodeGen::genCodeForLclAddr(GenTree* tree)
+{
+ assert(tree->OperIs(GT_LCL_FLD_ADDR, GT_LCL_VAR_ADDR));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+
+ // Address of a local var. This by itself should never be allocated a register.
+ // If it is worth storing the address in a register then it should be cse'ed into
+ // a temp and that would be allocated a register.
+ noway_assert(targetType == TYP_BYREF);
+ noway_assert(!tree->InReg());
+
+ inst_RV_TT(INS_lea, targetReg, tree, 0, EA_BYREF);
+ genProduceReg(tree);
+}
+
+//------------------------------------------------------------------------
+// genCodeForLclFld: Produce code for a GT_LCL_FLD node.
+//
+// Arguments:
+// tree - the GT_LCL_FLD node
+//
+void CodeGen::genCodeForLclFld(GenTreeLclFld* tree)
+{
+ assert(tree->OperIs(GT_LCL_FLD));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+ emitter* emit = getEmitter();
+
+ NYI_IF(targetType == TYP_STRUCT, "GT_LCL_FLD: struct load local field not supported");
+ NYI_IF(targetReg == REG_NA, "GT_LCL_FLD: load local field not into a register is not supported");
+
+ emitAttr size = emitTypeSize(targetType);
+ unsigned offs = tree->gtLclOffs;
+ unsigned varNum = tree->gtLclNum;
+ assert(varNum < compiler->lvaCount);
+
+ if (varTypeIsFloating(targetType))
+ {
+ if (tree->InReg())
+ {
+ NYI("GT_LCL_FLD with register to register Floating point move");
+ }
+ else
+ {
+ emit->emitIns_R_S(ins_Load(targetType), size, targetReg, varNum, offs);
+ }
+ }
+ else
+ {
+#ifdef _TARGET_ARM64_
+ size = EA_SET_SIZE(size, EA_8BYTE);
+#endif // _TARGET_ARM64_
+ emit->emitIns_R_S(ins_Move_Extend(targetType, tree->InReg()), size, targetReg, varNum, offs);
+ }
+
+ genProduceReg(tree);
+}
+
+//------------------------------------------------------------------------
+// genCodeForIndir: Produce code for a GT_IND node.
+//
+// Arguments:
+// tree - the GT_IND node
+//
+void CodeGen::genCodeForIndir(GenTreeIndir* tree)
+{
+ assert(tree->OperIs(GT_IND));
+
+ var_types targetType = tree->TypeGet();
+ regNumber targetReg = tree->gtRegNum;
+ emitter* emit = getEmitter();
+
+ genConsumeAddress(tree->Addr());
+ emit->emitInsLoadStoreOp(ins_Load(targetType), emitTypeSize(tree), targetReg, tree);
+ genProduceReg(tree);
+
+ if (tree->gtFlags & GTF_IND_VOLATILE)
+ {
+ // issue a full memory barrier after a volatile LdInd operation
+ instGen_MemoryBarrier();
+ }
+}
+
// 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
@@ -873,7 +1371,19 @@ void CodeGen::genCodeForCpBlk(GenTreeBlk* cpBlkNode)
}
#endif // _TARGET_ARM64_
+ if (cpBlkNode->gtFlags & GTF_BLK_VOLATILE)
+ {
+ // issue a full memory barrier before & after a volatile CpBlkUnroll operation
+ instGen_MemoryBarrier();
+ }
+
genEmitHelperCall(CORINFO_HELP_MEMCPY, 0, EA_UNKNOWN);
+
+ if (cpBlkNode->gtFlags & GTF_BLK_VOLATILE)
+ {
+ // issue a full memory barrier before & after a volatile CpBlkUnroll operation
+ instGen_MemoryBarrier();
+ }
}
// Generates code for InitBlk by calling the VM memset helper function.
@@ -910,6 +1420,13 @@ void CodeGen::genCodeForInitBlk(GenTreeBlk* initBlkNode)
#endif // _TARGET_ARM64_
genConsumeBlockOp(initBlkNode, REG_ARG_0, REG_ARG_1, REG_ARG_2);
+
+ if (initBlkNode->gtFlags & GTF_BLK_VOLATILE)
+ {
+ // issue a full memory barrier before a volatile initBlock Operation
+ instGen_MemoryBarrier();
+ }
+
genEmitHelperCall(CORINFO_HELP_MEMSET, 0, EA_UNKNOWN);
}
@@ -1830,6 +2347,63 @@ void CodeGen::genCodeForJumpTrue(GenTreePtr tree)
}
}
+//------------------------------------------------------------------------
+// genCodeForStoreBlk: Produce code for a GT_STORE_OBJ/GT_STORE_DYN_BLK/GT_STORE_BLK node.
+//
+// Arguments:
+// tree - the node
+//
+void CodeGen::genCodeForStoreBlk(GenTreeBlk* blkOp)
+{
+ assert(blkOp->OperIs(GT_STORE_OBJ, GT_STORE_DYN_BLK, GT_STORE_BLK));
+
+ if (blkOp->OperIs(GT_STORE_OBJ) && blkOp->OperIsCopyBlkOp())
+ {
+ assert(blkOp->AsObj()->gtGcPtrCount != 0);
+ genCodeForCpObj(blkOp->AsObj());
+ return;
+ }
+
+ if (blkOp->gtBlkOpGcUnsafe)
+ {
+ getEmitter()->emitDisableGC();
+ }
+ bool isCopyBlk = blkOp->OperIsCopyBlkOp();
+
+ switch (blkOp->gtBlkOpKind)
+ {
+ case GenTreeBlk::BlkOpKindHelper:
+ if (isCopyBlk)
+ {
+ genCodeForCpBlk(blkOp);
+ }
+ else
+ {
+ genCodeForInitBlk(blkOp);
+ }
+ break;
+
+ case GenTreeBlk::BlkOpKindUnroll:
+ if (isCopyBlk)
+ {
+ genCodeForCpBlkUnroll(blkOp);
+ }
+ else
+ {
+ genCodeForInitBlkUnroll(blkOp);
+ }
+ break;
+
+ default:
+ unreached();
+ }
+
+ if (blkOp->gtBlkOpGcUnsafe)
+ {
+ getEmitter()->emitEnableGC();
+ }
+}
+
#endif // _TARGET_ARMARCH_
#endif // !LEGACY_BACKEND