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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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 numberDiff line numberDiff 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 DownExpand 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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
Copy Markdown
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
Copy Markdown
ContributorAuthor

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 numberDiff line numberDiff 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 DownExpand 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