Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
97d2ef3
Add LoadPairVector64 and LoadPairVector128 in AdvSimd.cs AdvSimd.Plat…
echesakov Jul 9, 2020
e7e4ef7
Add LoadPairScalarVector64 in AdvSimd.cs AdvSimd.PlatformNotSupported.cs
echesakov Jul 9, 2020
fe3cae3
Add LoadPairVector64NonTemporal and LoadPairVector128NonTemporal in A…
echesakov Jul 9, 2020
e8acfcb
Add LoadPairScalarVector64NonTemporal in AdvSimd.cs AdvSimd.PlatformN…
echesakov Jul 9, 2020
9c7c982
Update System.Runtime.Intrinsics.cs
echesakov Feb 2, 2022
b3f9bef
Add LoadPairScalar() in src/tests/JIT/HardwareIntrinsics/Arm/Shared/H…
echesakov Nov 19, 2020
5109ecb
Add LoadPairVectorTest.template
echesakov Nov 18, 2020
4fc3de7
Add LoadPairVector64 and LoadPairVector128 in GenerateTests.csx
echesakov Nov 18, 2020
120e1e3
Update src/tests/JIT/HardwareIntrinsics/Arm/AdvSimd.Arm64/*
echesakov Feb 6, 2022
dda86a1
Use AdvSimd.Arm64.LoadPairVector128 in ASCIIUtility.cs
echesakov May 5, 2021
6d41f27
Use AdvSimd.Arm64.StorePair in BitArray.cs
echesakov May 5, 2021
bfa5a9f
Use AdvSimd.Arm64.LoadPairVector128 in OptimizedInboxTextEncoder.AdvS…
echesakov May 5, 2021
c22f924
Add HW_Flag_MultiReg and HWIntrinsicInfo::IsMultiReg() in hwintrinsic.h
echesakov Apr 15, 2021
a62d2bc
Add LoadPairVector64 and LoadPairVector128 in hwintrinsiclistarm64.h
echesakov Jul 14, 2020
e25774f
Adjust asserts to support multireg intrinsics in Compiler::impHWIntri…
echesakov Nov 17, 2020
ccc3f2d
Implement LoadPairVector128/64 in Compiler::impSpecialIntrinsic() in …
echesakov Feb 5, 2022
1246824
Implement LoadPairVector128/64 in CodeGen::genHWIntrinsic() in hwintr…
echesakov Nov 17, 2020
f3f5d42
Adjust asserts in Compiler::impAssignStructPtr() and Compiler::impNor…
echesakov Apr 15, 2021
4d17799
Support multi-register HW intrinsics on arm64 in gentree.h gentree.cpp
echesakov Apr 16, 2021
589742f
Support multi-register HW intrinsics on arm64 in lsraAssignRegToTree …
echesakov Apr 16, 2021
b5898c7
Extend LinearScan::BuildHWIntrinsic to support intrinsics returning v…
echesakov Nov 18, 2020
c1cbc9a
Don't insert indirection when source of a block assignment is a multi…
echesakov Feb 5, 2022
e974d4c
Don't morph multireg intrinsic on rhs of a block assigment in src/cor…
echesakov Feb 5, 2022
15e56a0
[mono] Implement LoadPair{,Scalar}Vector{64,128}{,NonTemporal}
imhameed Jun 26, 2021
11e58a4
Undo marking LoadPairVector64/128 as containable in /src/coreclr/jit/…
echesakov Feb 9, 2022
3420e44
Add HWIntrinsicInfo::GetMultiRegCount() helper in src/coreclr/jit/hwi…
echesakov Feb 9, 2022
a88b96f
Use HWIntrinsicInfo::GetMultiRegCount() helper in src/coreclr/jit/gen…
echesakov Feb 9, 2022
41b4adb
Use HWIntrinsicInfo::IsMultiReg() in GenTree::IsMultiRegNode() in src…
echesakov Feb 9, 2022
ed5e9c0
Revert "Use AdvSimd.Arm64.LoadPairVector128 in ASCIIUtility.cs"
echesakov Feb 10, 2022
936bfa0
Revert "Use AdvSimd.Arm64.LoadPairVector128 in OptimizedInboxTextEnco…
echesakov Feb 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 126 additions & 6 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,14 +706,19 @@ int GenTree::GetRegisterDstCount(Compiler* compiler) const
#endif
}
#endif

#if defined(TARGET_XARCH) && defined(FEATURE_HW_INTRINSICS)
if (OperIs(GT_HWINTRINSIC))
#ifdef FEATURE_HW_INTRINSICS
else if (OperIsHWIntrinsic())
{
assert(TypeGet() == TYP_STRUCT);
return 2;
assert(TypeIs(TYP_STRUCT));

const GenTreeHWIntrinsic* intrinsic = AsHWIntrinsic();
const NamedIntrinsic intrinsicId = intrinsic->GetHWIntrinsicId();
assert(HWIntrinsicInfo::IsMultiReg(intrinsicId));

return HWIntrinsicInfo::GetMultiRegCount(intrinsicId);
}
#endif
#endif // FEATURE_HW_INTRINSICS

if (OperIsScalarLocal())
{
return AsLclVar()->GetFieldCount(compiler);
Expand All @@ -722,6 +727,121 @@ int GenTree::GetRegisterDstCount(Compiler* compiler) const
return 0;
}

//-----------------------------------------------------------------------------------
// IsMultiRegNode: whether a node returning its value in more than one register
//
// Arguments:
// None
//
// Return Value:
// Returns true if this GenTree is a multi-reg node.
//
// Notes:
// All targets that support multi-reg ops of any kind also support multi-reg return
// values for calls. Should that change with a future target, this method will need
// to change accordingly.
//
bool GenTree::IsMultiRegNode() const
{
#if FEATURE_MULTIREG_RET
if (IsMultiRegCall())
{
return true;
}

#if FEATURE_ARG_SPLIT
if (OperIsPutArgSplit())
{
return true;
}
#endif

#if !defined(TARGET_64BIT)
if (OperIsMultiRegOp())
{
return true;
}
#endif

if (OperIs(GT_COPY, GT_RELOAD))
{
return true;
}
#endif // FEATURE_MULTIREG_RET

#ifdef FEATURE_HW_INTRINSICS
if (OperIsHWIntrinsic())
{
return HWIntrinsicInfo::IsMultiReg(AsHWIntrinsic()->GetHWIntrinsicId());
}
#endif // FEATURE_HW_INTRINSICS

if (IsMultiRegLclVar())
{
return true;
}
return false;
}

//-----------------------------------------------------------------------------------
// GetMultiRegCount: Return the register count for a multi-reg node.
//
// Arguments:
// None
//
// Return Value:
// Returns the number of registers defined by this node.
//
unsigned GenTree::GetMultiRegCount() const
{
#if FEATURE_MULTIREG_RET
if (IsMultiRegCall())
{
return AsCall()->GetReturnTypeDesc()->GetReturnRegCount();
}

#if FEATURE_ARG_SPLIT
if (OperIsPutArgSplit())
{
return AsPutArgSplit()->gtNumRegs;
}
#endif

#if !defined(TARGET_64BIT)
if (OperIsMultiRegOp())
{
return AsMultiRegOp()->GetRegCount();
}
#endif

if (OperIs(GT_COPY, GT_RELOAD))
{
return AsCopyOrReload()->GetRegCount();
}
#endif // FEATURE_MULTIREG_RET

#ifdef FEATURE_HW_INTRINSICS
if (OperIsHWIntrinsic())
{
return HWIntrinsicInfo::GetMultiRegCount(AsHWIntrinsic()->GetHWIntrinsicId());
}
#endif // FEATURE_HW_INTRINSICS

if (OperIs(GT_LCL_VAR, GT_STORE_LCL_VAR))
{
assert((gtFlags & GTF_VAR_MULTIREG) != 0);
// The register count for a multireg lclVar requires looking at the LclVarDsc,
// which requires a Compiler instance. The caller must handle this separately.
// The register count for a multireg lclVar requires looking at the LclVarDsc,
// which requires a Compiler instance. The caller must use the GetFieldCount
// method on GenTreeLclVar.

assert(!"MultiRegCount for LclVar");
}
assert(!"GetMultiRegCount called with non-multireg node");
return 1;
}

//---------------------------------------------------------------
// gtGetRegMask: Get the reg mask of the node.
//
Expand Down
141 changes: 21 additions & 120 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1747,10 +1747,10 @@ struct GenTree
inline bool IsMultiRegLclVar() const;

// Returns true if it is a node returning its value in more than one register
inline bool IsMultiRegNode() const;
bool IsMultiRegNode() const;

// Returns the number of registers defined by a multireg node.
unsigned GetMultiRegCount();
unsigned GetMultiRegCount() const;

// Returns the regIndex'th register defined by a possibly-multireg node.
regNumber GetRegByIndex(int regIndex);
Expand Down Expand Up @@ -7149,7 +7149,7 @@ struct GenTreeCopyOrReload : public GenTreeUnOp
#endif
}

unsigned GetRegCount()
unsigned GetRegCount() const
{
#if FEATURE_MULTIREG_RET
// We need to return the highest index for which we have a valid register.
Expand Down Expand Up @@ -7998,117 +7998,6 @@ inline bool GenTree::IsMultiRegLclVar() const
return false;
}

//-----------------------------------------------------------------------------------
// IsMultiRegNode: whether a node returning its value in more than one register
//
// Arguments:
// None
//
// Return Value:
// Returns true if this GenTree is a multi-reg node.
//
// Notes:
// All targets that support multi-reg ops of any kind also support multi-reg return
// values for calls. Should that change with a future target, this method will need
// to change accordingly.
//
inline bool GenTree::IsMultiRegNode() const
{
#if FEATURE_MULTIREG_RET
if (IsMultiRegCall())
{
return true;
}

#if FEATURE_ARG_SPLIT
if (OperIsPutArgSplit())
{
return true;
}
#endif

#if !defined(TARGET_64BIT)
if (OperIsMultiRegOp())
{
return true;
}
#endif

if (OperIs(GT_COPY, GT_RELOAD))
{
return true;
}
#endif // FEATURE_MULTIREG_RET
#if defined(TARGET_XARCH) && defined(FEATURE_HW_INTRINSICS)
if (OperIs(GT_HWINTRINSIC))
{
return (TypeGet() == TYP_STRUCT);
}
#endif
if (IsMultiRegLclVar())
{
return true;
}
return false;
}
//-----------------------------------------------------------------------------------
// GetMultiRegCount: Return the register count for a multi-reg node.
//
// Arguments:
// None
//
// Return Value:
// Returns the number of registers defined by this node.
//
inline unsigned GenTree::GetMultiRegCount()
{
#if FEATURE_MULTIREG_RET
if (IsMultiRegCall())
{
return AsCall()->GetReturnTypeDesc()->GetReturnRegCount();
}

#if FEATURE_ARG_SPLIT
if (OperIsPutArgSplit())
{
return AsPutArgSplit()->gtNumRegs;
}
#endif

#if !defined(TARGET_64BIT)
if (OperIsMultiRegOp())
{
return AsMultiRegOp()->GetRegCount();
}
#endif

if (OperIs(GT_COPY, GT_RELOAD))
{
return AsCopyOrReload()->GetRegCount();
}
#endif // FEATURE_MULTIREG_RET
#if defined(TARGET_XARCH) && defined(FEATURE_HW_INTRINSICS)
if (OperIs(GT_HWINTRINSIC))
{
assert(TypeGet() == TYP_STRUCT);
return 2;
}
#endif
if (OperIs(GT_LCL_VAR, GT_STORE_LCL_VAR))
{
assert((gtFlags & GTF_VAR_MULTIREG) != 0);
// The register count for a multireg lclVar requires looking at the LclVarDsc,
// which requires a Compiler instance. The caller must handle this separately.
// The register count for a multireg lclVar requires looking at the LclVarDsc,
// which requires a Compiler instance. The caller must use the GetFieldCount
// method on GenTreeLclVar.

assert(!"MultiRegCount for LclVar");
}
assert(!"GetMultiRegCount called with non-multireg node");
return 1;
}

//-----------------------------------------------------------------------------------
// GetRegByIndex: Get a specific register, based on regIndex, that is produced
// by this node.
Expand Down Expand Up @@ -8156,13 +8045,14 @@ inline regNumber GenTree::GetRegByIndex(int regIndex)
return AsCopyOrReload()->GetRegNumByIdx(regIndex);
}
#endif // FEATURE_MULTIREG_RET
#if defined(TARGET_XARCH) && defined(FEATURE_HW_INTRINSICS)
#ifdef FEATURE_HW_INTRINSICS
if (OperIs(GT_HWINTRINSIC))
{
assert(regIndex == 1);
// TODO-ARM64-NYI: Support hardware intrinsics operating on multiple contiguous registers.
return AsHWIntrinsic()->GetOtherReg();
}
#endif
#endif // FEATURE_HW_INTRINSICS
if (OperIs(GT_LCL_VAR, GT_STORE_LCL_VAR))
{
return AsLclVar()->GetRegNumByIdx(regIndex);
Expand Down Expand Up @@ -8213,15 +8103,26 @@ inline var_types GenTree::GetRegTypeByIndex(int regIndex)

#endif // FEATURE_MULTIREG_RET

#if defined(TARGET_XARCH) && defined(FEATURE_HW_INTRINSICS)
if (OperIs(GT_HWINTRINSIC))
if (OperIsHWIntrinsic())
{
assert(TypeGet() == TYP_STRUCT);
#ifdef TARGET_ARM64
if (AsHWIntrinsic()->GetSimdSize() == 16)
{
return TYP_SIMD16;
}
else
{
assert(AsHWIntrinsic()->GetSimdSize() == 8);
return TYP_SIMD8;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any cases of "arg size is 8" but "return size is 16" or vice-versa?

I know some instructions fit that bill, I'm not sure if any of the multi-reg cases will

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see an example on Arm64 when this wouldn't hold.
ld[1-4] should be similar to ldp.

As for tbl and tbx:

TBX <Vd>.<Ta>, { <Vn>.16B, <Vn+1>.16B, <Vn+2>.16B }, <Vm>.<Ta>

the return value is going to be single-reg but the first source operand is multi-reg and composed of Vector128<byte>.

}
#elif defined(TARGET_XARCH)
// At this time, the only multi-reg HW intrinsics all return the type of their
// arguments. If this changes, we will need a way to record or determine this.
assert(TypeGet() == TYP_STRUCT);
return gtGetOp1()->TypeGet();
}
#endif
}

if (OperIs(GT_LCL_VAR, GT_STORE_LCL_VAR))
{
if (TypeGet() == TYP_LONG)
Expand Down
23 changes: 16 additions & 7 deletions src/coreclr/jit/hwintrinsic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -770,14 +770,23 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
{
unsigned int sizeBytes;
simdBaseJitType = getBaseJitTypeAndSizeOfSIMDType(sig->retTypeSigClass, &sizeBytes);
retType = getSIMDTypeForSize(sizeBytes);
assert(sizeBytes != 0);

// We want to return early here for cases where retType was TYP_STRUCT as per method signature and
// rather than deferring the decision after getting the simdBaseJitType of arg.
if (!isSupportedBaseType(intrinsic, simdBaseJitType))
if (HWIntrinsicInfo::IsMultiReg(intrinsic))
{
return nullptr;
assert(sizeBytes == 0);
}
else
{
assert(sizeBytes != 0);

// We want to return early here for cases where retType was TYP_STRUCT as per method signature and
// rather than deferring the decision after getting the simdBaseJitType of arg.
if (!isSupportedBaseType(intrinsic, simdBaseJitType))
{
return nullptr;
}

retType = getSIMDTypeForSize(sizeBytes);
}
}

Expand Down Expand Up @@ -1188,7 +1197,7 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
}

// This operation contains an implicit indirection
// it could point into the gloabal heap or
// it could point into the global heap or
// it could throw a null reference exception.
//
retNode->gtFlags |= (GTF_GLOB_REF | GTF_EXCEPT);
Expand Down
Loading