summaryrefslogtreecommitdiff
path: root/src/jit/codegenxarch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/codegenxarch.cpp')
-rw-r--r--src/jit/codegenxarch.cpp173
1 files changed, 173 insertions, 0 deletions
diff --git a/src/jit/codegenxarch.cpp b/src/jit/codegenxarch.cpp
index 01ba9c2647..1e99da45bd 100644
--- a/src/jit/codegenxarch.cpp
+++ b/src/jit/codegenxarch.cpp
@@ -7331,6 +7331,173 @@ void CodeGen::genSSE2BitwiseOp(GenTreePtr treeNode)
inst_RV_RV(ins, targetReg, operandReg, targetType);
}
+//-----------------------------------------------------------------------------------------
+// genSSE41RoundOp - generate SSE41 code for the given tree as a round operation
+//
+// Arguments:
+// treeNode - tree node
+//
+// Return value:
+// None
+//
+// Assumptions:
+// i) SSE4.1 is supported by the underlying hardware
+// ii) treeNode oper is a GT_INTRINSIC
+// iii) treeNode type is a floating point type
+// iv) treeNode is not used from memory
+// v) tree oper is CORINFO_INTRINSIC_Round, _Ceiling, or _Floor
+// vi) caller of this routine needs to call genProduceReg()
+void CodeGen::genSSE41RoundOp(GenTreeOp* treeNode)
+{
+ // i) SSE4.1 is supported by the underlying hardware
+ assert(compiler->compSupports(InstructionSet_SSE41));
+
+ // ii) treeNode oper is a GT_INTRINSIC
+ assert(treeNode->OperGet() == GT_INTRINSIC);
+
+ GenTree* srcNode = treeNode->gtGetOp1();
+
+ // iii) treeNode type is floating point type
+ assert(varTypeIsFloating(srcNode));
+ assert(srcNode->TypeGet() == treeNode->TypeGet());
+
+ // iv) treeNode is not used from memory
+ assert(!treeNode->isUsedFromMemory());
+
+ genConsumeOperands(treeNode);
+
+ instruction ins = (treeNode->TypeGet() == TYP_FLOAT) ? INS_roundss : INS_roundsd;
+ emitAttr size = emitTypeSize(treeNode);
+
+ regNumber dstReg = treeNode->gtRegNum;
+
+ unsigned ival = 0;
+
+ // v) tree oper is CORINFO_INTRINSIC_Round, _Ceiling, or _Floor
+ switch (treeNode->gtIntrinsic.gtIntrinsicId)
+ {
+ case CORINFO_INTRINSIC_Round:
+ ival = 4;
+ break;
+
+ case CORINFO_INTRINSIC_Ceiling:
+ ival = 10;
+ break;
+
+ case CORINFO_INTRINSIC_Floor:
+ ival = 9;
+ break;
+
+ default:
+ ins = INS_invalid;
+ assert(!"genSSE41RoundOp: unsupported intrinsic");
+ unreached();
+ }
+
+ if (srcNode->isContained() || srcNode->isUsedFromSpillTemp())
+ {
+ emitter* emit = getEmitter();
+
+ TempDsc* tmpDsc = nullptr;
+ unsigned varNum = BAD_VAR_NUM;
+ unsigned offset = (unsigned)-1;
+
+ if (srcNode->isUsedFromSpillTemp())
+ {
+ assert(srcNode->IsRegOptional());
+
+ tmpDsc = getSpillTempDsc(srcNode);
+ varNum = tmpDsc->tdTempNum();
+ offset = 0;
+
+ compiler->tmpRlsTemp(tmpDsc);
+ }
+ else if (srcNode->isIndir())
+ {
+ GenTreeIndir* memIndir = srcNode->AsIndir();
+ GenTree* memBase = memIndir->gtOp1;
+
+ switch (memBase->OperGet())
+ {
+ case GT_LCL_VAR_ADDR:
+ {
+ varNum = memBase->AsLclVarCommon()->GetLclNum();
+ offset = 0;
+
+ // Ensure that all the GenTreeIndir values are set to their defaults.
+ assert(memBase->gtRegNum == REG_NA);
+ assert(!memIndir->HasIndex());
+ assert(memIndir->Scale() == 1);
+ assert(memIndir->Offset() == 0);
+
+ break;
+ }
+
+ case GT_CLS_VAR_ADDR:
+ {
+ emit->emitIns_R_C_I(ins, size, dstReg, memBase->gtClsVar.gtClsVarHnd, 0, ival);
+ return;
+ }
+
+ default:
+ {
+ emit->emitIns_R_A_I(ins, size, dstReg, memIndir, ival);
+ return;
+ }
+ }
+ }
+ else
+ {
+ switch (srcNode->OperGet())
+ {
+ case GT_CNS_DBL:
+ {
+ GenTreeDblCon* dblConst = srcNode->AsDblCon();
+ CORINFO_FIELD_HANDLE hnd = emit->emitFltOrDblConst(dblConst->gtDconVal, emitTypeSize(dblConst));
+
+ emit->emitIns_R_C_I(ins, size, dstReg, hnd, 0, ival);
+ return;
+ }
+
+ case GT_LCL_FLD:
+ {
+ GenTreeLclFld* lclField = srcNode->AsLclFld();
+
+ varNum = lclField->GetLclNum();
+ offset = lclField->gtLclFld.gtLclOffs;
+ break;
+ }
+
+ case GT_LCL_VAR:
+ {
+ assert(srcNode->IsRegOptional() ||
+ !compiler->lvaTable[srcNode->gtLclVar.gtLclNum].lvIsRegCandidate());
+
+ varNum = srcNode->AsLclVar()->GetLclNum();
+ offset = 0;
+ break;
+ }
+
+ default:
+ unreached();
+ break;
+ }
+ }
+
+ // Ensure we got a good varNum and offset.
+ // We also need to check for `tmpDsc != nullptr` since spill temp numbers
+ // are negative and start with -1, which also happens to be BAD_VAR_NUM.
+ assert((varNum != BAD_VAR_NUM) || (tmpDsc != nullptr));
+ assert(offset != (unsigned)-1);
+
+ emit->emitIns_R_S_I(ins, size, dstReg, varNum, offset, ival);
+ }
+ else
+ {
+ inst_RV_RV_IV(ins, size, dstReg, srcNode->gtRegNum, ival);
+ }
+}
+
//---------------------------------------------------------------------
// genIntrinsic - generate code for a given intrinsic
//
@@ -7361,6 +7528,12 @@ void CodeGen::genIntrinsic(GenTreePtr treeNode)
genSSE2BitwiseOp(treeNode);
break;
+ case CORINFO_INTRINSIC_Round:
+ case CORINFO_INTRINSIC_Ceiling:
+ case CORINFO_INTRINSIC_Floor:
+ genSSE41RoundOp(treeNode->AsOp());
+ break;
+
default:
assert(!"genIntrinsic: Unsupported intrinsic");
unreached();