diff options
Diffstat (limited to 'src/jit/codegenarmarch.cpp')
-rw-r--r-- | src/jit/codegenarmarch.cpp | 834 |
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 |