Skip to content

Commit 0ee150e

Browse files
github-actions[bot]tannergoodingjeffschwMSFT
authored
[release/9.0] Ensure that constant folding of bitwise operations for float/double are bitwise (#106830)
* Ensure that constant folding of bitwise operations for float/double are bitwise * Ensure that the new header only methods are marked `inline` to avoid duplicate definitions * Apply formatting patch --------- Co-authored-by: Tanner Gooding <tagoo@outlook.com> Co-authored-by: Jeff Schwartz <jeffschw@microsoft.com>
1 parent 5ec43e0 commit 0ee150e

File tree

3 files changed

+136
-26
lines changed

3 files changed

+136
-26
lines changed

src/coreclr/jit/simd.h

+85-26
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,11 @@ typedef simd64_t simd_t;
363363
typedef simd16_t simd_t;
364364
#endif
365365

366+
inline bool IsUnaryBitwiseOperation(genTreeOps oper)
367+
{
368+
return (oper == GT_LZCNT) || (oper == GT_NOT);
369+
}
370+
366371
template <typename TBase>
367372
TBase EvaluateUnaryScalarSpecialized(genTreeOps oper, TBase arg0)
368373
{
@@ -404,27 +409,35 @@ TBase EvaluateUnaryScalarSpecialized(genTreeOps oper, TBase arg0)
404409
template <>
405410
inline float EvaluateUnaryScalarSpecialized<float>(genTreeOps oper, float arg0)
406411
{
407-
if (oper == GT_NEG)
412+
switch (oper)
408413
{
409-
return -arg0;
410-
}
414+
case GT_NEG:
415+
{
416+
return -arg0;
417+
}
411418

412-
uint32_t arg0Bits = BitOperations::SingleToUInt32Bits(arg0);
413-
uint32_t resultBits = EvaluateUnaryScalarSpecialized<uint32_t>(oper, arg0Bits);
414-
return BitOperations::UInt32BitsToSingle(resultBits);
419+
default:
420+
{
421+
unreached();
422+
}
423+
}
415424
}
416425

417426
template <>
418427
inline double EvaluateUnaryScalarSpecialized<double>(genTreeOps oper, double arg0)
419428
{
420-
if (oper == GT_NEG)
429+
switch (oper)
421430
{
422-
return -arg0;
423-
}
431+
case GT_NEG:
432+
{
433+
return -arg0;
434+
}
424435

425-
uint64_t arg0Bits = BitOperations::DoubleToUInt64Bits(arg0);
426-
uint64_t resultBits = EvaluateUnaryScalarSpecialized<uint64_t>(oper, arg0Bits);
427-
return BitOperations::UInt64BitsToDouble(resultBits);
436+
default:
437+
{
438+
unreached();
439+
}
440+
}
428441
}
429442

430443
template <typename TBase>
@@ -600,13 +613,37 @@ void EvaluateUnarySimd(genTreeOps oper, bool scalar, var_types baseType, TSimd*
600613
{
601614
case TYP_FLOAT:
602615
{
603-
EvaluateUnarySimd<TSimd, float>(oper, scalar, result, arg0);
616+
// Some operations are bitwise and we want to ensure inputs like
617+
// sNaN are preserved rather than being converted to a qNaN when
618+
// the CPU encounters them. So we check for and handle that early
619+
// prior to extracting the element out of the vector value.
620+
621+
if (IsUnaryBitwiseOperation(oper))
622+
{
623+
EvaluateUnarySimd<TSimd, int32_t>(oper, scalar, result, arg0);
624+
}
625+
else
626+
{
627+
EvaluateUnarySimd<TSimd, float>(oper, scalar, result, arg0);
628+
}
604629
break;
605630
}
606631

607632
case TYP_DOUBLE:
608633
{
609-
EvaluateUnarySimd<TSimd, double>(oper, scalar, result, arg0);
634+
// Some operations are bitwise and we want to ensure inputs like
635+
// sNaN are preserved rather than being converted to a qNaN when
636+
// the CPU encounters them. So we check for and handle that early
637+
// prior to extracting the element out of the vector value.
638+
639+
if (IsUnaryBitwiseOperation(oper))
640+
{
641+
EvaluateUnarySimd<TSimd, int64_t>(oper, scalar, result, arg0);
642+
}
643+
else
644+
{
645+
EvaluateUnarySimd<TSimd, double>(oper, scalar, result, arg0);
646+
}
610647
break;
611648
}
612649

@@ -665,6 +702,12 @@ void EvaluateUnarySimd(genTreeOps oper, bool scalar, var_types baseType, TSimd*
665702
}
666703
}
667704

705+
inline bool IsBinaryBitwiseOperation(genTreeOps oper)
706+
{
707+
return (oper == GT_AND) || (oper == GT_AND_NOT) || (oper == GT_LSH) || (oper == GT_OR) || (oper == GT_ROL) ||
708+
(oper == GT_ROR) || (oper == GT_RSH) || (oper == GT_RSZ) || (oper == GT_XOR);
709+
}
710+
668711
template <typename TBase>
669712
TBase EvaluateBinaryScalarRSZ(TBase arg0, TBase arg1)
670713
{
@@ -902,11 +945,7 @@ inline float EvaluateBinaryScalarSpecialized<float>(genTreeOps oper, float arg0,
902945

903946
default:
904947
{
905-
uint32_t arg0Bits = BitOperations::SingleToUInt32Bits(arg0);
906-
uint32_t arg1Bits = BitOperations::SingleToUInt32Bits(arg1);
907-
908-
uint32_t resultBits = EvaluateBinaryScalarSpecialized<uint32_t>(oper, arg0Bits, arg1Bits);
909-
return BitOperations::UInt32BitsToSingle(resultBits);
948+
unreached();
910949
}
911950
}
912951
}
@@ -948,11 +987,7 @@ inline double EvaluateBinaryScalarSpecialized<double>(genTreeOps oper, double ar
948987

949988
default:
950989
{
951-
uint64_t arg0Bits = BitOperations::DoubleToUInt64Bits(arg0);
952-
uint64_t arg1Bits = BitOperations::DoubleToUInt64Bits(arg1);
953-
954-
uint64_t resultBits = EvaluateBinaryScalarSpecialized<uint64_t>(oper, arg0Bits, arg1Bits);
955-
return BitOperations::UInt64BitsToDouble(resultBits);
990+
unreached();
956991
}
957992
}
958993
}
@@ -1188,13 +1223,37 @@ void EvaluateBinarySimd(
11881223
{
11891224
case TYP_FLOAT:
11901225
{
1191-
EvaluateBinarySimd<TSimd, float>(oper, scalar, result, arg0, arg1);
1226+
// Some operations are bitwise and we want to ensure inputs like
1227+
// sNaN are preserved rather than being converted to a qNaN when
1228+
// the CPU encounters them. So we check for and handle that early
1229+
// prior to extracting the element out of the vector value.
1230+
1231+
if (IsBinaryBitwiseOperation(oper))
1232+
{
1233+
EvaluateBinarySimd<TSimd, int32_t>(oper, scalar, result, arg0, arg1);
1234+
}
1235+
else
1236+
{
1237+
EvaluateBinarySimd<TSimd, float>(oper, scalar, result, arg0, arg1);
1238+
}
11921239
break;
11931240
}
11941241

11951242
case TYP_DOUBLE:
11961243
{
1197-
EvaluateBinarySimd<TSimd, double>(oper, scalar, result, arg0, arg1);
1244+
// Some operations are bitwise and we want to ensure inputs like
1245+
// sNaN are preserved rather than being converted to a qNaN when
1246+
// the CPU encounters them. So we check for and handle that early
1247+
// prior to extracting the element out of the vector value.
1248+
1249+
if (IsBinaryBitwiseOperation(oper))
1250+
{
1251+
EvaluateBinarySimd<TSimd, int64_t>(oper, scalar, result, arg0, arg1);
1252+
}
1253+
else
1254+
{
1255+
EvaluateBinarySimd<TSimd, double>(oper, scalar, result, arg0, arg1);
1256+
}
11981257
break;
11991258
}
12001259

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.CompilerServices;
6+
using System.Numerics;
7+
using System.Runtime.Intrinsics;
8+
using System.Runtime.Intrinsics.X86;
9+
using Xunit;
10+
11+
// Generated by Fuzzlyn v2.2 on 2024-08-17 17:40:06
12+
// Run on X86 Windows
13+
// Seed: 1343518557351353159-vectort,vector128,vector256,vector512,x86aes,x86avx,x86avx2,x86avx512bw,x86avx512bwvl,x86avx512cd,x86avx512cdvl,x86avx512dq,x86avx512dqvl,x86avx512f,x86avx512fvl,x86avx512vbmi,x86avx512vbmivl,x86bmi1,x86bmi2,x86fma,x86lzcnt,x86pclmulqdq,x86popcnt,x86sse,x86sse2,x86sse3,x86sse41,x86sse42,x86ssse3,x86x86base
14+
// Reduced from 171.2 KiB to 0.6 KiB in 00:06:37
15+
// Debug: Outputs <4292870144, 0, 0, 0, 0, 0, 0, 0>
16+
// Release: Outputs <0, 0, 0, 0, 0, 0, 0, 0>
17+
18+
public class C1
19+
{
20+
public Vector256<float> F5;
21+
22+
public C1(Vector256<float> f5)
23+
{
24+
F5 = f5;
25+
}
26+
}
27+
28+
public class Runtime_106610
29+
{
30+
[Fact]
31+
public static void TestEntryPoint()
32+
{
33+
if (Avx512DQ.VL.IsSupported)
34+
{
35+
var vr4 = Vector256.Create<float>(0);
36+
var vr5 = Vector256.CreateScalar(1f);
37+
var vr6 = Vector256.CreateScalar(-10f);
38+
var vr7 = Avx.Or(vr5, vr6);
39+
C1 vr8 = new C1(Avx512DQ.VL.Range(vr4, vr7, 0));
40+
Assert.Equal(Vector256.CreateScalar<uint>(4292870144), vr8.F5.AsUInt32());
41+
}
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<Optimize>True</Optimize>
4+
</PropertyGroup>
5+
<ItemGroup>
6+
<Compile Include="$(MSBuildProjectName).cs" />
7+
</ItemGroup>
8+
</Project>

0 commit comments

Comments
 (0)