FORCEINLINE void Compiler::eeGetMethodSig(CORINFO_METHOD_HANDLE methHnd, CORINFO_SIG_INFO* sigRet, CORINFO_CLASS_HANDLE owner) { info.compCompHnd->getMethodSig(methHnd, sigRet, owner); assert(!varTypeIsComposite(JITtype2varType(sigRet->retType)) || sigRet->retTypeClass != nullptr); }
inline var_types Compiler::eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig, bool* isPinned) { CORINFO_CLASS_HANDLE argClass; CorInfoTypeWithMod type = info.compCompHnd->getArgType(sig, list, &argClass); *isPinned = ((type & ~CORINFO_TYPE_MASK) != 0); return JITtype2varType(strip(type)); }
void Rationalizer::RewriteNodeAsCall(GenTree** use, ArrayStack<GenTree*>& parents, CORINFO_METHOD_HANDLE callHnd, #ifdef FEATURE_READYTORUN_COMPILER CORINFO_CONST_LOOKUP entryPoint, #endif GenTreeArgList* args) { GenTree* const tree = *use; GenTree* const treeFirstNode = comp->fgGetFirstNode(tree); GenTree* const insertionPoint = treeFirstNode->gtPrev; BlockRange().Remove(treeFirstNode, tree); // Create the call node GenTreeCall* call = comp->gtNewCallNode(CT_USER_FUNC, callHnd, tree->gtType, args); #if DEBUG CORINFO_SIG_INFO sig; comp->eeGetMethodSig(callHnd, &sig); assert(JITtype2varType(sig.retType) == tree->gtType); #endif // DEBUG call = comp->fgMorphArgs(call); // Determine if this call has changed any codegen requirements. comp->fgCheckArgCnt(); #ifdef FEATURE_READYTORUN_COMPILER call->gtCall.setEntryPoint(entryPoint); #endif // Replace "tree" with "call" if (parents.Height() > 1) { parents.Index(1)->ReplaceOperand(use, call); } else { // If there's no parent, the tree being replaced is the root of the // statement (and no special handling is necessary). *use = call; } comp->gtSetEvalOrder(call); BlockRange().InsertAfter(insertionPoint, LIR::Range(comp->fgSetTreeSeq(call), call)); // Propagate flags of "call" to its parents. // 0 is current node, so start at 1 for (int i = 1; i < parents.Height(); i++) { parents.Index(i)->gtFlags |= (call->gtFlags & GTF_ALL_EFFECT) | GTF_CALL; } // Since "tree" is replaced with "call", pop "tree" node (i.e the current node) // and replace it with "call" on parent stack. assert(parents.Top() == tree); (void)parents.Pop(); parents.Push(call); }
FORCEINLINE void Compiler::eeGetCallSiteSig(unsigned sigTok, CORINFO_MODULE_HANDLE scope, CORINFO_CONTEXT_HANDLE context, CORINFO_SIG_INFO* sigRet) { info.compCompHnd->findCallSiteSig(scope, sigTok, context, sigRet); assert(!varTypeIsComposite(JITtype2varType(sigRet->retType)) || sigRet->retTypeClass != nullptr); }
inline var_types Compiler::eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig) { CORINFO_CLASS_HANDLE argClass; return (JITtype2varType(strip(info.compCompHnd->getArgType(sig, list, &argClass)))); }
void Rationalizer::RewriteNodeAsCall(GenTree** use, Compiler::fgWalkData* data, CORINFO_METHOD_HANDLE callHnd, #ifdef FEATURE_READYTORUN_COMPILER CORINFO_CONST_LOOKUP entryPoint, #endif GenTreeArgList* args) { GenTreePtr tree = *use; Compiler* comp = data->compiler; SplitData* tmpState = (SplitData*)data->pCallbackData; GenTreePtr root = tmpState->root; GenTreePtr treeFirstNode = comp->fgGetFirstNode(tree); GenTreePtr treeLastNode = tree; GenTreePtr treePrevNode = treeFirstNode->gtPrev; GenTreePtr treeNextNode = treeLastNode->gtNext; // Create the call node GenTreeCall* call = comp->gtNewCallNode(CT_USER_FUNC, callHnd, tree->gtType, args); #if DEBUG CORINFO_SIG_INFO sig; comp->eeGetMethodSig(callHnd, &sig); assert(JITtype2varType(sig.retType) == tree->gtType); #endif // DEBUG call = comp->fgMorphArgs(call); // Determine if this call has changed any codegen requirements. comp->fgCheckArgCnt(); #ifdef FEATURE_READYTORUN_COMPILER call->gtCall.setEntryPoint(entryPoint); #endif // Replace "tree" with "call" if (data->parentStack->Height() > 1) { data->parentStack->Index(1)->ReplaceOperand(use, call); } else { // If there's no parent, the tree being replaced is the root of the // statement (and no special handling is necessary). *use = call; } // Rebuild the evaluation order. comp->gtSetStmtInfo(root); // Rebuild the execution order. comp->fgSetTreeSeq(call, treePrevNode); // Restore linear-order Prev and Next for "call". if (treePrevNode) { treeFirstNode = comp->fgGetFirstNode(call); treeFirstNode->gtPrev = treePrevNode; treePrevNode->gtNext = treeFirstNode; } else { // Update the linear oder start of "root" if treeFirstNode // appears to have replaced the original first node. assert(treeFirstNode == root->gtStmt.gtStmtList); root->gtStmt.gtStmtList = comp->fgGetFirstNode(call); } if (treeNextNode) { treeLastNode = call; treeLastNode->gtNext = treeNextNode; treeNextNode->gtPrev = treeLastNode; } // Propagate flags of "call" to its parents. // 0 is current node, so start at 1 for (int i = 1; i < data->parentStack->Height(); i++) { GenTree* node = data->parentStack->Index(i); node->gtFlags |= GTF_CALL; node->gtFlags |= call->gtFlags & GTF_ALL_EFFECT; } // Since "tree" is replaced with "call", pop "tree" node (i.e the current node) // and replace it with "call" on parent stack. assert(data->parentStack->Top() == tree); (void)data->parentStack->Pop(); data->parentStack->Push(call); }
//------------------------------------------------------------------------ // impHWIntrinsic: dispatch hardware intrinsics to their own implementation // function // // Arguments: // intrinsic -- id of the intrinsic function. // method -- method handle of the intrinsic function. // sig -- signature of the intrinsic call // // Return Value: // the expanded intrinsic. // GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic, CORINFO_METHOD_HANDLE method, CORINFO_SIG_INFO* sig, bool mustExpand) { GenTree* retNode = nullptr; GenTree* op1 = nullptr; GenTree* op2 = nullptr; GenTree* op3 = nullptr; CORINFO_CLASS_HANDLE simdClass = nullptr; var_types simdType = TYP_UNKNOWN; var_types simdBaseType = TYP_UNKNOWN; unsigned simdSizeBytes = 0; switch (HWIntrinsicInfo::lookup(intrinsic).form) { case HWIntrinsicInfo::SimdBinaryOp: case HWIntrinsicInfo::SimdInsertOp: case HWIntrinsicInfo::SimdSelectOp: case HWIntrinsicInfo::SimdSetAllOp: case HWIntrinsicInfo::SimdUnaryOp: case HWIntrinsicInfo::SimdBinaryRMWOp: case HWIntrinsicInfo::SimdTernaryRMWOp: case HWIntrinsicInfo::Sha1HashOp: simdClass = sig->retTypeClass; break; case HWIntrinsicInfo::SimdExtractOp: info.compCompHnd->getArgType(sig, sig->args, &simdClass); break; default: break; } // Simd instantiation type check if (simdClass != nullptr) { compFloatingPointUsed = true; simdBaseType = getBaseTypeAndSizeOfSIMDType(simdClass, &simdSizeBytes); if (simdBaseType == TYP_UNKNOWN) { return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_TYPE_NOT_SUPPORTED, method, sig, mustExpand); } simdType = getSIMDTypeForSize(simdSizeBytes); } switch (HWIntrinsicInfo::lookup(intrinsic).form) { case HWIntrinsicInfo::IsSupported: return gtNewIconNode((intrinsic == NI_ARM64_IsSupported_True) ? 1 : 0); case HWIntrinsicInfo::Unsupported: return impUnsupportedHWIntrinsic(CORINFO_HELP_THROW_PLATFORM_NOT_SUPPORTED, method, sig, mustExpand); case HWIntrinsicInfo::UnaryOp: op1 = impPopStack().val; return gtNewScalarHWIntrinsicNode(JITtype2varType(sig->retType), op1, intrinsic); case HWIntrinsicInfo::SimdBinaryOp: case HWIntrinsicInfo::SimdBinaryRMWOp: // op1 is the first operand // op2 is the second operand op2 = impSIMDPopStack(simdType); op1 = impSIMDPopStack(simdType); return gtNewSimdHWIntrinsicNode(simdType, op1, op2, intrinsic, simdBaseType, simdSizeBytes); case HWIntrinsicInfo::SimdTernaryRMWOp: case HWIntrinsicInfo::SimdSelectOp: // op1 is the first operand // op2 is the second operand // op3 is the third operand op3 = impSIMDPopStack(simdType); op2 = impSIMDPopStack(simdType); op1 = impSIMDPopStack(simdType); return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes); case HWIntrinsicInfo::SimdSetAllOp: op1 = impPopStack().val; return gtNewSimdHWIntrinsicNode(simdType, op1, intrinsic, simdBaseType, simdSizeBytes); case HWIntrinsicInfo::SimdUnaryOp: op1 = impSIMDPopStack(simdType); return gtNewSimdHWIntrinsicNode(simdType, op1, intrinsic, simdBaseType, simdSizeBytes); case HWIntrinsicInfo::SimdExtractOp: if (!mustExpand && !impCheckImmediate(impStackTop(0).val, getSIMDVectorLength(simdSizeBytes, simdBaseType))) { // Immediate lane not constant or out of range return nullptr; } op2 = impPopStack().val; op1 = impSIMDPopStack(simdType); return gtNewScalarHWIntrinsicNode(JITtype2varType(sig->retType), op1, op2, intrinsic); case HWIntrinsicInfo::SimdInsertOp: if (!mustExpand && !impCheckImmediate(impStackTop(1).val, getSIMDVectorLength(simdSizeBytes, simdBaseType))) { // Immediate lane not constant or out of range return nullptr; } op3 = impPopStack().val; op2 = impPopStack().val; op1 = impSIMDPopStack(simdType); return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes); case HWIntrinsicInfo::Sha1HashOp: op3 = impSIMDPopStack(simdType); op2 = impPopStack().val; op1 = impSIMDPopStack(simdType); return gtNewSimdHWIntrinsicNode(simdType, op1, op2, op3, intrinsic, simdBaseType, simdSizeBytes); case HWIntrinsicInfo::Sha1RotateOp: assert(sig->numArgs == 1); compFloatingPointUsed = true; return gtNewScalarHWIntrinsicNode(TYP_UINT, impPopStack().val, NI_ARM64_Sha1FixedRotate); default: JITDUMP("Not implemented hardware intrinsic form"); assert(!"Unimplemented SIMD Intrinsic form"); break; } return retNode; }