diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index 5130c79983241..2af1b6bba3317 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -1250,6 +1250,7 @@ class CodeGen final : public CodeGenInterface void genCall(GenTreeCall* call); void genCallInstruction(GenTreeCall* call X86_ARG(target_ssize_t stackArgBytes)); void genDefinePendingCallLabel(GenTreeCall* call); + void genCallPlaceRegArgs(GenTreeCall* call); void genJmpPlaceArgs(GenTree* jmp); void genJmpPlaceVarArgs(); BasicBlock* genCallFinally(BasicBlock* block); diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 6b3b077f7b740..d89892b8898c5 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -3264,60 +3264,7 @@ void CodeGen::genCodeForInitBlkLoop(GenTreeBlk* initBlkNode) // void CodeGen::genCall(GenTreeCall* call) { - // Consume all the arg regs - for (CallArg& arg : call->gtArgs.LateArgs()) - { - CallArgABIInformation& abiInfo = arg.AbiInfo; - GenTree* argNode = arg.GetLateNode(); - - if (abiInfo.GetRegNum() == REG_STK) - continue; - - // Deal with multi register passed struct args. - if (argNode->OperGet() == GT_FIELD_LIST) - { - regNumber argReg = abiInfo.GetRegNum(); - for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) - { - GenTree* putArgRegNode = use.GetNode(); - assert(putArgRegNode->gtOper == GT_PUTARG_REG); - - genConsumeReg(putArgRegNode); - inst_Mov_Extend(putArgRegNode->TypeGet(), /* srcInReg */ true, argReg, putArgRegNode->GetRegNum(), - /* canSkip */ true, emitActualTypeSize(TYP_I_IMPL)); - - argReg = genRegArgNext(argReg); - -#if defined(TARGET_ARM) - // A double register is modelled as an even-numbered single one - if (putArgRegNode->TypeGet() == TYP_DOUBLE) - { - argReg = genRegArgNext(argReg); - } -#endif // TARGET_ARM - } - } - else if (abiInfo.IsSplit()) - { - assert(compFeatureArgSplit()); - assert(abiInfo.NumRegs >= 1); - genConsumeArgSplitStruct(argNode->AsPutArgSplit()); - for (unsigned idx = 0; idx < abiInfo.NumRegs; idx++) - { - regNumber argReg = (regNumber)((unsigned)abiInfo.GetRegNum() + idx); - regNumber allocReg = argNode->AsPutArgSplit()->GetRegNumByIdx(idx); - inst_Mov_Extend(argNode->TypeGet(), /* srcInReg */ true, argReg, allocReg, /* canSkip */ true, - emitActualTypeSize(TYP_I_IMPL)); - } - } - else - { - regNumber argReg = abiInfo.GetRegNum(); - genConsumeReg(argNode); - inst_Mov_Extend(argNode->TypeGet(), /* srcInReg */ true, argReg, argNode->GetRegNum(), /* canSkip */ true, - emitActualTypeSize(TYP_I_IMPL)); - } - } + genCallPlaceRegArgs(call); // Insert a null check on "this" pointer if asked. if (call->NeedsNullCheck()) @@ -3548,14 +3495,14 @@ void CodeGen::genCallInstruction(GenTreeCall* call) for (CallArg& arg : call->gtArgs.Args()) { - for (unsigned j = 0; j < arg.AbiInfo.NumRegs; j++) + for (unsigned i = 0; i < arg.NewAbiInfo.NumSegments; i++) { - regNumber reg = arg.AbiInfo.GetRegNum(j); - if ((trashedByEpilog & genRegMask(reg)) != 0) + const ABIPassingSegment& seg = arg.NewAbiInfo.Segment(i); + if (seg.IsPassedInRegister() && ((trashedByEpilog & seg.GetRegisterMask()) != 0)) { JITDUMP("Tail call node:\n"); DISPTREE(call); - JITDUMP("Register used: %s\n", getRegName(reg)); + JITDUMP("Register used: %s\n", getRegName(seg.GetRegister())); assert(!"Argument to tailcall may be trashed by epilog"); } } diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 71459dbb497ba..4a0d11282c5b3 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -7444,6 +7444,109 @@ void CodeGen::genStructReturn(GenTree* treeNode) #endif } +//------------------------------------------------------------------------ +// genCallPlaceRegArgs: Place all arguments into their initial (ABI-decided) +// registers in preparation for a GT_CALL node. +// +// Arguments: +// call - The GT_CALL node +// +void CodeGen::genCallPlaceRegArgs(GenTreeCall* call) +{ + // Consume all the arg regs + for (CallArg& arg : call->gtArgs.LateArgs()) + { + ABIPassingInformation& abiInfo = arg.NewAbiInfo; + GenTree* argNode = arg.GetLateNode(); + +#if FEATURE_MULTIREG_ARGS + // Deal with multi register passed struct args. + if (argNode->OperIs(GT_FIELD_LIST)) + { + GenTreeFieldList::Use* use = argNode->AsFieldList()->Uses().begin().GetUse(); + for (unsigned i = 0; i < abiInfo.NumSegments; i++) + { + const ABIPassingSegment& seg = abiInfo.Segment(i); + if (!seg.IsPassedInRegister()) + { + continue; + } + + assert(use != nullptr); + GenTree* putArgRegNode = use->GetNode(); + assert(putArgRegNode->OperIs(GT_PUTARG_REG)); + + genConsumeReg(putArgRegNode); + inst_Mov(genActualType(putArgRegNode), seg.GetRegister(), putArgRegNode->GetRegNum(), + /* canSkip */ true); + + use = use->GetNext(); + } + + assert(use == nullptr); + continue; + } +#endif + +#if FEATURE_ARG_SPLIT + if (argNode->OperIs(GT_PUTARG_SPLIT)) + { + assert(compFeatureArgSplit()); + genConsumeArgSplitStruct(argNode->AsPutArgSplit()); + unsigned regIndex = 0; + for (unsigned i = 0; i < abiInfo.NumSegments; i++) + { + const ABIPassingSegment& seg = abiInfo.Segment(i); + if (!seg.IsPassedInRegister()) + { + continue; + } + + regNumber allocReg = argNode->AsPutArgSplit()->GetRegNumByIdx(regIndex); + var_types type = argNode->AsPutArgSplit()->GetRegType(regIndex); + inst_Mov(genActualType(type), seg.GetRegister(), allocReg, /* canSkip */ true); + + regIndex++; + } + + continue; + } +#endif + + if (abiInfo.HasExactlyOneRegisterSegment()) + { + regNumber argReg = abiInfo.Segment(0).GetRegister(); + genConsumeReg(argNode); + inst_Mov(genActualType(argNode), argReg, argNode->GetRegNum(), /* canSkip */ true); + continue; + } + + // Should be a stack argument then. + assert(!abiInfo.HasAnyRegisterSegment()); + } + +#ifdef WINDOWS_AMD64_ABI + // On win-x64, for varargs, if we placed any arguments in float registers + // they must also be placed in corresponding integer registers. + if (call->IsVarargs()) + { + for (CallArg& arg : call->gtArgs.Args()) + { + for (unsigned i = 0; i < arg.NewAbiInfo.NumSegments; i++) + { + const ABIPassingSegment& seg = arg.NewAbiInfo.Segment(i); + if (seg.IsPassedInRegister() && genIsValidFloatReg(seg.GetRegister())) + { + regNumber targetReg = compiler->getCallArgIntRegister(seg.GetRegister()); + inst_Mov(TYP_LONG, targetReg, seg.GetRegister(), /* canSkip */ false, + emitActualTypeSize(TYP_I_IMPL)); + } + } + } + } +#endif +} + //------------------------------------------------------------------------ // genJmpPlaceArgs: Place all parameters into their initial (ABI-decided) // registers in preparation for a GT_JMP node. diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index 1ffbff4e82936..7717df84494d1 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -6187,57 +6187,7 @@ void CodeGen::genCodeForInitBlkLoop(GenTreeBlk* initBlkNode) // void CodeGen::genCall(GenTreeCall* call) { - // Consume all the arg regs - for (CallArg& arg : call->gtArgs.LateArgs()) - { - CallArgABIInformation& abiInfo = arg.AbiInfo; - GenTree* argNode = arg.GetLateNode(); - - // GT_RELOAD/GT_COPY use the child node - argNode = argNode->gtSkipReloadOrCopy(); - - if (abiInfo.GetRegNum() == REG_STK) - { - continue; - } - - // Deal with multi register passed struct args. - if (argNode->OperGet() == GT_FIELD_LIST) - { - for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) - { - GenTree* putArgRegNode = use.GetNode(); - assert(putArgRegNode->gtOper == GT_PUTARG_REG); - - genConsumeReg(putArgRegNode); - } - } - else if (abiInfo.IsSplit()) - { - assert(compFeatureArgSplit()); - - GenTreePutArgSplit* splitNode = argNode->AsPutArgSplit(); - genConsumeArgSplitStruct(splitNode); - - regNumber argReg = abiInfo.GetRegNum(); - regNumber allocReg = splitNode->GetRegNumByIdx(0); - var_types regType = splitNode->GetRegType(0); - - // For LA64's ABI, the split is only using the A7 and stack for passing arg. - assert(argReg == REG_A7); - assert(emitter::isGeneralRegister(allocReg)); - assert(abiInfo.NumRegs == 1); - - inst_Mov(regType, argReg, allocReg, /* canSkip */ true); - } - else - { - regNumber argReg = abiInfo.GetRegNum(); - genConsumeReg(argNode); - var_types dstType = emitter::isFloatReg(argReg) ? TYP_DOUBLE : argNode->TypeGet(); - inst_Mov(dstType, argReg, argNode->GetRegNum(), /* canSkip */ true); - } - } + genCallPlaceRegArgs(call); // Insert a null check on "this" pointer if asked. if (call->NeedsNullCheck()) @@ -6431,14 +6381,14 @@ void CodeGen::genCallInstruction(GenTreeCall* call) for (CallArg& arg : call->gtArgs.Args()) { - for (unsigned j = 0; j < arg.AbiInfo.NumRegs; j++) + for (unsigned i = 0; i < arg.NewAbiInfo.NumSegments; i++) { - regNumber reg = arg.AbiInfo.GetRegNum(j); - if ((trashedByEpilog & genRegMask(reg)) != 0) + const ABIPassingSegment& seg = arg.NewAbiInfo.Segment(i); + if (seg.IsPassedInRegister() && ((trashedByEpilog & seg.GetRegisterMask()) != 0)) { JITDUMP("Tail call node:\n"); DISPTREE(call); - JITDUMP("Register used: %s\n", getRegName(reg)); + JITDUMP("Register used: %s\n", getRegName(seg.GetRegister())); assert(!"Argument to tailcall may be trashed by epilog"); } } diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 43799a7c7d2f3..c3caa821b830d 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -6314,57 +6314,7 @@ void CodeGen::genCodeForInitBlkLoop(GenTreeBlk* initBlkNode) // void CodeGen::genCall(GenTreeCall* call) { - // Consume all the arg regs - for (CallArg& arg : call->gtArgs.LateArgs()) - { - CallArgABIInformation& abiInfo = arg.AbiInfo; - GenTree* argNode = arg.GetLateNode(); - - // GT_RELOAD/GT_COPY use the child node - argNode = argNode->gtSkipReloadOrCopy(); - - if (abiInfo.GetRegNum() == REG_STK) - { - continue; - } - - // Deal with multi register passed struct args. - if (argNode->OperGet() == GT_FIELD_LIST) - { - for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) - { - GenTree* putArgRegNode = use.GetNode(); - assert(putArgRegNode->gtOper == GT_PUTARG_REG); - - genConsumeReg(putArgRegNode); - } - } - else if (abiInfo.IsSplit()) - { - assert(compFeatureArgSplit()); - - GenTreePutArgSplit* splitNode = argNode->AsPutArgSplit(); - genConsumeArgSplitStruct(splitNode); - - regNumber argReg = abiInfo.GetRegNum(); - regNumber allocReg = splitNode->GetRegNumByIdx(0); - var_types regType = splitNode->GetRegType(0); - - // For RISCV64's ABI, the split is only using the A7 and stack for passing arg. - assert(argReg == REG_A7); - assert(emitter::isGeneralRegister(allocReg)); - assert(abiInfo.NumRegs == 1); - - inst_Mov(regType, argReg, allocReg, /* canSkip */ true); - } - else - { - regNumber argReg = abiInfo.GetRegNum(); - genConsumeReg(argNode); - var_types dstType = emitter::isFloatReg(argReg) ? TYP_DOUBLE : argNode->TypeGet(); - inst_Mov(dstType, argReg, argNode->GetRegNum(), /* canSkip */ true); - } - } + genCallPlaceRegArgs(call); // Insert a null check on "this" pointer if asked. if (call->NeedsNullCheck()) @@ -6558,14 +6508,14 @@ void CodeGen::genCallInstruction(GenTreeCall* call) for (CallArg& arg : call->gtArgs.Args()) { - for (unsigned j = 0; j < arg.AbiInfo.NumRegs; j++) + for (unsigned i = 0; i < arg.NewAbiInfo.NumSegments; i++) { - regNumber reg = arg.AbiInfo.GetRegNum(j); - if ((trashedByEpilog & genRegMask(reg)) != 0) + const ABIPassingSegment& seg = arg.NewAbiInfo.Segment(i); + if (seg.IsPassedInRegister() && ((trashedByEpilog & seg.GetRegisterMask()) != 0)) { JITDUMP("Tail call node:\n"); DISPTREE(call); - JITDUMP("Register used: %s\n", getRegName(reg)); + JITDUMP("Register used: %s\n", getRegName(seg.GetRegister())); assert(!"Argument to tailcall may be trashed by epilog"); } } diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 1eb90747bc80f..4484343a1e058 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -5938,56 +5938,7 @@ void CodeGen::genCall(GenTreeCall* call) } } - // Consume all the arg regs - for (CallArg& arg : call->gtArgs.LateArgs()) - { - CallArgABIInformation& abiInfo = arg.AbiInfo; - GenTree* argNode = arg.GetLateNode(); - - if (abiInfo.GetRegNum() == REG_STK) - { - continue; - } - -#ifdef UNIX_AMD64_ABI - // Deal with multi register passed struct args. - if (argNode->OperGet() == GT_FIELD_LIST) - { - unsigned regIndex = 0; - for (GenTreeFieldList::Use& use : argNode->AsFieldList()->Uses()) - { - GenTree* putArgRegNode = use.GetNode(); - assert(putArgRegNode->gtOper == GT_PUTARG_REG); - regNumber argReg = abiInfo.GetRegNum(regIndex++); - - genConsumeReg(putArgRegNode); - - // Validate the putArgRegNode has the right type. - assert(varTypeUsesFloatReg(putArgRegNode->TypeGet()) == genIsValidFloatReg(argReg)); - inst_Mov_Extend(putArgRegNode->TypeGet(), /* srcInReg */ false, argReg, putArgRegNode->GetRegNum(), - /* canSkip */ true, emitActualTypeSize(TYP_I_IMPL)); - } - } - else -#endif // UNIX_AMD64_ABI - { - regNumber argReg = abiInfo.GetRegNum(); - genConsumeReg(argNode); - inst_Mov_Extend(argNode->TypeGet(), /* srcInReg */ false, argReg, argNode->GetRegNum(), /* canSkip */ true, - emitActualTypeSize(TYP_I_IMPL)); - } - - // 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 (compFeatureVarArg() && call->IsVarargs() && varTypeIsFloating(argNode)) - { - regNumber srcReg = argNode->GetRegNum(); - regNumber targetReg = compiler->getCallArgIntRegister(argNode->GetRegNum()); - inst_Mov(TYP_LONG, targetReg, srcReg, /* canSkip */ false, emitActualTypeSize(TYP_I_IMPL)); - } - } + genCallPlaceRegArgs(call); #if defined(TARGET_X86) || defined(UNIX_AMD64_ABI) // The call will pop its arguments. diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 0c816486ccc25..df0b8d73d5ea2 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -2634,6 +2634,11 @@ struct GenTreeFieldList : public GenTree { } + Use* GetUse() + { + return use; + } + Use& operator*() { return *use; diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index b6b4fd7ffbe1d..9fb350800686a 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -1244,10 +1244,6 @@ int LinearScan::BuildCall(GenTreeCall* call) for (CallArg& arg : call->gtArgs.LateArgs()) { // By this point, lowering has ensured that all call arguments are one of the following: - // - an arg setup store - // - an arg placeholder - // - a nop - // - a copy blk // - a field list // - a put arg //