Skip to content

Add Unsafe.IsAddressLess/GreaterThanOrEqualTo methods #89156

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions src/coreclr/jit/fgbasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1267,7 +1267,9 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
case NI_SRCS_UNSAFE_AreSame:
case NI_SRCS_UNSAFE_ByteOffset:
case NI_SRCS_UNSAFE_IsAddressGreaterThan:
case NI_SRCS_UNSAFE_IsAddressGreaterThanOrEqualTo:
case NI_SRCS_UNSAFE_IsAddressLessThan:
case NI_SRCS_UNSAFE_IsAddressLessThanOrEqualTo:
case NI_SRCS_UNSAFE_IsNullRef:
case NI_SRCS_UNSAFE_Subtract:
case NI_SRCS_UNSAFE_SubtractByteOffset:
Expand All @@ -1282,7 +1284,9 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, Fixed
{
case NI_SRCS_UNSAFE_AreSame:
case NI_SRCS_UNSAFE_IsAddressGreaterThan:
case NI_SRCS_UNSAFE_IsAddressGreaterThanOrEqualTo:
case NI_SRCS_UNSAFE_IsAddressLessThan:
case NI_SRCS_UNSAFE_IsAddressLessThanOrEqualTo:
case NI_SRCS_UNSAFE_IsNullRef:
{
fgObserveInlineConstants(opcode, pushedStack, isInlining);
Expand Down
46 changes: 46 additions & 0 deletions src/coreclr/jit/importercalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4317,6 +4317,25 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic,
return gtFoldExpr(tmp);
}

case NI_SRCS_UNSAFE_IsAddressGreaterThanOrEqualTo:
{
assert(sig->sigInst.methInstCount == 1);

// ldarg.0
// ldarg.1
// clt.un
// ldc.i4.0
// ceq
// ret

GenTree* op2 = impPopStack().val;
GenTree* op1 = impPopStack().val;

GenTree* tmp = gtNewOperNode(GT_GE, TYP_INT, op1, op2);
tmp->gtFlags |= GTF_UNSIGNED;
return gtFoldExpr(tmp);
}

case NI_SRCS_UNSAFE_IsAddressLessThan:
{
assert(sig->sigInst.methInstCount == 1);
Expand All @@ -4334,6 +4353,25 @@ GenTree* Compiler::impSRCSUnsafeIntrinsic(NamedIntrinsic intrinsic,
return gtFoldExpr(tmp);
}

case NI_SRCS_UNSAFE_IsAddressLessThanOrEqualTo:
{
assert(sig->sigInst.methInstCount == 1);

// ldarg.0
// ldarg.1
// cgt.un
// ldc.i4.0
// ceq
// ret

GenTree* op2 = impPopStack().val;
GenTree* op1 = impPopStack().val;

GenTree* tmp = gtNewOperNode(GT_LE, TYP_INT, op1, op2);
tmp->gtFlags |= GTF_UNSIGNED;
return gtFoldExpr(tmp);
}

case NI_SRCS_UNSAFE_IsNullRef:
{
assert(sig->sigInst.methInstCount == 1);
Expand Down Expand Up @@ -9026,10 +9064,18 @@ NamedIntrinsic Compiler::lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method)
{
result = NI_SRCS_UNSAFE_IsAddressGreaterThan;
}
else if (strcmp(methodName, "IsAddressGreaterThanOrEqualTo") == 0)
{
result = NI_SRCS_UNSAFE_IsAddressGreaterThanOrEqualTo;
}
else if (strcmp(methodName, "IsAddressLessThan") == 0)
{
result = NI_SRCS_UNSAFE_IsAddressLessThan;
}
else if (strcmp(methodName, "IsAddressLessThanOrEqualTo") == 0)
{
result = NI_SRCS_UNSAFE_IsAddressLessThanOrEqualTo;
}
else if (strcmp(methodName, "IsNullRef") == 0)
{
result = NI_SRCS_UNSAFE_IsNullRef;
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/namedintrinsiclist.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ enum NamedIntrinsic : unsigned short
NI_SRCS_UNSAFE_InitBlock,
NI_SRCS_UNSAFE_InitBlockUnaligned,
NI_SRCS_UNSAFE_IsAddressGreaterThan,
NI_SRCS_UNSAFE_IsAddressGreaterThanOrEqualTo,
NI_SRCS_UNSAFE_IsAddressLessThan,
NI_SRCS_UNSAFE_IsAddressLessThanOrEqualTo,
NI_SRCS_UNSAFE_IsNullRef,
NI_SRCS_UNSAFE_NullRef,
NI_SRCS_UNSAFE_Read,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,28 @@ public static MethodIL EmitIL(MethodDesc method)
(byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1,
(byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.cgt_un),
(byte)ILOpcode.ret }, Array.Empty<LocalVariableDefinition>(), null);
case "IsAddressGreaterThanOrEqualTo":
return new ILStubMethodIL(method, new byte[]
{
(byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1,
(byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.clt_un),
(byte)ILOpcode.ldc_i4_0,
(byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.ceq),
(byte)ILOpcode.ret }, Array.Empty<LocalVariableDefinition>(), null);
case "IsAddressLessThan":
return new ILStubMethodIL(method, new byte[]
{
(byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1,
(byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.clt_un),
(byte)ILOpcode.ret }, Array.Empty<LocalVariableDefinition>(), null);
case "IsAddressLessThanOrEqualTo":
return new ILStubMethodIL(method, new byte[]
{
(byte)ILOpcode.ldarg_0, (byte)ILOpcode.ldarg_1,
(byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.cgt_un),
(byte)ILOpcode.ldc_i4_0,
(byte)ILOpcode.prefix1, unchecked((byte)ILOpcode.ceq),
(byte)ILOpcode.ret }, Array.Empty<LocalVariableDefinition>(), null);
case "ByteOffset":
return new ILStubMethodIL(method, new byte[]
{
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/corelib.h
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,9 @@ DEFINE_METHOD(UNSAFE, BYREF_COPY_BLOCK, CopyBlock, SM_RefByt
DEFINE_METHOD(UNSAFE, PTR_COPY_BLOCK_UNALIGNED, CopyBlockUnaligned, SM_PtrVoid_PtrVoid_UInt_RetVoid)
DEFINE_METHOD(UNSAFE, BYREF_COPY_BLOCK_UNALIGNED, CopyBlockUnaligned, SM_RefByte_RefByte_UInt_RetVoid)
DEFINE_METHOD(UNSAFE, BYREF_IS_ADDRESS_GREATER_THAN, IsAddressGreaterThan, NoSig)
DEFINE_METHOD(UNSAFE, BYREF_IS_ADDRESS_GREATER_THAN_OR_EQUAL_TO, IsAddressGreaterThanOrEqualTo, NoSig)
DEFINE_METHOD(UNSAFE, BYREF_IS_ADDRESS_LESS_THAN, IsAddressLessThan, NoSig)
DEFINE_METHOD(UNSAFE, BYREF_IS_ADDRESS_LESS_THAN_OR_EQUAL_TO, IsAddressLessThanOrEqualTo, NoSig)
DEFINE_METHOD(UNSAFE, BYREF_INIT_BLOCK, InitBlockUnaligned, SM_RefByte_Byte_UInt_RetVoid)
DEFINE_METHOD(UNSAFE, PTR_INIT_BLOCK, InitBlock, SM_PtrVoid_Byte_UInt_RetVoid)
DEFINE_METHOD(UNSAFE, BYREF_INIT_BLOCK_UNALIGNED, InitBlock, SM_RefByte_Byte_UInt_RetVoid)
Expand Down
34 changes: 34 additions & 0 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6969,6 +6969,23 @@ bool getILIntrinsicImplementationForUnsafe(MethodDesc * ftn,

return true;
}
else if (tk == CoreLibBinder::GetMethod(METHOD__UNSAFE__BYREF_IS_ADDRESS_GREATER_THAN_OR_EQUAL_TO)->GetMemberDef())
{
// Compare the two arguments
static const BYTE ilcode[] =
{
CEE_LDARG_0,
CEE_LDARG_1,
CEE_PREFIX1, (CEE_CLT_UN & 0xFF),
CEE_LDC_I4_0,
CEE_PREFIX1, (CEE_CEQ & 0xFF),
CEE_RET
};

setILIntrinsicMethodInfo(methInfo,const_cast<BYTE*>(ilcode),sizeof(ilcode), 2);

return true;
}
else if (tk == CoreLibBinder::GetMethod(METHOD__UNSAFE__BYREF_IS_ADDRESS_LESS_THAN)->GetMemberDef())
{
// Compare the two arguments
Expand All @@ -6984,6 +7001,23 @@ bool getILIntrinsicImplementationForUnsafe(MethodDesc * ftn,

return true;
}
else if (tk == CoreLibBinder::GetMethod(METHOD__UNSAFE__BYREF_IS_ADDRESS_LESS_THAN_OR_EQUAL_TO)->GetMemberDef())
{
// Compare the two arguments
static const BYTE ilcode[] =
{
CEE_LDARG_0,
CEE_LDARG_1,
CEE_PREFIX1, (CEE_CGT_UN & 0xFF),
CEE_LDC_I4_0,
CEE_PREFIX1, (CEE_CEQ & 0xFF),
CEE_RET
};

setILIntrinsicMethodInfo(methInfo,const_cast<BYTE*>(ilcode),sizeof(ilcode), 2);

return true;
}
else if (tk == CoreLibBinder::GetMethod(METHOD__UNSAFE__BYREF_NULLREF)->GetMemberDef())
{
static const BYTE ilcode[] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ public static IEnumerable<object[]> StringStringData() =>
new[]
{
"AsPointer", "As", "AsRef", "Add", "AddByteOffset", "Copy", "CopyBlock", "CopyBlockUnaligned", "InitBlock", "InitBlockUnaligned", "Read", "Write",
"ReadUnaligned", "WriteUnaligned", "AreSame", "IsAddressGreaterThan", "IsAddressLessThan", "ByteOffset", "NullRef", "IsNullRef", "SkipInit",
"Subtract", "SubtractByteOffset", "Unbox",
"ReadUnaligned", "WriteUnaligned", "AreSame", "IsAddressGreaterThan", "IsAddressGreaterThanOrEqualTo", "IsAddressLessThan", "IsAddressLessThanOrEqualTo",
"ByteOffset", "NullRef", "IsNullRef", "SkipInit", "Subtract", "SubtractByteOffset", "Unbox",
},

// from https://raw.githubusercontent.com/dotnet/roslyn/0456b4adc6939e366e7c509318b3ac6a85cda496/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenLengthBasedSwitchTests.cs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ private static unsafe int PickPivotAndPartition(Span<T> keys)
while (Unsafe.IsAddressGreaterThan(ref rightRef, ref zeroRef) && LessThan(ref pivot, ref rightRef = ref Unsafe.Add(ref rightRef, -1))) ;
}

if (!Unsafe.IsAddressLessThan(ref leftRef, ref rightRef))
if (Unsafe.IsAddressGreaterThanOrEqualTo(ref leftRef, ref rightRef))
{
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,31 @@ public static bool IsAddressGreaterThan<T>([AllowNull] ref T left, [AllowNull] r
// ret
}

/// <summary>
/// Determines whether the memory address referenced by <paramref name="left"/> is greater than
/// or equal to the memory address referenced by <paramref name="right"/>.
/// </summary>
/// <remarks>
/// This check is conceptually similar to "(void*)(&amp;left) &gt;= (void*)(&amp;right)".
/// </remarks>
[Intrinsic]
// CoreCLR:CoreCLR:METHOD__UNSAFE__BYREF_IS_ADDRESS_GREATER_THAN_OR_EQUAL_TO
// AOT:IsAddressGreaterThanOrEqualTo
// Mono:IsAddressGreaterThanOrEqualTo
[NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsAddressGreaterThanOrEqualTo<T>([AllowNull] ref T left, [AllowNull] ref T right)
{
throw new PlatformNotSupportedException();

// ldarg.0
// ldarg.1
// clt.un
// ldc.i4.0
// ceq
// ret
}

/// <summary>
/// Determines whether the memory address referenced by <paramref name="left"/> is less than
/// the memory address referenced by <paramref name="right"/>.
Expand All @@ -418,6 +443,31 @@ public static bool IsAddressLessThan<T>([AllowNull] ref T left, [AllowNull] ref
// ret
}

/// <summary>
/// Determines whether the memory address referenced by <paramref name="left"/> is less than
/// or equal to the memory address referenced by <paramref name="right"/>.
/// </summary>
/// <remarks>
/// This check is conceptually similar to "(void*)(&amp;left) &lt;= (void*)(&amp;right)".
/// </remarks>
[Intrinsic]
// CoreCLR:METHOD__UNSAFE__BYREF_IS_ADDRESS_LESS_THAN_OR_EQUAL_TO
// AOT:IsAddressLessThanOrEqualTo
// Mono:IsAddressLessThanOrEqualTo
[NonVersionable]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsAddressLessThanOrEqualTo<T>([AllowNull] ref T left, [AllowNull] ref T right)
{
throw new PlatformNotSupportedException();

// ldarg.0
// ldarg.1
// cgt.un
// ldc.i4.0
// ceq
// ret
}

/// <summary>
/// Initializes a block of memory at the given location with a given initial value.
/// </summary>
Expand Down
Loading