From c2c2bba420ea2237a35e34f5bc0aa3fc37050ea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 2 May 2024 15:14:23 +0200 Subject: [PATCH 01/60] First, add a bunch of failing tests --- .../JIT/Directed/StructABI/CMakeLists.txt | 5 +- .../JIT/Directed/StructABI/StructABI.cpp | 159 ++++++ src/tests/JIT/Directed/StructABI/StructABI.cs | 497 ++++++++++++++++++ 3 files changed, 659 insertions(+), 2 deletions(-) create mode 100644 src/tests/JIT/Directed/StructABI/StructABI.cpp diff --git a/src/tests/JIT/Directed/StructABI/CMakeLists.txt b/src/tests/JIT/Directed/StructABI/CMakeLists.txt index 1c7c8684995a59..35f73d8d208fa8 100644 --- a/src/tests/JIT/Directed/StructABI/CMakeLists.txt +++ b/src/tests/JIT/Directed/StructABI/CMakeLists.txt @@ -2,13 +2,14 @@ project (StructABILib) include_directories(${INC_PLATFORM_DIR}) if(CLR_CMAKE_HOST_WIN32) - add_compile_options(/TC) # compile all files as C + set_source_files_properties(StructABI.c PROPERTIES COMPILE_OPTIONS /TC) # compile as C else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") + set(CMAKE_CPP_FLAGS "${CMAKE_CPP_FLAGS} -fvisibility=hidden") endif() # add the executable -add_library (StructABILib SHARED StructABI.c) +add_library (StructABILib SHARED StructABI.c StructABI.cpp) # add the install targets install (TARGETS StructABILib DESTINATION bin) diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cpp b/src/tests/JIT/Directed/StructABI/StructABI.cpp new file mode 100644 index 00000000000000..428d90f18be126 --- /dev/null +++ b/src/tests/JIT/Directed/StructABI/StructABI.cpp @@ -0,0 +1,159 @@ +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include + +#ifdef _MSC_VER +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT __attribute__((visibility("default"))) +#endif // _MSC_VER + +struct Empty +{ +}; +static_assert(sizeof(Empty) == 1, "Empty struct must be sized like in .NET"); + +struct LongEmptyDouble +{ + int64_t FieldL; + Empty FieldE; + double FieldD; +}; + +struct NestedEmpty +{ + struct InnerEmpty + { + Empty e; + } e; +}; +static_assert(sizeof(NestedEmpty) == 1, "Nested empty struct must be sized like in .NET"); + +struct NestedEmptyFloatDouble +{ + NestedEmpty FieldNE; + float FieldF; + double FieldD; +}; + +struct EmptyIntAndFloat +{ + struct EmptyInt + { + Empty FieldE; + int32_t FieldI; + }; + EmptyInt FieldEI; + float FieldF; +}; + +struct LongEmptyAndFloat +{ + struct LongEmpty + { + int64_t FieldL; + Empty FieldE; + }; + LongEmpty FieldLE; + float FieldF; +}; + +struct ArrayOfEmpties +{ + Empty e[1]; +}; + +struct ArrayOfEmptiesFloatDouble +{ + ArrayOfEmpties FieldAoE; + float FieldF; + double FieldD; +}; + +template +struct Sixteen +{ + T e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16; +}; + +struct FloatEmptyMegabyteInt +{ + float FieldF; + Sixteen>>>> FieldEmptyMegabyte; + int32_t FieldI; +}; + +#pragma pack(push, 1) +struct PackedEmptyFloatLong +{ + Empty FieldE; + float FieldF; + int64_t FieldL; +}; +#pragma pack(pop) + +struct ExplicitFloatLong +{ + PackedEmptyFloatLong s; +}; +static_assert(offsetof(ExplicitFloatLong, s.FieldE) == 0, ""); +static_assert(offsetof(ExplicitFloatLong, s.FieldF) == 1, ""); +static_assert(offsetof(ExplicitFloatLong, s.FieldL) == 5, ""); + +extern "C" +{ + +DLLEXPORT LongEmptyDouble EchoLongEmptyDoubleRiscV(LongEmptyDouble a0_fa0) +{ + return a0_fa0; +} + +DLLEXPORT LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscV( + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble byRef) +{ + return byRef; +} + +DLLEXPORT NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscV(NestedEmptyFloatDouble fa0_fa1) +{ + return fa0_fa1; +} + +DLLEXPORT NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscV( + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a0_a1) +{ + return a0_a1; +} + +DLLEXPORT EmptyIntAndFloat EchoEmptyIntAndFloatRiscV(EmptyIntAndFloat a0_fa0) +{ + return a0_fa0; +} + +DLLEXPORT LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(LongEmptyAndFloat a0_fa0) +{ + return a0_fa0; +} + +DLLEXPORT ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscV(ArrayOfEmptiesFloatDouble a0_a1) +{ + return a0_a1; +} + +DLLEXPORT FloatEmptyMegabyteInt EchoFloatEmptyMegabyteIntRiscV(FloatEmptyMegabyteInt fa0_a0) +{ + return fa0_a0; +} + +DLLEXPORT PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(PackedEmptyFloatLong fa0_a0) +{ + return fa0_a0; +} + +DLLEXPORT ExplicitFloatLong EchoExplicitFloatLongRiscV(ExplicitFloatLong fa0_a0) +{ + return fa0_a0; +} + +} // extern "C" diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index 2f74d49e75853f..b89d2edde7f1c5 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -668,6 +668,176 @@ public bool Equals(Nested9 other) } } +struct Empty +{ +} + +struct LongEmptyDouble +{ + long FieldL; + Empty FieldE; + double FieldD; + + public static LongEmptyDouble Get() + { + return new LongEmptyDouble { FieldL = 0xcafef00d, FieldD = 3.14159d }; + } + + public bool Equals(LongEmptyDouble other) + { + return FieldL == other.FieldL && FieldD.Equals(other.FieldD); + } +} + +struct NestedEmpty +{ + struct InnerEmpty + { + Empty e; + } + InnerEmpty e; +} + +struct NestedEmptyFloatDouble +{ + NestedEmpty FieldNE; + float FieldF; + double FieldD; + + public static NestedEmptyFloatDouble Get() + { + return new NestedEmptyFloatDouble { FieldF = 3.14159f, FieldD = 3.14159 }; + } + + public bool Equals(NestedEmptyFloatDouble other) + { + return FieldF.Equals(other.FieldF) && FieldD.Equals(other.FieldD); + } +} + +struct EmptyIntAndFloat +{ + struct EmptyInt + { + public Empty FieldE; + public int FieldI; + } + EmptyInt FieldEI; + float FieldF; + + public static EmptyIntAndFloat Get() + { + return new EmptyIntAndFloat { FieldEI = new EmptyInt { FieldI = 0xcafe }, FieldF = 3.14159f }; + } + + public bool Equals(EmptyIntAndFloat other) + { + return FieldEI.FieldI == other.FieldEI.FieldI && FieldF.Equals(other.FieldF); + } +} + +struct LongEmptyAndFloat +{ + struct LongEmpty + { + public long FieldL; + public Empty FieldE; + } + LongEmpty FieldLE; + float FieldF; + + public static LongEmptyAndFloat Get() + { + return new LongEmptyAndFloat { FieldLE = new LongEmpty { FieldL = 0xcafef00d }, FieldF = 3.14159f }; + } + + public bool Equals(LongEmptyAndFloat other) + { + return FieldLE.FieldL == other.FieldLE.FieldL && FieldF.Equals(other.FieldF); + } +} + +[InlineArray(1)] +struct ArrayOfEmpties +{ + Empty e; +}; + +struct ArrayOfEmptiesFloatDouble +{ + ArrayOfEmpties FieldAoE; + float FieldF; + double FieldD; + + public static ArrayOfEmptiesFloatDouble Get() + { + return new ArrayOfEmptiesFloatDouble { FieldF = 3.14159f, FieldD = 3.14159 }; + } + + public bool Equals(ArrayOfEmptiesFloatDouble other) + { + return FieldF.Equals(other.FieldF) && FieldD.Equals(other.FieldD); + } +} + +struct Sixteen +{ + T e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16; +} + +struct FloatEmptyMegabyteInt +{ + float FieldF; + Sixteen>>>> FieldEmptyMegabyte; + int FieldI; + + public static FloatEmptyMegabyteInt Get() + { + return new FloatEmptyMegabyteInt { FieldF = 3.14159f, FieldI = 0xcafe }; + } + + public bool Equals(FloatEmptyMegabyteInt other) + { + return FieldF.Equals(other.FieldF) && FieldI == other.FieldI; + } +} + +[StructLayout(LayoutKind.Sequential, Pack=1)] +struct PackedEmptyFloatLong +{ + Empty FieldE; + float FieldF; + long FieldL; + + public static PackedEmptyFloatLong Get() + { + return new PackedEmptyFloatLong { FieldF = 3.14159f, FieldL = 0xcafef00d }; + } + + public bool Equals(PackedEmptyFloatLong other) + { + return FieldF.Equals(other.FieldF) && FieldL == other.FieldL; + } +} + +[StructLayout(LayoutKind.Explicit)] +struct ExplicitFloatLong +{ + [FieldOffset(1)] float FieldF; + [FieldOffset(5)] long FieldL; + + public static ExplicitFloatLong Get() + { + return new ExplicitFloatLong { FieldF = 3.14159f, FieldL = 0xcafef00d }; + } + + public bool Equals(ExplicitFloatLong other) + { + return FieldF.Equals(other.FieldF) && FieldL == other.FieldL; + } +} + + public static partial class StructABI { [DllImport("StructABILib")] @@ -799,6 +969,40 @@ public static partial class StructABI [DllImport("StructABILib")] static extern DoubleAndByte EnoughRegistersSysV4(double a, double b, double c, double d, double e, double f, double g, DoubleAndByte value); + [DllImport("StructABILib")] + static extern LongEmptyDouble EchoLongEmptyDoubleRiscV(LongEmptyDouble value); + + [DllImport("StructABILib")] + static extern LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscV( + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble byRef); + + [DllImport("StructABILib")] + static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscV(NestedEmptyFloatDouble fa0_fa1); + + [DllImport("StructABILib")] + static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscV( + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a0_a1); + + [DllImport("StructABILib")] + static extern EmptyIntAndFloat EchoEmptyIntAndFloatRiscV(EmptyIntAndFloat a0_fa0); + + [DllImport("StructABILib")] + static extern LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(LongEmptyAndFloat a0_fa0); + + [DllImport("StructABILib")] + static extern ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscV( + ArrayOfEmptiesFloatDouble a0_a1); + + [DllImport("StructABILib")] + static extern FloatEmptyMegabyteInt EchoFloatEmptyMegabyteIntRiscV( + FloatEmptyMegabyteInt fa0_a0); + + [DllImport("StructABILib")] + static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(PackedEmptyFloatLong fa0_a0); + + [DllImport("StructABILib")] + static extern ExplicitFloatLong EchoExplicitFloatLongRiscV(ExplicitFloatLong fa0_a0); + //////////////////////////////////////////////////////////////////////////// // Managed echo tests. //////////////////////////////////////////////////////////////////////////// @@ -1062,6 +1266,70 @@ static DoubleAndByte EnoughRegistersSysV4Managed(double a, double b, double c, d return value; } + [MethodImpl(MethodImplOptions.NoInlining)] + static LongEmptyDouble EchoLongEmptyDoubleRiscVManaged(LongEmptyDouble a0_fa0) + { + return a0_fa0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscVManaged( + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble byRef) + { + return byRef; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscVManaged(NestedEmptyFloatDouble fa0_fa1) + { + return fa0_fa1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscVManaged( + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a0_a1) + { + return a0_a1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static EmptyIntAndFloat EchoEmptyIntAndFloatRiscVManaged(EmptyIntAndFloat a0_fa0) + { + return a0_fa0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static LongEmptyAndFloat EchoLongEmptyAndFloatRiscVManaged(LongEmptyAndFloat a0_fa0) + { + return a0_fa0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVManaged( + ArrayOfEmptiesFloatDouble a0_a1) + { + return a0_a1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static FloatEmptyMegabyteInt EchoFloatEmptyMegabyteIntRiscVManaged( + FloatEmptyMegabyteInt fa0_a0) + { + return fa0_a0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscVManaged(PackedEmptyFloatLong fa0_a0) + { + return fa0_a0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static ExplicitFloatLong EchoExplicitFloatLongRiscVManaged(ExplicitFloatLong fa0_a0) + { + return fa0_a0; + } + //////////////////////////////////////////////////////////////////////////// // Wrapper methods // @@ -2154,6 +2422,226 @@ static bool EnoughRegistersSysV4Wrapper() return ok; } + static bool EchoLongEmptyDoubleRiscVWrapper() + { + bool ok = true; + LongEmptyDouble expected = LongEmptyDouble.Get(); + LongEmptyDouble native = EchoLongEmptyDoubleRiscV(expected); + LongEmptyDouble managed = EchoLongEmptyDoubleRiscVManaged(expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoLongEmptyDoubleRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoLongEmptyDoubleRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoLongEmptyDoubleByImplicitRefRiscVWrapper() + { + bool ok = true; + LongEmptyDouble expected = LongEmptyDouble.Get(); + LongEmptyDouble native = EchoLongEmptyDoubleByImplicitRefRiscV(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + LongEmptyDouble managed = EchoLongEmptyDoubleByImplicitRefRiscVManaged(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoLongEmptyDoubleByImplicitRefRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoLongEmptyDoubleByImplicitRefRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoNestedEmptyFloatDoubleRiscVWrapper() + { + bool ok = true; + NestedEmptyFloatDouble expected = NestedEmptyFloatDouble.Get(); + NestedEmptyFloatDouble native = EchoNestedEmptyFloatDoubleRiscV(expected); + NestedEmptyFloatDouble managed = EchoNestedEmptyFloatDoubleRiscVManaged(expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoNestedEmptyFloatDoubleRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoNestedEmptyFloatDoubleRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper() + { + bool ok = true; + NestedEmptyFloatDouble expected = NestedEmptyFloatDouble.Get(); + NestedEmptyFloatDouble native = EchoNestedEmptyFloatDoubleInIntegerRegsRiscV(0f, 1f, 2f, 3f, 4f, 5f, 6f, expected); + NestedEmptyFloatDouble managed = EchoNestedEmptyFloatDoubleInIntegerRegsRiscVManaged(0f, 1f, 2f, 3f, 4f, 5f, 6f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoNestedEmptyFloatDoubleInIntegerRegsRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoNestedEmptyFloatDoubleInIntegerRegsRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoEmptyIntAndFloatRiscVWrapper() + { + bool ok = true; + EmptyIntAndFloat expected = EmptyIntAndFloat.Get(); + EmptyIntAndFloat native = EchoEmptyIntAndFloatRiscV(expected); + EmptyIntAndFloat managed = EchoEmptyIntAndFloatRiscVManaged(expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmptyIntAndFloatRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmptyIntAndFloatRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoLongEmptyAndFloatRiscVWrapper() + { + bool ok = true; + LongEmptyAndFloat expected = LongEmptyAndFloat.Get(); + LongEmptyAndFloat native = EchoLongEmptyAndFloatRiscV(expected); + LongEmptyAndFloat managed = EchoLongEmptyAndFloatRiscVManaged(expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoLongEmptyAndFloatRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoLongEmptyAndFloatRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVWrapper() + { + bool ok = true; + ArrayOfEmptiesFloatDouble expected = ArrayOfEmptiesFloatDouble.Get(); + ArrayOfEmptiesFloatDouble native = EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscV(expected); + ArrayOfEmptiesFloatDouble managed = EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVManaged(expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoArrayOfEmptiesFloatDoubleInIntegerRegs failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoArrayOfEmptiesFloatDoubleInIntegerRegs failed"); + ok = false; + } + + return ok; + } + + static bool EchoFloatEmptyMegabyteIntRiscVWrapper() + { + bool ok = true; + FloatEmptyMegabyteInt expected = FloatEmptyMegabyteInt.Get(); + FloatEmptyMegabyteInt native = EchoFloatEmptyMegabyteIntRiscV(expected); + FloatEmptyMegabyteInt managed = EchoFloatEmptyMegabyteIntRiscVManaged(expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoFloatEmptyMegabyteInt failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoFloatEmptyMegabyteInt failed"); + ok = false; + } + + return ok; + } + + static bool EchoPackedEmptyFloatLongRiscVWrapper() + { + bool ok = true; + PackedEmptyFloatLong expected = PackedEmptyFloatLong.Get(); + PackedEmptyFloatLong native = EchoPackedEmptyFloatLongRiscV(expected); + PackedEmptyFloatLong managed = EchoPackedEmptyFloatLongRiscVManaged(expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoPackedEmptyFloatLong failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoPackedEmptyFloatLong failed"); + ok = false; + } + + return ok; + } + + static bool EchoExplicitFloatLongRiscVWrapper() + { + bool ok = true; + ExplicitFloatLong expected = ExplicitFloatLong.Get(); + ExplicitFloatLong native = EchoExplicitFloatLongRiscV(expected); + ExplicitFloatLong managed = EchoExplicitFloatLongRiscVManaged(expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoExplicitFloatLong failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoExplicitFloatLong failed"); + ok = false; + } + + return ok; + } + [Fact] public static int TestEntryPoint() { @@ -2202,6 +2690,15 @@ public static int TestEntryPoint() if (!EnoughRegistersSysV2Wrapper()) ok = false; if (!EnoughRegistersSysV3Wrapper()) ok = false; if (!EnoughRegistersSysV4Wrapper()) ok = false; + if (!EchoLongEmptyDoubleRiscVWrapper()) ok = false; + if (!EchoLongEmptyDoubleByImplicitRefRiscVWrapper()) ok = false; + if (!EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoEmptyIntAndFloatRiscVWrapper()) ok = false; + if (!EchoLongEmptyAndFloatRiscVWrapper()) ok = false; + if (!EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoFloatEmptyMegabyteIntRiscVWrapper()) ok = false; + if (!EchoPackedEmptyFloatLongRiscVWrapper()) ok = false; + if (!EchoExplicitFloatLongRiscVWrapper()) ok = false; return ok ? 100 : -1; } From 03d4b231fddbecf3cfa695e7dba210fdd8a4448b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 6 May 2024 09:07:49 +0200 Subject: [PATCH 02/60] Reduce empty megabyte field to 32k as msvc caps size of arguments at 64k --- .../JIT/Directed/StructABI/StructABI.cpp | 10 +++--- src/tests/JIT/Directed/StructABI/StructABI.cs | 36 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cpp b/src/tests/JIT/Directed/StructABI/StructABI.cpp index 428d90f18be126..bd600653b94a14 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cpp +++ b/src/tests/JIT/Directed/StructABI/StructABI.cpp @@ -72,15 +72,15 @@ struct ArrayOfEmptiesFloatDouble }; template -struct Sixteen +struct Eight { - T e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16; + T e1, e2, e3, e4, e5, e6, e7, e8; }; -struct FloatEmptyMegabyteInt +struct FloatEmpty32kInt { float FieldF; - Sixteen>>>> FieldEmptyMegabyte; + Eight>>>> FieldEmpty32k; int32_t FieldI; }; @@ -141,7 +141,7 @@ DLLEXPORT ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleInIntegerRegsRi return a0_a1; } -DLLEXPORT FloatEmptyMegabyteInt EchoFloatEmptyMegabyteIntRiscV(FloatEmptyMegabyteInt fa0_a0) +DLLEXPORT FloatEmpty32kInt EchoFloatEmpty32kIntRiscV(FloatEmpty32kInt fa0_a0) { return fa0_a0; } diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index b89d2edde7f1c5..68c83d73325c58 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -780,23 +780,23 @@ public bool Equals(ArrayOfEmptiesFloatDouble other) } } -struct Sixteen +struct Eight { - T e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16; + T e1, e2, e3, e4, e5, e6, e7, e8; } -struct FloatEmptyMegabyteInt +struct FloatEmpty32kInt { float FieldF; - Sixteen>>>> FieldEmptyMegabyte; + Eight>>>> FieldEmpty32k; int FieldI; - public static FloatEmptyMegabyteInt Get() + public static FloatEmpty32kInt Get() { - return new FloatEmptyMegabyteInt { FieldF = 3.14159f, FieldI = 0xcafe }; + return new FloatEmpty32kInt { FieldF = 3.14159f, FieldI = 0xcafe }; } - public bool Equals(FloatEmptyMegabyteInt other) + public bool Equals(FloatEmpty32kInt other) { return FieldF.Equals(other.FieldF) && FieldI == other.FieldI; } @@ -994,8 +994,8 @@ static extern ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleInIntegerRe ArrayOfEmptiesFloatDouble a0_a1); [DllImport("StructABILib")] - static extern FloatEmptyMegabyteInt EchoFloatEmptyMegabyteIntRiscV( - FloatEmptyMegabyteInt fa0_a0); + static extern FloatEmpty32kInt EchoFloatEmpty32kIntRiscV( + FloatEmpty32kInt fa0_a0); [DllImport("StructABILib")] static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(PackedEmptyFloatLong fa0_a0); @@ -1312,8 +1312,8 @@ static ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscV } [MethodImpl(MethodImplOptions.NoInlining)] - static FloatEmptyMegabyteInt EchoFloatEmptyMegabyteIntRiscVManaged( - FloatEmptyMegabyteInt fa0_a0) + static FloatEmpty32kInt EchoFloatEmpty32kIntRiscVManaged( + FloatEmpty32kInt fa0_a0) { return fa0_a0; } @@ -2576,22 +2576,22 @@ static bool EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVWrapper() return ok; } - static bool EchoFloatEmptyMegabyteIntRiscVWrapper() + static bool EchoFloatEmpty32kIntRiscVWrapper() { bool ok = true; - FloatEmptyMegabyteInt expected = FloatEmptyMegabyteInt.Get(); - FloatEmptyMegabyteInt native = EchoFloatEmptyMegabyteIntRiscV(expected); - FloatEmptyMegabyteInt managed = EchoFloatEmptyMegabyteIntRiscVManaged(expected); + FloatEmpty32kInt expected = FloatEmpty32kInt.Get(); + FloatEmpty32kInt native = EchoFloatEmpty32kIntRiscV(expected); + FloatEmpty32kInt managed = EchoFloatEmpty32kIntRiscVManaged(expected); if (!expected.Equals(native)) { - Console.WriteLine("Native call for EchoFloatEmptyMegabyteInt failed"); + Console.WriteLine("Native call for EchoFloatEmpty32kInt failed"); ok = false; } if (!expected.Equals(managed)) { - Console.WriteLine("Managed call for EchoFloatEmptyMegabyteInt failed"); + Console.WriteLine("Managed call for EchoFloatEmpty32kInt failed"); ok = false; } @@ -2696,7 +2696,7 @@ public static int TestEntryPoint() if (!EchoEmptyIntAndFloatRiscVWrapper()) ok = false; if (!EchoLongEmptyAndFloatRiscVWrapper()) ok = false; if (!EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVWrapper()) ok = false; - if (!EchoFloatEmptyMegabyteIntRiscVWrapper()) ok = false; + if (!EchoFloatEmpty32kIntRiscVWrapper()) ok = false; if (!EchoPackedEmptyFloatLongRiscVWrapper()) ok = false; if (!EchoExplicitFloatLongRiscVWrapper()) ok = false; From 4403c4ba4504d4046a5bc62eafc80cfaa4331f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 6 May 2024 11:17:37 +0200 Subject: [PATCH 03/60] Don't stop calculating flags if struct size > 16 bytes --- .../Common/JitInterface/RISCV64PassStructInRegister.cs | 7 +------ src/coreclr/vm/methodtable.cpp | 3 --- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index fae694f8768fc9..d7f1001b4e2146 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -11,9 +11,7 @@ namespace Internal.JitInterface { internal static class RISCV64PassStructInRegister { - private const int - ENREGISTERED_PARAMTYPE_MAXSIZE = 16, - TARGET_POINTER_SIZE = 8; + private const int TARGET_POINTER_SIZE = 8; private static bool HandleInlineArray(int elementTypeIndex, int nElements, Span types, ref int typeIndex) { @@ -90,9 +88,6 @@ private static bool FlattenFieldTypes(TypeDesc td, Span ENREGISTERED_PARAMTYPE_MAXSIZE) - return (uint)STRUCT_NO_FLOAT_FIELD; - Span types = stackalloc StructFloatFieldInfoFlags[] { STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD }; diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index a20835d2d0114a..472d1773e7f705 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3567,9 +3567,6 @@ static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], int MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle th) { - if (th.GetSize() > ENREGISTERED_PARAMTYPE_MAXSIZE) - return STRUCT_NO_FLOAT_FIELD; - StructFloatFieldInfoFlags types[2] = {STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD}; int nFields = 0; if (!FlattenFieldTypes(th, types, nFields) || nFields == 0) From bf811163bc607a40c72f4f318820e6dbe6b26465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 8 May 2024 12:59:22 +0200 Subject: [PATCH 04/60] Classify empty structs on x64 like padding The current implementation barred a struct containing empty struct fields from enregistration. This did not match the [System V ABI](https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf) which says "NO_CLASS This class is used as initializer in the algorithms. It will be used for padding and **empty structures** and unions". It also does not match the behavior of GCC & Clang on Linux. --- .../SystemVStructClassificator.cs | 19 +++++++++--- src/coreclr/vm/methodtable.cpp | 31 ++++++++++++++----- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs b/src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs index bfb88cf877537e..f8a2235096c5f2 100644 --- a/src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs +++ b/src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using ILCompiler; using Internal.TypeSystem; +using System.Runtime.CompilerServices; using static Internal.JitInterface.SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR; using static Internal.JitInterface.SystemVClassificationType; @@ -207,7 +208,10 @@ private static bool ClassifyEightBytes(TypeDesc typeDesc, if (numIntroducedFields == 0) { - return false; + // Classify empty struct like padding + helper.LargestFieldOffset = startOffsetOfStruct; + AssignClassifiedEightByteTypes(ref helper); + return true; } // The SIMD and Int128 Intrinsic types are meant to be handled specially and should not be passed as struct registers @@ -375,8 +379,12 @@ private static void AssignClassifiedEightByteTypes(ref SystemVStructRegisterPass // Calculate the eightbytes and their types. int lastFieldOrdinal = sortedFieldOrder[largestFieldOffset]; - int offsetAfterLastFieldByte = largestFieldOffset + helper.FieldSizes[lastFieldOrdinal]; - SystemVClassificationType lastFieldClassification = helper.FieldClassifications[lastFieldOrdinal]; + int lastFieldSize = (lastFieldOrdinal >= 0) ? helper.FieldSizes[lastFieldOrdinal] : 0; + int offsetAfterLastFieldByte = largestFieldOffset + lastFieldSize; + Debug.Assert(offsetAfterLastFieldByte <= helper.StructSize); + SystemVClassificationType lastFieldClassification = (lastFieldOrdinal >= 0) + ? helper.FieldClassifications[lastFieldOrdinal] + : SystemVClassificationTypeNoClass; int usedEightBytes = 0; int accumulatedSizeForEightBytes = 0; @@ -403,6 +411,8 @@ private static void AssignClassifiedEightByteTypes(ref SystemVStructRegisterPass // the SysV ABI spec. fieldSize = 1; fieldClassificationType = offset < offsetAfterLastFieldByte ? SystemVClassificationTypeNoClass : lastFieldClassification; + if (offset % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0) // new eightbyte + foundFieldInEightByte = false; } else { @@ -455,7 +465,8 @@ private static void AssignClassifiedEightByteTypes(ref SystemVStructRegisterPass } } - if ((offset + 1) % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0) // If we just finished checking the last byte of an eightbyte + // If we just finished checking the last byte of an eightbyte or the entire struct + if ((offset + 1) % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0 || (offset + 1) == helper.StructSize) { if (!foundFieldInEightByte) { diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 472d1773e7f705..6cd502e26a5dc5 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2212,11 +2212,14 @@ bool MethodTable::ClassifyEightBytesWithManagedLayout(SystemVStructRegisterPassi DWORD numIntroducedFields = GetNumIntroducedInstanceFields(); - // It appears the VM gives a struct with no fields of size 1. - // Don't pass in register such structure. if (numIntroducedFields == 0) { - return false; + helperPtr->largestFieldOffset = startOffsetOfStruct; + LOG((LF_JIT, LL_EVERYTHING, "%*s**** Classify empty struct %s (%p) like padding, startOffset %d, total struct size %d\n", + nestingLevel * 5, "", this->GetDebugClassName(), this, startOffsetOfStruct, helperPtr->structSize)); + + AssignClassifiedEightByteTypes(helperPtr, nestingLevel); + return true; } // The SIMD Intrinsic types are meant to be handled specially and should not be passed as struct registers @@ -2432,7 +2435,12 @@ bool MethodTable::ClassifyEightBytesWithNativeLayout(SystemVStructRegisterPassin // No fields. if (numIntroducedFields == 0) { - return false; + helperPtr->largestFieldOffset = startOffsetOfStruct; + LOG((LF_JIT, LL_EVERYTHING, "%*s**** Classify empty struct %s (%p) like padding, startOffset %d, total struct size %d\n", + nestingLevel * 5, "", this->GetDebugClassName(), this, startOffsetOfStruct, helperPtr->structSize)); + + AssignClassifiedEightByteTypes(helperPtr, nestingLevel); + return true; } bool hasImpliedRepeatedFields = HasImpliedRepeatedFields(this); @@ -2684,8 +2692,12 @@ void MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe // Calculate the eightbytes and their types. int lastFieldOrdinal = sortedFieldOrder[largestFieldOffset]; - unsigned int offsetAfterLastFieldByte = largestFieldOffset + helperPtr->fieldSizes[lastFieldOrdinal]; - SystemVClassificationType lastFieldClassification = helperPtr->fieldClassifications[lastFieldOrdinal]; + unsigned int lastFieldSize = (lastFieldOrdinal >= 0) ? helperPtr->fieldSizes[lastFieldOrdinal] : 0; + unsigned int offsetAfterLastFieldByte = largestFieldOffset + lastFieldSize; + _ASSERTE(offsetAfterLastFieldByte <= helperPtr->structSize); + SystemVClassificationType lastFieldClassification = (lastFieldOrdinal >= 0) + ? helperPtr->fieldClassifications[lastFieldOrdinal] + : SystemVClassificationTypeNoClass; unsigned int usedEightBytes = 0; unsigned int accumulatedSizeForEightBytes = 0; @@ -2712,6 +2724,8 @@ void MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe // the SysV ABI spec. fieldSize = 1; fieldClassificationType = offset < offsetAfterLastFieldByte ? SystemVClassificationTypeNoClass : lastFieldClassification; + if (offset % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0) // new eightbyte + foundFieldInEightByte = false; } else { @@ -2764,7 +2778,8 @@ void MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe } } - if ((offset + 1) % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0) // If we just finished checking the last byte of an eightbyte + // If we just finished checking the last byte of an eightbyte or the entire struct + if ((offset + 1) % SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES == 0 || (offset + 1) == helperPtr->structSize) { if (!foundFieldInEightByte) { @@ -2803,9 +2818,9 @@ void MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe LOG((LF_JIT, LL_EVERYTHING, " **** Number EightBytes: %d\n", helperPtr->eightByteCount)); for (unsigned i = 0; i < helperPtr->eightByteCount; i++) { - _ASSERTE(helperPtr->eightByteClassifications[i] != SystemVClassificationTypeNoClass); LOG((LF_JIT, LL_EVERYTHING, " **** eightByte %d -- classType: %s, eightByteOffset: %d, eightByteSize: %d\n", i, GetSystemVClassificationTypeName(helperPtr->eightByteClassifications[i]), helperPtr->eightByteOffsets[i], helperPtr->eightByteSizes[i])); + _ASSERTE(helperPtr->eightByteClassifications[i] != SystemVClassificationTypeNoClass); } #endif // _DEBUG } From 60ad8349ddb37d3757ada52915f7fddcccaa5d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 8 May 2024 13:18:27 +0200 Subject: [PATCH 05/60] Quell C-linkage warnings just in case --- src/tests/JIT/Directed/StructABI/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/JIT/Directed/StructABI/CMakeLists.txt b/src/tests/JIT/Directed/StructABI/CMakeLists.txt index 35f73d8d208fa8..1b2006ac2ef18b 100644 --- a/src/tests/JIT/Directed/StructABI/CMakeLists.txt +++ b/src/tests/JIT/Directed/StructABI/CMakeLists.txt @@ -5,7 +5,7 @@ if(CLR_CMAKE_HOST_WIN32) set_source_files_properties(StructABI.c PROPERTIES COMPILE_OPTIONS /TC) # compile as C else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") - set(CMAKE_CPP_FLAGS "${CMAKE_CPP_FLAGS} -fvisibility=hidden") + set(CMAKE_CPP_FLAGS "${CMAKE_CPP_FLAGS} -fvisibility=hidden -Wno-return-type-c-linkage") endif() # add the executable From 9064784564938a245f0885b3c64901ca7477b295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 9 May 2024 13:30:10 +0000 Subject: [PATCH 06/60] Add Pack=1 to bypass a known problem with field alignment requirements on ARM 32 --- src/tests/JIT/Directed/StructABI/StructABI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index 68c83d73325c58..e054c6281bf26a 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -820,7 +820,7 @@ public bool Equals(PackedEmptyFloatLong other) } } -[StructLayout(LayoutKind.Explicit)] +[StructLayout(LayoutKind.Explicit, Pack=1)] struct ExplicitFloatLong { [FieldOffset(1)] float FieldF; From b02c9134a81ecef56da8eea1ac586d480f64c010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 10 May 2024 15:45:07 +0200 Subject: [PATCH 07/60] Check `size > 16` only when passing parameters according to integer calling convention, in ArgIterator::GetNextOffset and RiscV64Classifier --- src/coreclr/jit/targetriscv64.cpp | 9 ++-- src/coreclr/vm/callingconvention.h | 81 +++++++++++++----------------- 2 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index 581087b5cc63f8..1f721bb1ed3758 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -65,11 +65,7 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, if (varTypeIsStruct(type)) { passedSize = structLayout->GetSize(); - if (passedSize > MAX_PASS_MULTIREG_BYTES) - { - passedSize = TARGET_POINTER_SIZE; // pass by reference - } - else if (!structLayout->IsBlockLayout()) + if (!structLayout->IsBlockLayout()) { flags = (StructFloatFieldInfoFlags)comp->info.compCompHnd->getRISCV64PassStructInRegisterFlags( structLayout->GetClassHandle()); @@ -154,6 +150,9 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, } }; + if (passedSize > MAX_PASS_MULTIREG_BYTES) + passedSize = TARGET_POINTER_SIZE; // pass by implicit reference + if (passedSize <= TARGET_POINTER_SIZE) { return ABIPassingInformation::FromSegment(comp, passSlot(0, passedSize)); diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 7ec2525d48ef2c..6ec9d4fcdcef3c 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -1777,59 +1777,37 @@ int ArgIteratorTemplate::GetNextOffset() return argOfs; #elif defined(TARGET_RISCV64) - + assert(!this->IsVarArg()); // Varargs on RISC-V not supported yet int cFPRegs = 0; int flags = 0; switch (argType) { - case ELEMENT_TYPE_R4: - // 32-bit floating point argument. - cFPRegs = 1; - break; - case ELEMENT_TYPE_R8: - // 64-bit floating point argument. + // Floating point argument cFPRegs = 1; break; case ELEMENT_TYPE_VALUETYPE: - { - // Handle struct which containing floats or doubles that can be passed - // in FP registers if possible. - - // Composite greater than 16bytes should be passed by reference - if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) + flags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType); + if (flags != STRUCT_NO_FLOAT_FIELD) { - argSize = sizeof(TADDR); + // Struct may be passed according to hardware floating-point calling convention + cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1; } - else - { - flags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType); - if (flags & STRUCT_HAS_FLOAT_FIELDS_MASK) - { - cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1; - } - } - break; - } default: break; } - const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE); - const bool isFloatHfa = thValueType.IsFloatHfa(); - const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa); - - if (cFPRegs > 0 && !this->IsVarArg()) + if (cFPRegs > 0) { + // Pass according to hardware floating-point calling convention iff the argument can be fully enregistered if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) { assert(cFPRegs == 1); - assert((STRUCT_FLOAT_FIELD_FIRST == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK)) || (STRUCT_FLOAT_FIELD_SECOND == (flags & STRUCT_HAS_FLOAT_FIELDS_MASK))); if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS)) { @@ -1868,25 +1846,36 @@ int ArgIteratorTemplate::GetNextOffset() } } + // Pass according to integer calling convention + + if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) + argSize = sizeof(TADDR); // pass by implicit reference + + const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE); + const bool isFloatHfa = thValueType.IsFloatHfa(); + const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa); + + const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; + assert(regSlots <= 2); + if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS) // pass in register(s) { - const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; - if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS) - { - int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; - m_idxGenReg += regSlots; - return argOfs; - } - else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS) - { - int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; - m_ofsStack += (m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS)*8; - assert(m_ofsStack == 8); - m_idxGenReg = NUM_ARGUMENT_REGISTERS; - return argOfs; - } + int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + m_idxGenReg += regSlots; + return argOfs; } + else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS) // pass split; head in register, tail on stack + { + assert(m_idxGenReg + 1 == NUM_ARGUMENT_REGISTERS); // last argument register should be free + assert((m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS) == 1); // one stack slot needed + assert(m_ofsStack == 0); // tail of a split argument should be the first slot on the stack - int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack; + int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + m_ofsStack = 8; + m_idxGenReg = NUM_ARGUMENT_REGISTERS; + return argOfs; + } + + int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack; // pass entirely on stack m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE); return argOfs; From 0fde22d042a6ac9a27975fd92b2ac8c08d19ae88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 13 May 2024 15:15:55 +0200 Subject: [PATCH 08/60] Don't assume struct size > 16 means return by implicit ref to a return buffer; always calculate GetRiscV64PassStructInRegisterFlags in ComputeReturnFlags\(\) --- src/coreclr/vm/callingconvention.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 6ec9d4fcdcef3c..f18391bc9d7931 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -2002,12 +2002,12 @@ void ArgIteratorTemplate::ComputeReturnFlags() break; } #elif defined(TARGET_RISCV64) - if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) - { - assert(!thValueType.IsTypeDesc()); - flags = (MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType) & 0xff) << RETURN_FP_SIZE_SHIFT; + assert(!thValueType.IsTypeDesc()); + int structFlags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType); + flags |= (structFlags & 0xff) << RETURN_FP_SIZE_SHIFT; + + if (structFlags != STRUCT_NO_FLOAT_FIELD || size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) break; - } #else if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) break; From f89a68dd92e0b759909bfebffccfbe081624184e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 13 May 2024 16:29:31 +0200 Subject: [PATCH 09/60] Don't assume if struct size > 16, IsArgPassedByRef should return true. We still need to look at GetRiscV64PassStructInRegisterFlags to rule out passing in registers according to hw FP call conv --- src/coreclr/vm/callingconvention.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index f18391bc9d7931..9c501b08df7941 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -529,7 +529,13 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE // Composites greater than 16 bytes are passed by reference return (size > ENREGISTERED_PARAMTYPE_MAXSIZE); #elif defined(TARGET_RISCV64) - return (size > ENREGISTERED_PARAMTYPE_MAXSIZE); + if (th.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE) + { + int flags = MethodTable::GetRiscV64PassStructInRegisterFlags(th); + return (flags == STRUCT_NO_FLOAT_FIELD) && (size > ENREGISTERED_PARAMTYPE_MAXSIZE); + } + assert(size <= ENREGISTERED_PARAMTYPE_MAXSIZE); + return FALSE; #else PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef"); return FALSE; @@ -588,7 +594,12 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE if (m_argType == ELEMENT_TYPE_VALUETYPE) { _ASSERTE(!m_argTypeHandle.IsNull()); + #ifdef TARGET_RISCV64 + int flags = MethodTable::GetRiscV64PassStructInRegisterFlags(m_argTypeHandle); + return (flags == STRUCT_NO_FLOAT_FIELD) && (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE); + #else return (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE); + #endif } return FALSE; #else From f5e393022364698036c9ab2f56718bd0cfdcd079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 14 May 2024 09:13:35 +0200 Subject: [PATCH 10/60] Adjust ArgIterator.GetNextOffset() for crossgen2 to be the same as the native version for VM --- .../ReadyToRun/ArgIterator.cs | 122 ++++++++---------- src/coreclr/vm/callingconvention.h | 56 ++++---- 2 files changed, 83 insertions(+), 95 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index eee92e23a86824..5253928f484e01 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -12,6 +12,7 @@ using Internal.NativeFormat; using Internal.TypeSystem; using Internal.CorConstants; +using static Internal.JitInterface.StructFloatFieldInfoFlags; namespace ILCompiler.DependencyAnalysis.ReadyToRun @@ -1449,58 +1450,42 @@ public int GetNextOffset() case TargetArchitecture.RiscV64: { + if (IsVarArg) + throw new NotImplementedException("Varargs on RISC-V not supported yet"); + int cFPRegs = 0; - uint floatFieldFlags = (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD; + StructFloatFieldInfoFlags flags = STRUCT_NO_FLOAT_FIELD; _hasArgLocDescForStructInRegs = false; switch (argType) { case CorElementType.ELEMENT_TYPE_R4: - // 32-bit floating point argument. - cFPRegs = 1; - break; - case CorElementType.ELEMENT_TYPE_R8: - // 64-bit floating point argument. + // Floating point argument cFPRegs = 1; break; case CorElementType.ELEMENT_TYPE_VALUETYPE: + flags = (StructFloatFieldInfoFlags)RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle()); + if (flags != STRUCT_NO_FLOAT_FIELD) { - // Composite greater than 16 bytes should be passed by reference - if (argSize > _transitionBlock.EnregisteredParamTypeMaxSize) - { - argSize = _transitionBlock.PointerSize; - } - else - { - floatFieldFlags = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle()); - if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) - { - cFPRegs = 2; - } - else if ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) - { - cFPRegs = 1; - } - } - - break; + // Struct may be passed according to hardware floating-point calling convention + cFPRegs = ((flags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) ? 2 : 1; } + break; default: break; } - bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE); - int cbArg = _transitionBlock.StackElemSize(argSize, isValueType, false); - - if (cFPRegs > 0 && !IsVarArg) + if (cFPRegs > 0) { - if (isValueType && ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_HAS_ONE_FLOAT_MASK) != 0)) + // Pass according to hardware floating-point calling convention iff the argument can be fully enregistered + if ((flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) != 0) { Debug.Assert(cFPRegs == 1); - if ((_riscv64IdxFPReg < 8) && (_riscv64IdxGenReg < 8)) + + if ((1 + _riscv64IdxFPReg <= _transitionBlock.NumArgumentRegisters) && (1 + _riscv64IdxGenReg <= _transitionBlock.NumArgumentRegisters)) { _argLocDescForStructInRegs = new ArgLocDesc(); _argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg; @@ -1509,64 +1494,67 @@ public int GetNextOffset() _argLocDescForStructInRegs.m_idxGenReg = _riscv64IdxGenReg; _argLocDescForStructInRegs.m_cGenReg = 1; + _argLocDescForStructInRegs.m_floatFlags = (uint)flags; _hasArgLocDescForStructInRegs = true; - _argLocDescForStructInRegs.m_floatFlags = floatFieldFlags; - int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8; + int regOffset = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * _transitionBlock.FloatRegisterSize; _riscv64IdxFPReg++; _riscv64IdxGenReg++; - return argOfsInner; + return regOffset; } } - else if (cFPRegs + _riscv64IdxFPReg <= 8) + else if (cFPRegs + _riscv64IdxFPReg <= _transitionBlock.NumArgumentRegisters) { - // Each floating point register in the argument area is 8 bytes. - int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8; - if (floatFieldFlags == (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO) + int regOffset = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * _transitionBlock.FloatRegisterSize; + if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two single-float fields { - // struct with two single-float fields. _argLocDescForStructInRegs = new ArgLocDesc(); _argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg; _argLocDescForStructInRegs.m_cFloatReg = 2; Debug.Assert(cFPRegs == 2); - Debug.Assert(argSize == 8); + Debug.Assert(argSize == 2 * 4); + _argLocDescForStructInRegs.m_floatFlags = (uint)STRUCT_FLOAT_FIELD_ONLY_TWO; _hasArgLocDescForStructInRegs = true; - _argLocDescForStructInRegs.m_floatFlags = (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO; } _riscv64IdxFPReg += cFPRegs; - return argOfsInner; - } - else - { - _riscv64IdxFPReg = 8; + return regOffset; } } - { - Debug.Assert((cbArg % _transitionBlock.PointerSize) == 0); + // Pass according to integer calling convention - int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize; - // Only a0-a7 are valid argument registers. - if (_riscv64IdxGenReg + regSlots <= 8) - { - // The entirety of the arg fits in the register slots. - int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8; - _riscv64IdxGenReg += regSlots; - return argOfsInner; - } - else if (_riscv64IdxGenReg < 8) - { - int argOfsInner = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8; - _riscv64IdxGenReg = 8; - _riscv64OfsStack += 8; - return argOfsInner; - } + if (argSize > _transitionBlock.EnregisteredParamTypeMaxSize) + argSize = _transitionBlock.PointerSize; // pass by implicit reference + + bool isValueType = (argType == CorElementType.ELEMENT_TYPE_VALUETYPE); + int cbArg = _transitionBlock.StackElemSize(argSize, isValueType, false); + Debug.Assert((cbArg % _transitionBlock.PointerSize) == 0); + + int regSlots = ALIGN_UP(cbArg, _transitionBlock.PointerSize) / _transitionBlock.PointerSize; + Debug.Assert(regSlots <= 2); + if (_riscv64IdxGenReg + regSlots <= _transitionBlock.NumArgumentRegisters) // pass in register(s) + { + int regOffset = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * _transitionBlock.PointerSize; + _riscv64IdxGenReg += regSlots; + return regOffset; + } + else if (_riscv64IdxGenReg < _transitionBlock.NumArgumentRegisters) // pass split; head in register, tail on stack + { + Debug.Assert(regSlots == 2); + Debug.Assert(_riscv64IdxGenReg + 1 == _transitionBlock.NumArgumentRegisters, "last argument register should be free"); + Debug.Assert((_riscv64IdxGenReg + regSlots - _transitionBlock.NumArgumentRegisters) == 1, "one stack slot needed"); + Debug.Assert(_riscv64OfsStack == 0, "tail of a split argument should be the first slot on the stack"); + + int regOffset = _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * _transitionBlock.PointerSize; + _riscv64OfsStack = 8; + _riscv64IdxGenReg = _transitionBlock.NumArgumentRegisters; + return regOffset; } - argOfs = _transitionBlock.OffsetOfArgs + _riscv64OfsStack; - _riscv64OfsStack += cbArg; - return argOfs; + int stackOffset = _transitionBlock.OffsetOfArgs + _riscv64OfsStack; // pass entirely on stack + _riscv64OfsStack += ALIGN_UP(cbArg, _transitionBlock.PointerSize); + return stackOffset; } default: diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 9c501b08df7941..da7375eec66f7c 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -1790,7 +1790,7 @@ int ArgIteratorTemplate::GetNextOffset() #elif defined(TARGET_RISCV64) assert(!this->IsVarArg()); // Varargs on RISC-V not supported yet int cFPRegs = 0; - int flags = 0; + int flags = STRUCT_NO_FLOAT_FIELD; switch (argType) { @@ -1805,7 +1805,7 @@ int ArgIteratorTemplate::GetNextOffset() if (flags != STRUCT_NO_FLOAT_FIELD) { // Struct may be passed according to hardware floating-point calling convention - cFPRegs = (flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? 2 : 1; + cFPRegs = ((flags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) ? 2 : 1; } break; @@ -1816,44 +1816,44 @@ int ArgIteratorTemplate::GetNextOffset() if (cFPRegs > 0) { // Pass according to hardware floating-point calling convention iff the argument can be fully enregistered - if (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) + if ((flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) != 0) { assert(cFPRegs == 1); - if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (m_idxGenReg + 1 <= NUM_ARGUMENT_REGISTERS)) + if ((1 + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) && (1 + m_idxGenReg <= NUM_ARGUMENT_REGISTERS)) { m_argLocDescForStructInRegs.Init(); m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; m_argLocDescForStructInRegs.m_cFloatReg = 1; - int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; - m_idxFPReg += 1; - - m_argLocDescForStructInRegs.m_structFields = flags; m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg; m_argLocDescForStructInRegs.m_cGenReg = 1; - m_idxGenReg += 1; + m_argLocDescForStructInRegs.m_structFields = flags; m_hasArgLocDescForStructInRegs = true; - - return argOfs; + + int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; + m_idxFPReg++; + m_idxGenReg++; + return regOffset; } } else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) { - int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; - if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two float-fields. + int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; + if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two single-float fields { m_argLocDescForStructInRegs.Init(); - m_hasArgLocDescForStructInRegs = true; m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; - assert(cFPRegs == 2); m_argLocDescForStructInRegs.m_cFloatReg = 2; - assert(argSize == 8); + assert(cFPRegs == 2); + assert(argSize == 2 * 4); + m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO; + m_hasArgLocDescForStructInRegs = true; } m_idxFPReg += cFPRegs; - return argOfs; + return regOffset; } } @@ -1862,34 +1862,34 @@ int ArgIteratorTemplate::GetNextOffset() if (argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) argSize = sizeof(TADDR); // pass by implicit reference - const bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE); - const bool isFloatHfa = thValueType.IsFloatHfa(); - const int cbArg = StackElemSize(argSize, isValueType, isFloatHfa); + bool isValueType = (argType == ELEMENT_TYPE_VALUETYPE); + int cbArg = StackElemSize(argSize, isValueType, false); + assert((cbArg % TARGET_POINTER_SIZE) == 0); - const int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; + int regSlots = ALIGN_UP(cbArg, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; assert(regSlots <= 2); if (m_idxGenReg + regSlots <= NUM_ARGUMENT_REGISTERS) // pass in register(s) { - int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + int regOffset = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * TARGET_POINTER_SIZE; m_idxGenReg += regSlots; - return argOfs; + return regOffset; } else if (m_idxGenReg < NUM_ARGUMENT_REGISTERS) // pass split; head in register, tail on stack { + assert(regSlots == 2); assert(m_idxGenReg + 1 == NUM_ARGUMENT_REGISTERS); // last argument register should be free assert((m_idxGenReg + regSlots - NUM_ARGUMENT_REGISTERS) == 1); // one stack slot needed assert(m_ofsStack == 0); // tail of a split argument should be the first slot on the stack - int argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8; + int regOffset = TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * TARGET_POINTER_SIZE; m_ofsStack = 8; m_idxGenReg = NUM_ARGUMENT_REGISTERS; - return argOfs; + return regOffset; } - int argOfs = TransitionBlock::GetOffsetOfArgs() + m_ofsStack; // pass entirely on stack + int stackOffset = TransitionBlock::GetOffsetOfArgs() + m_ofsStack; // pass entirely on stack m_ofsStack += ALIGN_UP(cbArg, TARGET_POINTER_SIZE); - - return argOfs; + return stackOffset; #else PORTABILITY_ASSERT("ArgIteratorTemplate::GetNextOffset"); return TransitionBlock::InvalidOffset; From 0a5ce0538ac75da45a441b1f599096c89f051b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 14 May 2024 09:42:07 +0200 Subject: [PATCH 11/60] Adjust crossgen2 version of ComputeReturnFlags (ComputeReturnValueTreatment) --- .../ReadyToRun/TransitionBlock.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index 7f2b1fd25ee4fe..d79d9fe64f4776 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -385,15 +385,20 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp break; } } - - if (size <= EnregisteredReturnTypeIntegerMaxSize) + else if (IsLoongArch64) { - if (IsLoongArch64) + if (size <= EnregisteredReturnTypeIntegerMaxSize) + { fpReturnSize = LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff; - else if (IsRiscV64) - fpReturnSize = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff; - break; + break; + } + } + else if (IsRiscV64) + { + fpReturnSize = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff; + if (fpReturnSize != (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD || size <= EnregisteredReturnTypeIntegerMaxSize) + break; } } From 6a3118b65f0844ccfc4ef9ebf329db4e46796084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 14 May 2024 10:27:37 +0200 Subject: [PATCH 12/60] Adjust IsArgPassedByRef for crossgen2 with native version for VM. Also don't calculate GetRiscV64PassStructInRegisterFlags if struct fits in 16 bytes because we don't care whether it's passed in registers according to integer or hardware floating-point calling convention --- .../DependencyAnalysis/ReadyToRun/ArgIterator.cs | 7 +------ .../DependencyAnalysis/ReadyToRun/TransitionBlock.cs | 10 +++++++--- src/coreclr/vm/callingconvention.h | 7 +++++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index 5253928f484e01..63c42fbef099ce 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -626,12 +626,7 @@ public bool IsArgPassedByRef() } return false; case TargetArchitecture.RiscV64: - if (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE) - { - Debug.Assert(!_argTypeHandle.IsNull()); - return ((_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) || _transitionBlock.IsArgPassedByRef(_argTypeHandle)); - } - return false; + return (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE) && _transitionBlock.IsArgPassedByRef(_argTypeHandle); default: throw new NotImplementedException(); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index d79d9fe64f4776..950eaaeac28725 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -11,6 +11,7 @@ using Internal.TypeSystem; using Internal.CorConstants; using Internal.JitInterface; +using static Internal.JitInterface.StructFloatFieldInfoFlags; namespace ILCompiler.DependencyAnalysis.ReadyToRun { @@ -397,7 +398,7 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp { fpReturnSize = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff; - if (fpReturnSize != (uint)StructFloatFieldInfoFlags.STRUCT_NO_FLOAT_FIELD || size <= EnregisteredReturnTypeIntegerMaxSize) + if (fpReturnSize != (uint)STRUCT_NO_FLOAT_FIELD || size <= EnregisteredReturnTypeIntegerMaxSize) break; } @@ -719,9 +720,12 @@ public override bool IsArgPassedByRef(TypeHandle th) { Debug.Assert(!th.IsNull()); Debug.Assert(th.IsValueType()); + if (th.GetSize() <= EnregisteredParamTypeMaxSize) + return false; - // Composites greater than 16 bytes are passed by reference - return th.GetSize() > EnregisteredParamTypeMaxSize; + // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields or more padding + uint flags = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(th.GetRuntimeTypeHandle()); + return (flags == (uint)STRUCT_NO_FLOAT_FIELD); } public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfFirstGCRefMapSlot + (hasThis ? 8 : 0); diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index da7375eec66f7c..a5dd36d0de51e5 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -531,10 +531,13 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE #elif defined(TARGET_RISCV64) if (th.GetSignatureCorElementType() == ELEMENT_TYPE_VALUETYPE) { + if (size <= ENREGISTERED_PARAMTYPE_MAXSIZE) + return FALSE; + + // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields or more padding int flags = MethodTable::GetRiscV64PassStructInRegisterFlags(th); - return (flags == STRUCT_NO_FLOAT_FIELD) && (size > ENREGISTERED_PARAMTYPE_MAXSIZE); + return (flags == STRUCT_NO_FLOAT_FIELD); } - assert(size <= ENREGISTERED_PARAMTYPE_MAXSIZE); return FALSE; #else PORTABILITY_ASSERT("ArgIteratorTemplate::IsArgPassedByRef"); From 0a1a7ff4d5c9410420ff116c023c4ed9e320154a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 14 May 2024 12:23:15 +0200 Subject: [PATCH 13/60] Don't assume struct (size > 16) means pass by reference in Compiler::get(Arg|Return)TypeForStruct --- src/coreclr/jit/compiler.cpp | 51 +++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 6a16fa4e0d7f7c..bdf2d02e65b206 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -743,7 +743,7 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, // Otherwise we pass this struct by value on the stack // setup wbPassType and useType indicate that this is passed by value according to the X86/ARM32 ABI - // On LOONGARCH64 struct that is 1-16 bytes is passed by value in one/two register(s) + // On LOONGARCH64 and RISCV64 struct that is 1-16 bytes is returned by value in one/two register(s) howToPassStruct = SPK_ByValue; useType = TYP_STRUCT; @@ -757,7 +757,6 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, else // (structSize > MAX_PASS_MULTIREG_BYTES) { // We have a (large) struct that can't be replaced with a "primitive" type - // and can't be passed in multiple registers #if defined(TARGET_X86) || defined(TARGET_ARM) || defined(UNIX_AMD64_ABI) @@ -767,11 +766,23 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, useType = TYP_STRUCT; #elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - - // Otherwise we pass this struct by reference to a copy - // setup wbPassType and useType indicate that this is passed using one register (by reference to a copy) - howToPassStruct = SPK_ByReference; - useType = TYP_UNKNOWN; +#ifdef TARGET_RISCV64 + // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields + // or more padding + uint32_t flags = info.compCompHnd->getRISCV64PassStructInRegisterFlags(clsHnd); + if (flags != STRUCT_NO_FLOAT_FIELD) + { + howToPassStruct = SPK_ByValue; + useType = TYP_STRUCT; + } + else +#endif + { + // Otherwise we pass this struct by reference to a copy + // setup wbPassType and useType indicate that this is passed using one register (by reference to a copy) + howToPassStruct = SPK_ByReference; + useType = TYP_UNKNOWN; + } #else // TARGET_XXX @@ -945,20 +956,17 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, } #elif defined(TARGET_RISCV64) - if (structSize <= (TARGET_POINTER_SIZE * 2)) + uint32_t floatFieldFlags = info.compCompHnd->getRISCV64PassStructInRegisterFlags(clsHnd); + if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) { - uint32_t floatFieldFlags = info.compCompHnd->getRISCV64PassStructInRegisterFlags(clsHnd); - - if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) - { - howToReturnStruct = SPK_PrimitiveType; - useType = (structSize > 4) ? TYP_DOUBLE : TYP_FLOAT; - } - else if (floatFieldFlags & (STRUCT_HAS_FLOAT_FIELDS_MASK ^ STRUCT_FLOAT_FIELD_ONLY_ONE)) - { - howToReturnStruct = SPK_ByValue; - useType = TYP_STRUCT; - } + howToReturnStruct = SPK_PrimitiveType; + useType = ((floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) != 0) ? TYP_DOUBLE : TYP_FLOAT; + } + else if (floatFieldFlags != STRUCT_NO_FLOAT_FIELD) + { + assert((floatFieldFlags & (STRUCT_HAS_FLOAT_FIELDS_MASK ^ STRUCT_FLOAT_FIELD_ONLY_ONE)) != 0); + howToReturnStruct = SPK_ByValue; + useType = TYP_STRUCT; } #endif @@ -1105,7 +1113,8 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // On LOONGARCH64/RISCV64 struct that is 1-16 bytes is returned by value in one/two register(s) + // On LOONGARCH64 (always) and RISCV64 (integer calling convention) struct that is 1-16 bytes is + // returned by value in one/two register(s) howToReturnStruct = SPK_ByValue; useType = TYP_STRUCT; From 9aa2d10cbf33881e07634366e3612b24d86222e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 14 May 2024 12:47:16 +0200 Subject: [PATCH 14/60] Add a test for a single-float struct padded with empty struct field --- .../JIT/Directed/StructABI/StructABI.cpp | 11 +++++ src/tests/JIT/Directed/StructABI/StructABI.cs | 48 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cpp b/src/tests/JIT/Directed/StructABI/StructABI.cpp index bd600653b94a14..e0fb335158628c 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cpp +++ b/src/tests/JIT/Directed/StructABI/StructABI.cpp @@ -14,6 +14,12 @@ struct Empty }; static_assert(sizeof(Empty) == 1, "Empty struct must be sized like in .NET"); +struct EmptyFloat +{ + Empty FieldE; + float FieldF; +}; + struct LongEmptyDouble { int64_t FieldL; @@ -104,6 +110,11 @@ static_assert(offsetof(ExplicitFloatLong, s.FieldL) == 5, ""); extern "C" { +DLLEXPORT EmptyFloat EchoEmptyFloatRiscV(EmptyFloat fa0) +{ + return fa0; +} + DLLEXPORT LongEmptyDouble EchoLongEmptyDoubleRiscV(LongEmptyDouble a0_fa0) { return a0_fa0; diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index e054c6281bf26a..c36fe9373edfde 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -672,6 +672,22 @@ struct Empty { } +struct EmptyFloat +{ + Empty FieldE; + float FieldF; + + public static EmptyFloat Get() + { + return new EmptyFloat { FieldF = 3.14159f }; + } + + public bool Equals(EmptyFloat other) + { + return FieldF.Equals(other.FieldF); + } +} + struct LongEmptyDouble { long FieldL; @@ -969,6 +985,9 @@ public static partial class StructABI [DllImport("StructABILib")] static extern DoubleAndByte EnoughRegistersSysV4(double a, double b, double c, double d, double e, double f, double g, DoubleAndByte value); + [DllImport("StructABILib")] + static extern EmptyFloat EchoEmptyFloatRiscV(EmptyFloat fa0); + [DllImport("StructABILib")] static extern LongEmptyDouble EchoLongEmptyDoubleRiscV(LongEmptyDouble value); @@ -1266,6 +1285,12 @@ static DoubleAndByte EnoughRegistersSysV4Managed(double a, double b, double c, d return value; } + [MethodImpl(MethodImplOptions.NoInlining)] + static EmptyFloat EchoEmptyFloatRiscVManaged(EmptyFloat fa0) + { + return fa0; + } + [MethodImpl(MethodImplOptions.NoInlining)] static LongEmptyDouble EchoLongEmptyDoubleRiscVManaged(LongEmptyDouble a0_fa0) { @@ -2422,6 +2447,28 @@ static bool EnoughRegistersSysV4Wrapper() return ok; } + static bool EchoEmptyFloatRiscVWrapper() + { + bool ok = true; + EmptyFloat expected = EmptyFloat.Get(); + EmptyFloat native = EchoEmptyFloatRiscV(expected); + EmptyFloat managed = EchoEmptyFloatRiscVManaged(expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmptyFloatRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmptyFloatRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoLongEmptyDoubleRiscVWrapper() { bool ok = true; @@ -2690,6 +2737,7 @@ public static int TestEntryPoint() if (!EnoughRegistersSysV2Wrapper()) ok = false; if (!EnoughRegistersSysV3Wrapper()) ok = false; if (!EnoughRegistersSysV4Wrapper()) ok = false; + if (!EchoEmptyFloatRiscVWrapper()) ok = false; if (!EchoLongEmptyDoubleRiscVWrapper()) ok = false; if (!EchoLongEmptyDoubleByImplicitRefRiscVWrapper()) ok = false; if (!EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper()) ok = false; From 9d9d0e22df7db5d461edffd63015c0b84af6e41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 16 May 2024 16:33:44 +0200 Subject: [PATCH 15/60] Relax checks so the new test cases JIT without false positive assertions --- src/coreclr/jit/gentree.cpp | 27 ++++++++++++++++++--------- src/coreclr/jit/morph.cpp | 19 ++++++++++++++----- src/coreclr/jit/scopeinfo.cpp | 2 ++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index b71aa661229f42..ec6413d907c25e 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -27358,35 +27358,40 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, } #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - assert((structSize >= TARGET_POINTER_SIZE) && (structSize <= (2 * TARGET_POINTER_SIZE))); + assert(structSize >= TARGET_POINTER_SIZE); #ifdef TARGET_LOONGARCH64 + assert(structSize <= (2 * TARGET_POINTER_SIZE)); uint32_t floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(retClsHnd); -#else + BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; +#else // TARGET_RISCV64 uint32_t floatFieldFlags = comp->info.compCompHnd->getRISCV64PassStructInRegisterFlags(retClsHnd); + // Most cases will be up to 2 floating/integer fields with an occasional empty field + BYTE gcPtrsStack[4] = {TYPE_GC_NONE, TYPE_GC_NONE, TYPE_GC_NONE, TYPE_GC_NONE}; + unsigned wordCount = roundUp(structSize, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; + BYTE* gcPtrs = (wordCount <= 4) ? gcPtrsStack : new (comp, CMK_GC) BYTE[wordCount]; + // TODO: we need field offsets along with getRISCV64PassStructInRegisterFlags because we don't know which of + // gcPtrs to inspect below + // TODO: doesn't ClassLayout encapsulate GC info already? #endif - BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; comp->info.compCompHnd->getClassGClayout(retClsHnd, &gcPtrs[0]); if (floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) { comp->compFloatingPointUsed = true; - assert((structSize > 8) == ((floatFieldFlags & STRUCT_HAS_8BYTES_FIELDS_MASK) > 0)); - m_regType[0] = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - m_regType[1] = (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + m_regType[0] = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + m_regType[1] = (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; } else if (floatFieldFlags & STRUCT_FLOAT_FIELD_FIRST) { comp->compFloatingPointUsed = true; - assert((structSize > 8) == ((floatFieldFlags & STRUCT_HAS_8BYTES_FIELDS_MASK) > 0)); - m_regType[0] = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + m_regType[0] = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; m_regType[1] = (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? comp->getJitGCType(gcPtrs[1]) : TYP_INT; } else if (floatFieldFlags & STRUCT_FLOAT_FIELD_SECOND) { comp->compFloatingPointUsed = true; - assert((structSize > 8) == ((floatFieldFlags & STRUCT_HAS_8BYTES_FIELDS_MASK) > 0)); m_regType[0] = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? comp->getJitGCType(gcPtrs[0]) : TYP_INT; m_regType[1] = (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; @@ -27398,6 +27403,10 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, m_regType[i] = comp->getJitGCType(gcPtrs[i]); } } +#ifdef TARGET_RISCV64 + if (wordCount > 4) + delete[] gcPtrs; +#endif #elif defined(TARGET_X86) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index a43882bd9867df..4f52247e508514 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2882,7 +2882,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call } else if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) { - structBaseType = structSize == 8 ? TYP_DOUBLE : TYP_FLOAT; + structBaseType = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; fltArgRegNum += 1; arg.AbiInfo.StructFloatFieldType[0] = structBaseType; } @@ -3294,7 +3294,13 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) // We have a struct argument that fits into a register, and it is either a power of 2, // or a local. // Change our argument, as needed, into a value of the appropriate type. - assert((structBaseType != TYP_STRUCT) && (genTypeSize(structBaseType) >= originalSize)); + assert(structBaseType != TYP_STRUCT); + + // On RISC-V / LoongArch the passing size may be smaller than the original size if we pass a struct + // according to hardware FP calling convention and it has empty fields +#if !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64) + assert(genTypeSize(structBaseType) >= originalSize); +#endif if (argObj->OperIsLoad()) { @@ -3627,7 +3633,10 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg) } else { +#ifndef TARGET_RISCV64 assert(structSize <= MAX_ARG_REG_COUNT * TARGET_POINTER_SIZE); +#endif + assert(arg->AbiInfo.NumRegs <= MAX_ARG_REG_COUNT); auto getSlotType = [layout](unsigned inx) { return (layout != nullptr) ? layout->GetGCPtrType(inx) : TYP_I_IMPL; @@ -3709,9 +3718,9 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg) } #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // For LoongArch64's ABI, the struct {long a; float b;} may be passed - // by integer and float registers and it needs to include the padding here. - assert(roundUp(structSize, TARGET_POINTER_SIZE) == roundUp(loadExtent, TARGET_POINTER_SIZE)); + // For LoongArch64's and RISC-V ABI, struct { long a; float b; } or struct { int a; float b; struct {/*empty*/}; } + // may be passed by integer and float registers and it needs to include the padding here. + assert(roundUp(structSize, TARGET_POINTER_SIZE) >= roundUp(loadExtent, TARGET_POINTER_SIZE)); #else if (argNode->IsLocal()) { diff --git a/src/coreclr/jit/scopeinfo.cpp b/src/coreclr/jit/scopeinfo.cpp index d2d9a65d01be3a..263956c22167e5 100644 --- a/src/coreclr/jit/scopeinfo.cpp +++ b/src/coreclr/jit/scopeinfo.cpp @@ -1750,9 +1750,11 @@ void CodeGen::psiBegProlog() var_types regType; if (varTypeIsStruct(lclVarDsc)) { +#ifdef TARGET_LOONGARCH // Must be <= 16 bytes or else it wouldn't be passed in registers, // which can be bigger (and is handled above). noway_assert(EA_SIZE_IN_BYTES(lclVarDsc->lvSize()) <= 16); +#endif // TARGET_LOONGARCH if (emitter::isFloatReg(lclVarDsc->GetArgReg())) { regType = TYP_DOUBLE; From 983d42355482f388ad8d08d074cd32b2be039944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 16 May 2024 16:38:15 +0200 Subject: [PATCH 16/60] New structure to store information about passing structs according to FP calling convention on RISC-V and LoongArch --- src/coreclr/inc/corinfo.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 2d526105164b6b..e0d15b62193d76 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -356,6 +356,31 @@ enum StructFloatFieldInfoFlags STRUCT_HAS_8BYTES_FIELDS_MASK = (STRUCT_FIRST_FIELD_SIZE_IS8 | STRUCT_SECOND_FIELD_SIZE_IS8), }; +// On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating, +// can be passed in registers. FPStructInfo represents passing information for such structs. +struct FPStructInfo +{ + struct Field + { + bool isFloating; + uint8_t size; + uint32_t offset; + + bool Empty() const + { + static_assert(sizeof(*this) == sizeof(uint64_t), "sizeof(FPStructField) must be 64 bits"); + return *(uint64_t*)this == 0; + } + }; + + Field fields[2]; + + bool IsIntegerOnly() const + { + return fields[0].Empty() && fields[1].Empty(); + } +}; + #include "corinfoinstructionset.h" // CorInfoHelpFunc defines the set of helpers (accessed via the ICorDynamicInfo::getHelperFtn()) From 8366470a17200e1ea8d6db2e2a41efa1f7f1947a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 17 May 2024 15:06:08 +0200 Subject: [PATCH 17/60] Implement GetRiscV64PassFPStructInfo for now with checks against existing flags, add some logging, started to replace in ArgIterator --- src/coreclr/inc/corinfo.h | 30 ++++- src/coreclr/vm/callingconvention.h | 28 ++-- src/coreclr/vm/jitinterface.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 206 ++++++++++++++++++++++++++++- src/coreclr/vm/methodtable.h | 1 + 5 files changed, 249 insertions(+), 18 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index e0d15b62193d76..d885f7ee496efc 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -375,10 +375,38 @@ struct FPStructInfo Field fields[2]; - bool IsIntegerOnly() const + bool IsPassedWithIntegerCallConv() const { return fields[0].Empty() && fields[1].Empty(); } + + bool IsFloatingOnly() const + { + return fields[0].isFloating && (fields[1].isFloating || fields[1].Empty()); + } + + unsigned NumFields() const + { + return !fields[0].Empty() + !fields[1].Empty(); + } + + // TODO: Remove, unless there are places where field offsets are unnecessary and it's difficult to pass FPStructInfo (CallDescrWorker?) + StructFloatFieldInfoFlags ToFlags() const + { + int flags = + (fields[0].size == 8 ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | + (fields[1].size == 8 ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0); + + if (IsFloatingOnly()) + { + return StructFloatFieldInfoFlags( + flags | (fields[1].Empty() ? STRUCT_FLOAT_FIELD_ONLY_ONE : STRUCT_FLOAT_FIELD_ONLY_TWO)); + } + + return StructFloatFieldInfoFlags(flags | + (fields[0].isFloating ? STRUCT_FLOAT_FIELD_FIRST : 0) | + (fields[1].isFloating ? STRUCT_FLOAT_FIELD_SECOND : 0)); + } }; #include "corinfoinstructionset.h" diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index a5dd36d0de51e5..3e8450794ee941 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -534,9 +534,9 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE if (size <= ENREGISTERED_PARAMTYPE_MAXSIZE) return FALSE; - // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields or more padding - int flags = MethodTable::GetRiscV64PassStructInRegisterFlags(th); - return (flags == STRUCT_NO_FLOAT_FIELD); + // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields or + // more padding, so make sure it's passed according to integer call conv which bounds struct to 16 bytes. + return MethodTable::GetRiscV64PassFPStructInfo(th).IsPassedWithIntegerCallConv(); } return FALSE; #else @@ -598,8 +598,10 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE { _ASSERTE(!m_argTypeHandle.IsNull()); #ifdef TARGET_RISCV64 - int flags = MethodTable::GetRiscV64PassStructInRegisterFlags(m_argTypeHandle); - return (flags == STRUCT_NO_FLOAT_FIELD) && (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE); + if (m_argSize <= ENREGISTERED_PARAMTYPE_MAXSIZE) + return FALSE; + + return MethodTable::GetRiscV64PassFPStructInfo(m_argTypeHandle).IsPassedWithIntegerCallConv(); #else return (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE); #endif @@ -1793,7 +1795,7 @@ int ArgIteratorTemplate::GetNextOffset() #elif defined(TARGET_RISCV64) assert(!this->IsVarArg()); // Varargs on RISC-V not supported yet int cFPRegs = 0; - int flags = STRUCT_NO_FLOAT_FIELD; + FPStructInfo info = {}; switch (argType) { @@ -1804,11 +1806,11 @@ int ArgIteratorTemplate::GetNextOffset() break; case ELEMENT_TYPE_VALUETYPE: - flags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType); - if (flags != STRUCT_NO_FLOAT_FIELD) + info = MethodTable::GetRiscV64PassFPStructInfo(thValueType); + if (!info.IsPassedWithIntegerCallConv()) { // Struct may be passed according to hardware floating-point calling convention - cFPRegs = ((flags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) ? 2 : 1; + cFPRegs = info.fields[0].isFloating + info.fields[1].isFloating; } break; @@ -1819,7 +1821,7 @@ int ArgIteratorTemplate::GetNextOffset() if (cFPRegs > 0) { // Pass according to hardware floating-point calling convention iff the argument can be fully enregistered - if ((flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) != 0) + if (info.NumFields() == 2 && (info.fields[0].isFloating != info.fields[1].isFloating)) { assert(cFPRegs == 1); @@ -1832,7 +1834,7 @@ int ArgIteratorTemplate::GetNextOffset() m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg; m_argLocDescForStructInRegs.m_cGenReg = 1; - m_argLocDescForStructInRegs.m_structFields = flags; + m_argLocDescForStructInRegs.m_structFields = info.ToFlags(); m_hasArgLocDescForStructInRegs = true; int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; @@ -1844,7 +1846,7 @@ int ArgIteratorTemplate::GetNextOffset() else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) { int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; - if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two single-float fields + if (info.fields[0].isFloating && info.fields[1].isFloating && info.fields[0].size != 8 && info.fields[1].size != 8) { m_argLocDescForStructInRegs.Init(); m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; @@ -1852,7 +1854,7 @@ int ArgIteratorTemplate::GetNextOffset() assert(cFPRegs == 2); assert(argSize == 2 * 4); - m_argLocDescForStructInRegs.m_structFields = STRUCT_FLOAT_FIELD_ONLY_TWO; + m_argLocDescForStructInRegs.m_structFields = info.ToFlags(); m_hasArgLocDescForStructInRegs = true; } m_idxFPReg += cFPRegs; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 0324a02c2c750b..5bce415a9f8aca 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9653,7 +9653,7 @@ uint32_t CEEInfo::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) uint32_t size = STRUCT_NO_FLOAT_FIELD; #if defined(TARGET_RISCV64) - size = (uint32_t)MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle(cls)); + size = (uint32_t)MethodTable::GetRiscV64PassFPStructInfo(TypeHandle(cls)).ToFlags(); #endif // TARGET_RISCV64 EE_TO_JIT_TRANSITION_LEAF(); diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index a294a57975d7ea..f71f941e3b4b56 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2829,7 +2829,7 @@ void MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe #endif // defined(UNIX_AMD64_ABI_ITF) #if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) -static bool HandleInlineArray(int elementTypeIndex, int nElements, StructFloatFieldInfoFlags types[2], int& typeIndex) +static bool HandleInlineArrayOld(int elementTypeIndex, int nElements, StructFloatFieldInfoFlags types[2], int& typeIndex) { int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; if (nFlattenedFieldsPerElement == 0) @@ -2896,7 +2896,7 @@ static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], { assert(nFields == 1); int nElements = pMT->GetNumInstanceFieldBytes() / fields[0].GetSize(); - if (!HandleInlineArray(elementTypeIndex, nElements, types, typeIndex)) + if (!HandleInlineArrayOld(elementTypeIndex, nElements, types, typeIndex)) return false; } } @@ -2919,7 +2919,7 @@ static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], // In native layout fixed arrays are marked as NESTED just like structs int nElements = fields[i].GetNumElements(); - if (!HandleInlineArray(elementTypeIndex, nElements, types, typeIndex)) + if (!HandleInlineArrayOld(elementTypeIndex, nElements, types, typeIndex)) return false; } else if (fields[i].NativeSize() <= TARGET_POINTER_SIZE) @@ -3013,6 +3013,206 @@ int MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle th) } #endif +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) +static bool HandleInlineArray(int elementTypeIndex, int nElements, FPStructInfo& info DEBUG_ARG(const char* fieldNames[2])) +{ + int typeIndex = info.NumFields(); + int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; + if (nFlattenedFieldsPerElement == 0) + return true; + + assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2); + + if (nElements > 2) + return false; + + if (nElements == 2) + { + if (typeIndex + nFlattenedFieldsPerElement > 2) + return false; + + assert(elementTypeIndex == 0); + assert(typeIndex == 1); + info.fields[1] = info.fields[0]; // duplicate the array element type + info.fields[1].offset += info.fields[0].size; + INDEBUG(fieldNames[1] = fieldNames[0];) + } + return true; +} + +static bool FlattenFields(TypeHandle th, uint32_t offset, FPStructInfo& info DEBUG_ARG(const char* fieldNames[2])) +{ + bool isManaged = !th.IsTypeDesc(); + MethodTable* pMT = isManaged ? th.AsMethodTable() : th.AsNativeValueType(); + int nFields = isManaged ? pMT->GetNumIntroducedInstanceFields() : pMT->GetNativeLayoutInfo()->GetNumFields(); + + // TODO: templatize isManaged and use if constexpr for differences when we migrate to C++17 + // because the logic for both branches is nearly the same. + if (isManaged) + { + FieldDesc* fields = pMT->GetApproxFieldDescListRaw(); + int elementTypeIndex = info.NumFields(); + for (int i = 0; i < nFields; ++i) + { + if (i > 0 && fields[i-1].GetOffset() + fields[i-1].GetSize() > fields[i].GetOffset()) + return false; // overlapping fields, treat as union + + CorElementType type = fields[i].GetFieldType(); + if (type == ELEMENT_TYPE_VALUETYPE) + { + MethodTable* nested = fields[i].GetApproxFieldTypeHandleThrowing().GetMethodTable(); + if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetOffset(), info DEBUG_ARG(fieldNames))) + return false; + } + else if (fields[i].GetSize() <= TARGET_POINTER_SIZE) + { + int typeIndex = info.NumFields(); + if (typeIndex >= 2) + return false; + + info.fields[typeIndex] = { + .isFloating = CorTypeInfo::IsFloat_NoThrow(type), + .size = CorTypeInfo::Size_NoThrow(type), + .offset = offset + fields[i].GetOffset(), + }; + INDEBUG(fields[i].GetName_NoThrow(&fieldNames[typeIndex]);) + } + else + { + return false; + } + } + + if (HasImpliedRepeatedFields(pMT)) // inline array or fixed buffer + { + assert(nFields == 1); + int nElements = pMT->GetNumInstanceFieldBytes() / fields[0].GetSize(); + if (!HandleInlineArray(elementTypeIndex, nElements, info DEBUG_ARG(fieldNames))) + return false; + } + } + else // native layout + { + const NativeFieldDescriptor* fields = pMT->GetNativeLayoutInfo()->GetNativeFieldDescriptors(); + for (int i = 0; i < nFields; ++i) + { + if (i > 0 && fields[i-1].GetExternalOffset() + fields[i-1].NativeSize() > fields[i].GetExternalOffset()) + return false; // overlapping fields, treat as union + + NativeFieldCategory category = fields[i].GetCategory(); + if (category == NativeFieldCategory::NESTED) + { + int elementTypeIndex = info.NumFields(); + + MethodTable* nested = fields[i].GetNestedNativeMethodTable(); + if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetExternalOffset(), info DEBUG_ARG(fieldNames))) + return false; + + // In native layout fixed arrays are marked as NESTED just like structs + int nElements = fields[i].GetNumElements(); + if (!HandleInlineArray(elementTypeIndex, nElements, info DEBUG_ARG(fieldNames))) + return false; + } + else if (fields[i].NativeSize() <= TARGET_POINTER_SIZE) + { + int typeIndex = info.NumFields(); + if (typeIndex >= 2) + return false; + + info.fields[typeIndex] = { + .isFloating = (category == NativeFieldCategory::FLOAT), + .size = fields[i].NativeSize(), + .offset = offset + fields[i].GetExternalOffset(), + }; + INDEBUG(fields[i].GetFieldDesc()->GetName_NoThrow(&fieldNames[typeIndex]);) + } + else + { + return false; + } + } + } + return true; +} +#endif + +#if defined(TARGET_RISCV64) +static void LogFPStructInfo(TypeHandle th, FPStructInfo info, const char* fieldNames[2], const char* message) +{ + if (!LoggingOn(LF_JIT, LL_EVERYTHING)) + return; + + SString name; + th.GetName(name); + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFPStructInfo: Struct %s %s, %u fields%s\n", + name.GetUTF8(), message, info.NumFields(), (info.NumFields() > 0 ? ":" : ""))); + + if (!info.fields[0].Empty()) + { + LOG((LF_JIT, LL_EVERYTHING, "\t1st field %s: %s, %u bytes at offset %u\n", + (fieldNames[0] ? fieldNames[0] : ""), (info.fields[0].isFloating ? "floating" : "integer"), info.fields[0].size, info.fields[0].offset)); + } + if (!info.fields[1].Empty()) + { + LOG((LF_JIT, LL_EVERYTHING, "\t2nd field %s: %s, %u bytes at offset %u\n", + (fieldNames[1] ? fieldNames[1] : ""), (info.fields[1].isFloating ? "floating" : "integer"), info.fields[1].size, info.fields[1].offset)); + } +} + +static FPStructInfo GetRiscV64PassFPStructInfoImpl(TypeHandle th) +{ + FPStructInfo info = {}; + INDEBUG(const char* fieldNames[2] = {};) + if (!FlattenFields(th, 0, info DEBUG_ARG(fieldNames))) + { + INDEBUG(LogFPStructInfo(th, info, fieldNames, "cannot be passed according to floating-point calling convention");) + return FPStructInfo{}; + } + + assert(!(info.fields[0].Empty() && !info.fields[1].Empty())); + + if (!info.fields[0].isFloating && !info.fields[1].isFloating) + { + INDEBUG(LogFPStructInfo(th, info, fieldNames, "does not have any floating fields");) + return FPStructInfo{}; + } + + INDEBUG(LogFPStructInfo(th, info, fieldNames, "can be passed according to floating-point calling convention");) + return info; +} + +FPStructInfo MethodTable::GetRiscV64PassFPStructInfo(TypeHandle th) +{ + FPStructInfo info = GetRiscV64PassFPStructInfoImpl(th); + int flags = GetRiscV64PassStructInRegisterFlags(th); + + if (info.IsPassedWithIntegerCallConv()) + { + assert(flags == STRUCT_NO_FLOAT_FIELD); + } + else if (info.IsFloatingOnly()) + { + unsigned n = info.NumFields(); + assert(n > 0); + assert((n == 1) == !!(flags & STRUCT_FLOAT_FIELD_ONLY_ONE)); + assert((n == 2) == !!(flags & STRUCT_FLOAT_FIELD_ONLY_TWO)); + } + else // mixed + { + assert(info.NumFields() == 2); + assert(info.fields[0].isFloating == !!(flags & STRUCT_FLOAT_FIELD_FIRST)); + assert(info.fields[1].isFloating == !!(flags & STRUCT_FLOAT_FIELD_SECOND)); + } + + assert((info.fields[0].size == 8) == !!(flags & STRUCT_FIRST_FIELD_SIZE_IS8)); + assert((info.fields[1].size == 8) == !!(flags & STRUCT_SECOND_FIELD_SIZE_IS8)); + + assert(flags == info.ToFlags()); + + return info; +} +#endif + #if !defined(DACCESS_COMPILE) namespace { diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index cb8a7ce9f9c240..936837425fb9e2 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -828,6 +828,7 @@ class MethodTable static int GetLoongArch64PassStructInRegisterFlags(TypeHandle th); #elif defined(TARGET_RISCV64) static int GetRiscV64PassStructInRegisterFlags(TypeHandle th); + static FPStructInfo GetRiscV64PassFPStructInfo(TypeHandle th); #endif #if defined(UNIX_AMD64_ABI_ITF) From c3e85787601d29350fd25c2b31b57096e98ac4c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 20 May 2024 10:02:12 +0200 Subject: [PATCH 18/60] Rename to match similar functions on other architectures --- src/coreclr/inc/corinfo.h | 6 +++--- src/coreclr/vm/callingconvention.h | 14 +++++--------- src/coreclr/vm/jitinterface.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 26 +++++++++++++------------- src/coreclr/vm/methodtable.h | 2 +- 5 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index d885f7ee496efc..7e1032e7bf66ae 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -357,8 +357,8 @@ enum StructFloatFieldInfoFlags }; // On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating, -// can be passed in registers. FPStructInfo represents passing information for such structs. -struct FPStructInfo +// can be passed in registers. FpStructInRegistersInfo represents passing information for such structs. +struct FpStructInRegistersInfo { struct Field { @@ -390,7 +390,7 @@ struct FPStructInfo return !fields[0].Empty() + !fields[1].Empty(); } - // TODO: Remove, unless there are places where field offsets are unnecessary and it's difficult to pass FPStructInfo (CallDescrWorker?) + // TODO: Remove, unless there are places where field offsets are unnecessary and it's difficult to pass FpStructInRegistersInfo (CallDescrWorker?) StructFloatFieldInfoFlags ToFlags() const { int flags = diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 3e8450794ee941..46a6e7d1ce9f6f 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -536,7 +536,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields or // more padding, so make sure it's passed according to integer call conv which bounds struct to 16 bytes. - return MethodTable::GetRiscV64PassFPStructInfo(th).IsPassedWithIntegerCallConv(); + return MethodTable::GetRiscV64PassFpStructInRegistersInfo(th).IsPassedWithIntegerCallConv(); } return FALSE; #else @@ -601,7 +601,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE if (m_argSize <= ENREGISTERED_PARAMTYPE_MAXSIZE) return FALSE; - return MethodTable::GetRiscV64PassFPStructInfo(m_argTypeHandle).IsPassedWithIntegerCallConv(); + return MethodTable::GetRiscV64PassFpStructInRegistersInfo(m_argTypeHandle).IsPassedWithIntegerCallConv(); #else return (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE); #endif @@ -1795,7 +1795,7 @@ int ArgIteratorTemplate::GetNextOffset() #elif defined(TARGET_RISCV64) assert(!this->IsVarArg()); // Varargs on RISC-V not supported yet int cFPRegs = 0; - FPStructInfo info = {}; + FpStructInRegistersInfo info = {}; switch (argType) { @@ -1806,12 +1806,8 @@ int ArgIteratorTemplate::GetNextOffset() break; case ELEMENT_TYPE_VALUETYPE: - info = MethodTable::GetRiscV64PassFPStructInfo(thValueType); - if (!info.IsPassedWithIntegerCallConv()) - { - // Struct may be passed according to hardware floating-point calling convention - cFPRegs = info.fields[0].isFloating + info.fields[1].isFloating; - } + info = MethodTable::GetRiscV64PassFpStructInRegistersInfo(thValueType); + cFPRegs = info.fields[0].isFloating + info.fields[1].isFloating; break; default: diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 5bce415a9f8aca..a150868b87905c 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9653,7 +9653,7 @@ uint32_t CEEInfo::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) uint32_t size = STRUCT_NO_FLOAT_FIELD; #if defined(TARGET_RISCV64) - size = (uint32_t)MethodTable::GetRiscV64PassFPStructInfo(TypeHandle(cls)).ToFlags(); + size = (uint32_t)MethodTable::GetRiscV64PassFpStructInRegistersInfo(TypeHandle(cls)).ToFlags(); #endif // TARGET_RISCV64 EE_TO_JIT_TRANSITION_LEAF(); diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index f71f941e3b4b56..350ae9db08f87a 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3014,7 +3014,7 @@ int MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle th) #endif #if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) -static bool HandleInlineArray(int elementTypeIndex, int nElements, FPStructInfo& info DEBUG_ARG(const char* fieldNames[2])) +static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInRegistersInfo& info DEBUG_ARG(const char* fieldNames[2])) { int typeIndex = info.NumFields(); int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; @@ -3040,7 +3040,7 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FPStructInfo& return true; } -static bool FlattenFields(TypeHandle th, uint32_t offset, FPStructInfo& info DEBUG_ARG(const char* fieldNames[2])) +static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInfo& info DEBUG_ARG(const char* fieldNames[2])) { bool isManaged = !th.IsTypeDesc(); MethodTable* pMT = isManaged ? th.AsMethodTable() : th.AsNativeValueType(); @@ -3137,14 +3137,14 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FPStructInfo& info DEB #endif #if defined(TARGET_RISCV64) -static void LogFPStructInfo(TypeHandle th, FPStructInfo info, const char* fieldNames[2], const char* message) +static void LogFpStructInRegistersInfo(TypeHandle th, FpStructInRegistersInfo info, const char* fieldNames[2], const char* message) { if (!LoggingOn(LF_JIT, LL_EVERYTHING)) return; SString name; th.GetName(name); - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFPStructInfo: Struct %s %s, %u fields%s\n", + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: Struct %s %s, %u fields%s\n", name.GetUTF8(), message, info.NumFields(), (info.NumFields() > 0 ? ":" : ""))); if (!info.fields[0].Empty()) @@ -3159,31 +3159,31 @@ static void LogFPStructInfo(TypeHandle th, FPStructInfo info, const char* fieldN } } -static FPStructInfo GetRiscV64PassFPStructInfoImpl(TypeHandle th) +static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHandle th) { - FPStructInfo info = {}; + FpStructInRegistersInfo info = {}; INDEBUG(const char* fieldNames[2] = {};) if (!FlattenFields(th, 0, info DEBUG_ARG(fieldNames))) { - INDEBUG(LogFPStructInfo(th, info, fieldNames, "cannot be passed according to floating-point calling convention");) - return FPStructInfo{}; + INDEBUG(LogFpStructInRegistersInfo(th, info, fieldNames, "cannot be passed according to floating-point calling convention");) + return FpStructInRegistersInfo{}; } assert(!(info.fields[0].Empty() && !info.fields[1].Empty())); if (!info.fields[0].isFloating && !info.fields[1].isFloating) { - INDEBUG(LogFPStructInfo(th, info, fieldNames, "does not have any floating fields");) - return FPStructInfo{}; + INDEBUG(LogFpStructInRegistersInfo(th, info, fieldNames, "does not have any floating fields");) + return FpStructInRegistersInfo{}; } - INDEBUG(LogFPStructInfo(th, info, fieldNames, "can be passed according to floating-point calling convention");) + INDEBUG(LogFpStructInRegistersInfo(th, info, fieldNames, "can be passed according to floating-point calling convention");) return info; } -FPStructInfo MethodTable::GetRiscV64PassFPStructInfo(TypeHandle th) +FpStructInRegistersInfo MethodTable::GetRiscV64PassFpStructInRegistersInfo(TypeHandle th) { - FPStructInfo info = GetRiscV64PassFPStructInfoImpl(th); + FpStructInRegistersInfo info = GetRiscV64PassFpStructInRegistersInfoImpl(th); int flags = GetRiscV64PassStructInRegisterFlags(th); if (info.IsPassedWithIntegerCallConv()) diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 936837425fb9e2..9116c1f6a9112d 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -828,7 +828,7 @@ class MethodTable static int GetLoongArch64PassStructInRegisterFlags(TypeHandle th); #elif defined(TARGET_RISCV64) static int GetRiscV64PassStructInRegisterFlags(TypeHandle th); - static FPStructInfo GetRiscV64PassFPStructInfo(TypeHandle th); + static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfo(TypeHandle th); #endif #if defined(UNIX_AMD64_ABI_ITF) From c2dd4493f73fc69ce117b6b24f4dd03a7ab05fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 20 May 2024 10:02:12 +0200 Subject: [PATCH 19/60] Use bitfields to keep the most important flags for decision making packed in low bits for easy fitting into instruction immmediates --- src/coreclr/inc/corinfo.h | 42 ++++---- src/coreclr/vm/callingconvention.h | 7 +- src/coreclr/vm/methodtable.cpp | 148 +++++++++++++++++------------ 3 files changed, 109 insertions(+), 88 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 7e1032e7bf66ae..d282d26f40f424 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -360,55 +360,45 @@ enum StructFloatFieldInfoFlags // can be passed in registers. FpStructInRegistersInfo represents passing information for such structs. struct FpStructInRegistersInfo { - struct Field - { - bool isFloating; - uint8_t size; - uint32_t offset; - - bool Empty() const - { - static_assert(sizeof(*this) == sizeof(uint64_t), "sizeof(FPStructField) must be 64 bits"); - return *(uint64_t*)this == 0; - } - }; - - Field fields[2]; + bool hasTwoFields : 1; + bool isFloating1st : 1; + bool isFloating2nd : 1; + uint8_t sizeShift1st : 2; + uint8_t sizeShift2nd : 2; + uint32_t offset1st; + uint32_t offset2nd; bool IsPassedWithIntegerCallConv() const { - return fields[0].Empty() && fields[1].Empty(); + return !hasTwoFields && !isFloating1st; } bool IsFloatingOnly() const { - return fields[0].isFloating && (fields[1].isFloating || fields[1].Empty()); - } - - unsigned NumFields() const - { - return !fields[0].Empty() + !fields[1].Empty(); + return isFloating1st && (isFloating2nd || !hasTwoFields); } // TODO: Remove, unless there are places where field offsets are unnecessary and it's difficult to pass FpStructInRegistersInfo (CallDescrWorker?) StructFloatFieldInfoFlags ToFlags() const { int flags = - (fields[0].size == 8 ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | - (fields[1].size == 8 ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0); + (sizeShift1st == 3 ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | + (sizeShift2nd == 3 ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0); if (IsFloatingOnly()) { return StructFloatFieldInfoFlags( - flags | (fields[1].Empty() ? STRUCT_FLOAT_FIELD_ONLY_ONE : STRUCT_FLOAT_FIELD_ONLY_TWO)); + flags | (hasTwoFields ? STRUCT_FLOAT_FIELD_ONLY_TWO : STRUCT_FLOAT_FIELD_ONLY_ONE)); } return StructFloatFieldInfoFlags(flags | - (fields[0].isFloating ? STRUCT_FLOAT_FIELD_FIRST : 0) | - (fields[1].isFloating ? STRUCT_FLOAT_FIELD_SECOND : 0)); + (isFloating1st ? STRUCT_FLOAT_FIELD_FIRST : 0) | + (isFloating2nd ? STRUCT_FLOAT_FIELD_SECOND : 0)); } }; +static_assert(sizeof(FpStructInRegistersInfo) == 3 * sizeof(uint32_t), ""); + #include "corinfoinstructionset.h" // CorInfoHelpFunc defines the set of helpers (accessed via the ICorDynamicInfo::getHelperFtn()) diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 46a6e7d1ce9f6f..9fffc1c18c1c23 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -1807,7 +1807,7 @@ int ArgIteratorTemplate::GetNextOffset() case ELEMENT_TYPE_VALUETYPE: info = MethodTable::GetRiscV64PassFpStructInRegistersInfo(thValueType); - cFPRegs = info.fields[0].isFloating + info.fields[1].isFloating; + cFPRegs = info.isFloating1st + info.isFloating2nd; break; default: @@ -1817,7 +1817,7 @@ int ArgIteratorTemplate::GetNextOffset() if (cFPRegs > 0) { // Pass according to hardware floating-point calling convention iff the argument can be fully enregistered - if (info.NumFields() == 2 && (info.fields[0].isFloating != info.fields[1].isFloating)) + if (info.hasTwoFields && (info.isFloating1st != info.isFloating2nd)) { assert(cFPRegs == 1); @@ -1842,8 +1842,9 @@ int ArgIteratorTemplate::GetNextOffset() else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) { int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; - if (info.fields[0].isFloating && info.fields[1].isFloating && info.fields[0].size != 8 && info.fields[1].size != 8) + if (info.isFloating1st && info.isFloating2nd && info.sizeShift1st == 2 && info.sizeShift2nd == 2) { + // Two single-float arguments m_argLocDescForStructInRegs.Init(); m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; m_argLocDescForStructInRegs.m_cFloatReg = 2; diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 350ae9db08f87a..9ba75f1b2970ac 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3014,9 +3014,36 @@ int MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle th) #endif #if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) -static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInRegistersInfo& info DEBUG_ARG(const char* fieldNames[2])) +static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int index, + bool isFloating, unsigned size, uint32_t offset) +{ + assert(index < 2); + + int sizeShift = + (size == 1) ? 0 : + (size == 2) ? 1 : + (size == 4) ? 2 : + (size == 8) ? 3 : + -1; + assert(sizeShift != -1); + + info.hasTwoFields = index; + if (index == 0) + { + info.isFloating1st = isFloating; + info.sizeShift1st = sizeShift; + info.offset1st = offset; + } + else + { + info.isFloating2nd = isFloating; + info.sizeShift2nd = sizeShift; + info.offset2nd = offset; + } +} + +static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInRegistersInfo& info, int& typeIndex DEBUG_ARG(const char* fieldNames[2])) { - int typeIndex = info.NumFields(); int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; if (nFlattenedFieldsPerElement == 0) return true; @@ -3033,14 +3060,18 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg assert(elementTypeIndex == 0); assert(typeIndex == 1); - info.fields[1] = info.fields[0]; // duplicate the array element type - info.fields[1].offset += info.fields[0].size; + + // duplicate the array element type + info.hasTwoFields = 1; + info.isFloating2nd = info.isFloating1st; + info.sizeShift2nd = info.sizeShift1st; + info.offset2nd = info.offset1st + (1u << info.sizeShift1st); INDEBUG(fieldNames[1] = fieldNames[0];) } return true; } -static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInfo& info DEBUG_ARG(const char* fieldNames[2])) +static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInfo& info, int& typeIndex DEBUG_ARG(const char* fieldNames[2])) { bool isManaged = !th.IsTypeDesc(); MethodTable* pMT = isManaged ? th.AsMethodTable() : th.AsNativeValueType(); @@ -3051,7 +3082,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf if (isManaged) { FieldDesc* fields = pMT->GetApproxFieldDescListRaw(); - int elementTypeIndex = info.NumFields(); + int elementTypeIndex = typeIndex; for (int i = 0; i < nFields; ++i) { if (i > 0 && fields[i-1].GetOffset() + fields[i-1].GetSize() > fields[i].GetOffset()) @@ -3061,21 +3092,20 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf if (type == ELEMENT_TYPE_VALUETYPE) { MethodTable* nested = fields[i].GetApproxFieldTypeHandleThrowing().GetMethodTable(); - if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetOffset(), info DEBUG_ARG(fieldNames))) + if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetOffset(), info, typeIndex DEBUG_ARG(fieldNames))) return false; } else if (fields[i].GetSize() <= TARGET_POINTER_SIZE) { - int typeIndex = info.NumFields(); if (typeIndex >= 2) return false; - info.fields[typeIndex] = { - .isFloating = CorTypeInfo::IsFloat_NoThrow(type), - .size = CorTypeInfo::Size_NoThrow(type), - .offset = offset + fields[i].GetOffset(), - }; INDEBUG(fields[i].GetName_NoThrow(&fieldNames[typeIndex]);) + SetFpStructInRegistersInfoField(info, typeIndex++, + CorTypeInfo::IsFloat_NoThrow(type), + CorTypeInfo::Size_NoThrow(type), + offset + fields[i].GetOffset() + ); } else { @@ -3087,7 +3117,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf { assert(nFields == 1); int nElements = pMT->GetNumInstanceFieldBytes() / fields[0].GetSize(); - if (!HandleInlineArray(elementTypeIndex, nElements, info DEBUG_ARG(fieldNames))) + if (!HandleInlineArray(elementTypeIndex, nElements, info, typeIndex DEBUG_ARG(fieldNames))) return false; } } @@ -3102,29 +3132,28 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf NativeFieldCategory category = fields[i].GetCategory(); if (category == NativeFieldCategory::NESTED) { - int elementTypeIndex = info.NumFields(); + int elementTypeIndex = typeIndex; MethodTable* nested = fields[i].GetNestedNativeMethodTable(); - if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetExternalOffset(), info DEBUG_ARG(fieldNames))) + if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetExternalOffset(), info, typeIndex DEBUG_ARG(fieldNames))) return false; // In native layout fixed arrays are marked as NESTED just like structs int nElements = fields[i].GetNumElements(); - if (!HandleInlineArray(elementTypeIndex, nElements, info DEBUG_ARG(fieldNames))) + if (!HandleInlineArray(elementTypeIndex, nElements, info, typeIndex DEBUG_ARG(fieldNames))) return false; } else if (fields[i].NativeSize() <= TARGET_POINTER_SIZE) { - int typeIndex = info.NumFields(); if (typeIndex >= 2) return false; - info.fields[typeIndex] = { - .isFloating = (category == NativeFieldCategory::FLOAT), - .size = fields[i].NativeSize(), - .offset = offset + fields[i].GetExternalOffset(), - }; INDEBUG(fields[i].GetFieldDesc()->GetName_NoThrow(&fieldNames[typeIndex]);) + SetFpStructInRegistersInfoField(info, typeIndex++, + (category == NativeFieldCategory::FLOAT), + fields[i].NativeSize(), + offset + fields[i].GetExternalOffset() + ); } else { @@ -3137,47 +3166,49 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf #endif #if defined(TARGET_RISCV64) -static void LogFpStructInRegistersInfo(TypeHandle th, FpStructInRegistersInfo info, const char* fieldNames[2], const char* message) -{ - if (!LoggingOn(LF_JIT, LL_EVERYTHING)) - return; - - SString name; - th.GetName(name); - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: Struct %s %s, %u fields%s\n", - name.GetUTF8(), message, info.NumFields(), (info.NumFields() > 0 ? ":" : ""))); - - if (!info.fields[0].Empty()) - { - LOG((LF_JIT, LL_EVERYTHING, "\t1st field %s: %s, %u bytes at offset %u\n", - (fieldNames[0] ? fieldNames[0] : ""), (info.fields[0].isFloating ? "floating" : "integer"), info.fields[0].size, info.fields[0].offset)); - } - if (!info.fields[1].Empty()) - { - LOG((LF_JIT, LL_EVERYTHING, "\t2nd field %s: %s, %u bytes at offset %u\n", - (fieldNames[1] ? fieldNames[1] : ""), (info.fields[1].isFloating ? "floating" : "integer"), info.fields[1].size, info.fields[1].offset)); - } -} static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHandle th) { +#ifdef _DEBUG + const char* fieldNames[2] = {}; + SString nameStr; + th.GetName(nameStr); + const char* name = nameStr.GetUTF8(); + if (name == nullptr) + name = "?"; +#endif + FpStructInRegistersInfo info = {}; - INDEBUG(const char* fieldNames[2] = {};) - if (!FlattenFields(th, 0, info DEBUG_ARG(fieldNames))) + int nFields = 0; + if (!FlattenFields(th, 0, info, nFields DEBUG_ARG(fieldNames))) { - INDEBUG(LogFpStructInRegistersInfo(th, info, fieldNames, "cannot be passed according to floating-point calling convention");) + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " + "Struct %s cannot be passed with floating-point calling convention\n", name)); return FpStructInRegistersInfo{}; } - assert(!(info.fields[0].Empty() && !info.fields[1].Empty())); - - if (!info.fields[0].isFloating && !info.fields[1].isFloating) + if (!info.isFloating1st && !info.isFloating2nd) { - INDEBUG(LogFpStructInRegistersInfo(th, info, fieldNames, "does not have any floating fields");) + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " + "Struct %s does not have any floating fields\n", name)); return FpStructInRegistersInfo{}; } - INDEBUG(LogFpStructInRegistersInfo(th, info, fieldNames, "can be passed according to floating-point calling convention");) + assert((nFields == 2) == info.hasTwoFields); + assert(nFields == 1 || nFields == 2); + assert(info.hasTwoFields || (!info.isFloating2nd && info.sizeShift2nd == 0 && info.offset2nd == 0)); + assert(!info.isFloating1st || info.sizeShift1st > 1); + assert(!info.isFloating2nd || info.sizeShift2nd > 1); + + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " + "Struct %s can be passed with floating-point calling convention, %u fields:\n", name, 1 + info.hasTwoFields)); + LOG((LF_JIT, LL_EVERYTHING, "\t1st field %s: %s, %u bytes at offset %u\n", + fieldNames[0], (info.isFloating1st ? "floating" : "integer"), 1 << info.sizeShift1st, info.offset1st)); + if (info.hasTwoFields) + { + LOG((LF_JIT, LL_EVERYTHING, "\t2nd field %s: %s, %u bytes at offset %u\n", + fieldNames[1], (info.isFloating2nd ? "floating" : "integer"), 1 << info.sizeShift2nd, info.offset2nd)); + } return info; } @@ -3192,20 +3223,19 @@ FpStructInRegistersInfo MethodTable::GetRiscV64PassFpStructInRegistersInfo(TypeH } else if (info.IsFloatingOnly()) { - unsigned n = info.NumFields(); - assert(n > 0); + unsigned n = 1 + info.hasTwoFields; assert((n == 1) == !!(flags & STRUCT_FLOAT_FIELD_ONLY_ONE)); assert((n == 2) == !!(flags & STRUCT_FLOAT_FIELD_ONLY_TWO)); } else // mixed { - assert(info.NumFields() == 2); - assert(info.fields[0].isFloating == !!(flags & STRUCT_FLOAT_FIELD_FIRST)); - assert(info.fields[1].isFloating == !!(flags & STRUCT_FLOAT_FIELD_SECOND)); + assert(info.hasTwoFields); + assert(info.isFloating1st == !!(flags & STRUCT_FLOAT_FIELD_FIRST)); + assert(info.isFloating2nd == !!(flags & STRUCT_FLOAT_FIELD_SECOND)); } - assert((info.fields[0].size == 8) == !!(flags & STRUCT_FIRST_FIELD_SIZE_IS8)); - assert((info.fields[1].size == 8) == !!(flags & STRUCT_SECOND_FIELD_SIZE_IS8)); + assert((info.sizeShift1st == 3) == !!(flags & STRUCT_FIRST_FIELD_SIZE_IS8)); + assert((info.sizeShift2nd == 3) == !!(flags & STRUCT_SECOND_FIELD_SIZE_IS8)); assert(flags == info.ToFlags()); From 0d81d682b0513b53009c26ddcc94b2ba9d1c18dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 20 May 2024 10:02:12 +0200 Subject: [PATCH 20/60] Fix JIT for structs with single float padded with empty structs --- src/coreclr/jit/lclvars.cpp | 14 ++++++---- src/coreclr/jit/lower.cpp | 4 ++- src/coreclr/jit/targetriscv64.cpp | 5 ++++ .../JIT/Directed/StructABI/StructABI.cpp | 6 ++-- src/tests/JIT/Directed/StructABI/StructABI.cs | 28 +++++++++---------- 5 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 5aa1e488b4ef50..d1704c6e199b61 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -901,7 +901,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un var_types argRegTypeInStruct1 = TYP_UNKNOWN; var_types argRegTypeInStruct2 = TYP_UNKNOWN; - if ((strip(corInfoType) == CORINFO_TYPE_VALUECLASS) && (argSize <= MAX_PASS_MULTIREG_BYTES)) + if ((strip(corInfoType) == CORINFO_TYPE_VALUECLASS) LOONGARCH64_ONLY(&&(argSize <= MAX_PASS_MULTIREG_BYTES))) { #if defined(TARGET_LOONGARCH64) floatFlags = info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd); @@ -916,16 +916,17 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un int floatNum = 0; if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) { - assert(argSize <= 8); assert(varDsc->lvExactSize() <= argSize); + cSlotsToEnregister = 1; floatNum = 1; canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1); - argRegTypeInStruct1 = (varDsc->lvExactSize() == 8) ? TYP_DOUBLE : TYP_FLOAT; + argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; } else if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) { + cSlotsToEnregister = 2; floatNum = 2; canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 2); @@ -934,6 +935,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un } else if ((floatFlags & STRUCT_FLOAT_FIELD_FIRST) != 0) { + cSlotsToEnregister = 2; floatNum = 1; canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1); canPassArgInRegisters = canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1); @@ -943,6 +945,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un } else if ((floatFlags & STRUCT_FLOAT_FIELD_SECOND) != 0) { + cSlotsToEnregister = 2; floatNum = 1; canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1); canPassArgInRegisters = canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1); @@ -1098,7 +1101,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un genMapRegArgNumToRegNum(secondAllocatedRegArgNum, argRegTypeInStruct2, info.compCallConv)); varDsc->lvIs4Field2 = (genTypeSize(argRegTypeInStruct2) == 4) ? 1 : 0; } - else if (cSlots > 1) + else if (cSlotsToEnregister > 1) { // Here a struct-arg which needs two registers but only one integer register available, // it has to be split. But we reserved extra 8-bytes for the whole struct. @@ -1279,7 +1282,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un } printf("\n"); } -#endif // DEBUG +#endif // DEBUG } // end if (canPassArgInRegisters) else { @@ -1942,7 +1945,6 @@ void Compiler::lvaClassifyParameterABI() // Old info does not take 4 shadow slots on win-x64 into account. oldStackSize += 32; #endif - assert(lvaParameterStackSize == roundUp(oldStackSize, TARGET_POINTER_SIZE)); } #endif diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 28538fc2b9053f..4755e63c3a20c0 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4691,12 +4691,14 @@ void Lowering::LowerStoreLocCommon(GenTreeLclVarCommon* lclStore) // x86 uses it only for long return type, not for structs. assert(slotCount == 1); assert(lclRegType != TYP_UNDEF); -#else // !TARGET_XARCH || UNIX_AMD64_ABI +#else // !TARGET_XARCH || UNIX_AMD64_ABI if (!comp->IsHfa(layout->GetClassHandle())) { if (slotCount > 1) { +#if !defined(TARGET_RISCV64) && !defined(TARGET_LOONGARCH64) assert(call->HasMultiRegRetVal()); +#endif } else { diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index 1f721bb1ed3758..2f0cbb91fbaea6 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -101,9 +101,14 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, if ((floatFields == 1) && (intFields == 0)) { if (flags == STRUCT_NO_FLOAT_FIELD) + { assert(varTypeIsFloating(type)); // standalone floating-point real + } else + { assert((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0); // struct containing just one FP real + passedSize = ((flags & STRUCT_FIRST_FIELD_SIZE_IS8) != 0) ? 8 : 4; + } return ABIPassingInformation::FromSegment(comp, ABIPassingSegment::InRegister(m_floatRegs.Dequeue(), 0, passedSize)); diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cpp b/src/tests/JIT/Directed/StructABI/StructABI.cpp index e0fb335158628c..bd66b159452dde 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cpp +++ b/src/tests/JIT/Directed/StructABI/StructABI.cpp @@ -14,9 +14,9 @@ struct Empty }; static_assert(sizeof(Empty) == 1, "Empty struct must be sized like in .NET"); -struct EmptyFloat +struct Empty8Float { - Empty FieldE; + Empty e0, e1, e2, e3, e4, e5, e6, e7; float FieldF; }; @@ -110,7 +110,7 @@ static_assert(offsetof(ExplicitFloatLong, s.FieldL) == 5, ""); extern "C" { -DLLEXPORT EmptyFloat EchoEmptyFloatRiscV(EmptyFloat fa0) +DLLEXPORT Empty8Float EchoEmpty8FloatRiscV(Empty8Float fa0) { return fa0; } diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index c36fe9373edfde..ad1d8eb070023f 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -672,17 +672,17 @@ struct Empty { } -struct EmptyFloat +struct Empty8Float { - Empty FieldE; + Empty e0, e1, e2, e3, e4, e5, e6, e7; float FieldF; - public static EmptyFloat Get() + public static Empty8Float Get() { - return new EmptyFloat { FieldF = 3.14159f }; + return new Empty8Float { FieldF = 3.14159f }; } - public bool Equals(EmptyFloat other) + public bool Equals(Empty8Float other) { return FieldF.Equals(other.FieldF); } @@ -986,7 +986,7 @@ public static partial class StructABI static extern DoubleAndByte EnoughRegistersSysV4(double a, double b, double c, double d, double e, double f, double g, DoubleAndByte value); [DllImport("StructABILib")] - static extern EmptyFloat EchoEmptyFloatRiscV(EmptyFloat fa0); + static extern Empty8Float EchoEmpty8FloatRiscV(Empty8Float fa0); [DllImport("StructABILib")] static extern LongEmptyDouble EchoLongEmptyDoubleRiscV(LongEmptyDouble value); @@ -1286,7 +1286,7 @@ static DoubleAndByte EnoughRegistersSysV4Managed(double a, double b, double c, d } [MethodImpl(MethodImplOptions.NoInlining)] - static EmptyFloat EchoEmptyFloatRiscVManaged(EmptyFloat fa0) + static Empty8Float EchoEmpty8FloatRiscVManaged(Empty8Float fa0) { return fa0; } @@ -2447,22 +2447,22 @@ static bool EnoughRegistersSysV4Wrapper() return ok; } - static bool EchoEmptyFloatRiscVWrapper() + static bool EchoEmpty8FloatRiscVWrapper() { bool ok = true; - EmptyFloat expected = EmptyFloat.Get(); - EmptyFloat native = EchoEmptyFloatRiscV(expected); - EmptyFloat managed = EchoEmptyFloatRiscVManaged(expected); + Empty8Float expected = Empty8Float.Get(); + Empty8Float native = EchoEmpty8FloatRiscV(expected); + Empty8Float managed = EchoEmpty8FloatRiscVManaged(expected); if (!expected.Equals(native)) { - Console.WriteLine("Native call for EchoEmptyFloatRiscV failed"); + Console.WriteLine("Native call for EchoEmpty8FloatRiscV failed"); ok = false; } if (!expected.Equals(managed)) { - Console.WriteLine("Managed call for EchoEmptyFloatRiscV failed"); + Console.WriteLine("Managed call for EchoEmpty8FloatRiscV failed"); ok = false; } @@ -2737,7 +2737,7 @@ public static int TestEntryPoint() if (!EnoughRegistersSysV2Wrapper()) ok = false; if (!EnoughRegistersSysV3Wrapper()) ok = false; if (!EnoughRegistersSysV4Wrapper()) ok = false; - if (!EchoEmptyFloatRiscVWrapper()) ok = false; + if (!EchoEmpty8FloatRiscVWrapper()) ok = false; if (!EchoLongEmptyDoubleRiscVWrapper()) ok = false; if (!EchoLongEmptyDoubleByImplicitRefRiscVWrapper()) ok = false; if (!EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper()) ok = false; From ec8d404bc343e858916235fbee4b79f6e3f31532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 21 May 2024 08:22:17 +0200 Subject: [PATCH 21/60] Replace bitfields with manual flags to avoid potential portability problems and to limit change from the previous implementation --- src/coreclr/inc/corinfo.h | 80 +++++++++++++------- src/coreclr/vm/callingconvention.h | 25 ++++--- src/coreclr/vm/jitinterface.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 116 +++++++++++++---------------- 4 files changed, 121 insertions(+), 102 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index d282d26f40f424..8246a24fee652d 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -356,44 +356,72 @@ enum StructFloatFieldInfoFlags STRUCT_HAS_8BYTES_FIELDS_MASK = (STRUCT_FIRST_FIELD_SIZE_IS8 | STRUCT_SECOND_FIELD_SIZE_IS8), }; -// On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating, -// can be passed in registers. FpStructInRegistersInfo represents passing information for such structs. +// Bitfields for FpStructInRegistersInfo::flags +namespace FpStruct +{ + enum Flags + { + // Positions of bitfields + PosOnlyOne = 0, + PosBothFloat = 1, + PosFloat1st = 2, + PosSizeShift1st = 3, + PosFloat2nd = 5, + PosSizeShift2nd = 6, + SizeShiftMask = 0b11, + + UseIntCallConv = 0, // struct is passed according to integer calling convention + + // The bitfields + OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point + BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point + Float1st = 1 << PosFloat1st, // has two fields, 1st is floating (and 2nd is integer) + SizeShift1st = SizeShiftMask << PosSizeShift1st, // log2(size) of 1st field + Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) + SizeShift2nd = SizeShiftMask << PosSizeShift2nd, // log2(size) of 2nd field + // Note: flags OnlyOne, BothFloat, Float1st, and Float2nd are mutually exclusive + }; +} + +// On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating-point, +// can be passed in registers according to hardware FP calling convention. FpStructInRegistersInfo represents +// passing information for such parameters. struct FpStructInRegistersInfo { - bool hasTwoFields : 1; - bool isFloating1st : 1; - bool isFloating2nd : 1; - uint8_t sizeShift1st : 2; - uint8_t sizeShift2nd : 2; - uint32_t offset1st; - uint32_t offset2nd; + FpStruct::Flags flags; + uint32_t offsets[2]; // field offsets in bytes, [0] for 1st, [1] for 2nd - bool IsPassedWithIntegerCallConv() const + unsigned GetSize1st() const { - return !hasTwoFields && !isFloating1st; + unsigned shift = (flags >> FpStruct::PosSizeShift1st) & FpStruct::SizeShiftMask; + return 1u << shift; } - bool IsFloatingOnly() const + unsigned GetSize2nd() const { - return isFloating1st && (isFloating2nd || !hasTwoFields); + unsigned shift = (flags >> FpStruct::PosSizeShift2nd) & FpStruct::SizeShiftMask; + return 1u << shift; } - // TODO: Remove, unless there are places where field offsets are unnecessary and it's difficult to pass FpStructInRegistersInfo (CallDescrWorker?) - StructFloatFieldInfoFlags ToFlags() const + bool IsSize1st8() const { - int flags = - (sizeShift1st == 3 ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | - (sizeShift2nd == 3 ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0); + return (flags & FpStruct::SizeShift1st) == (3 << FpStruct::PosSizeShift1st); + } - if (IsFloatingOnly()) - { - return StructFloatFieldInfoFlags( - flags | (hasTwoFields ? STRUCT_FLOAT_FIELD_ONLY_TWO : STRUCT_FLOAT_FIELD_ONLY_ONE)); - } + bool IsSize2nd8() const + { + return (flags & FpStruct::SizeShift2nd) == (3 << FpStruct::PosSizeShift2nd); + } - return StructFloatFieldInfoFlags(flags | - (isFloating1st ? STRUCT_FLOAT_FIELD_FIRST : 0) | - (isFloating2nd ? STRUCT_FLOAT_FIELD_SECOND : 0)); + StructFloatFieldInfoFlags ToOldFlags() const + { + return StructFloatFieldInfoFlags( + ((flags & FpStruct::OnlyOne) ? STRUCT_FLOAT_FIELD_ONLY_ONE : 0) | + ((flags & FpStruct::BothFloat) ? STRUCT_FLOAT_FIELD_ONLY_TWO : 0) | + ((flags & FpStruct::Float1st) ? STRUCT_FLOAT_FIELD_FIRST : 0) | + ((flags & FpStruct::Float2nd) ? STRUCT_FLOAT_FIELD_SECOND : 0) | + (IsSize1st8() ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | + (IsSize2nd8() ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0)); } }; diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 9fffc1c18c1c23..0a8d56123a91da 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -533,10 +533,9 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE { if (size <= ENREGISTERED_PARAMTYPE_MAXSIZE) return FALSE; - // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields or // more padding, so make sure it's passed according to integer call conv which bounds struct to 16 bytes. - return MethodTable::GetRiscV64PassFpStructInRegistersInfo(th).IsPassedWithIntegerCallConv(); + return MethodTable::GetRiscV64PassFpStructInRegistersInfo(th).flags == FpStruct::UseIntCallConv; } return FALSE; #else @@ -600,8 +599,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE #ifdef TARGET_RISCV64 if (m_argSize <= ENREGISTERED_PARAMTYPE_MAXSIZE) return FALSE; - - return MethodTable::GetRiscV64PassFpStructInRegistersInfo(m_argTypeHandle).IsPassedWithIntegerCallConv(); + return MethodTable::GetRiscV64PassFpStructInRegistersInfo(m_argTypeHandle).flags == FpStruct::UseIntCallConv; #else return (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE); #endif @@ -1807,7 +1805,10 @@ int ArgIteratorTemplate::GetNextOffset() case ELEMENT_TYPE_VALUETYPE: info = MethodTable::GetRiscV64PassFpStructInRegistersInfo(thValueType); - cFPRegs = info.isFloating1st + info.isFloating2nd; + if (info.flags != FpStruct::UseIntCallConv) + { + cFPRegs = (info.flags & FpStruct::BothFloat) ? 2 : 1; + } break; default: @@ -1817,7 +1818,7 @@ int ArgIteratorTemplate::GetNextOffset() if (cFPRegs > 0) { // Pass according to hardware floating-point calling convention iff the argument can be fully enregistered - if (info.hasTwoFields && (info.isFloating1st != info.isFloating2nd)) + if (info.flags & (FpStruct::Float1st | FpStruct::Float2nd)) { assert(cFPRegs == 1); @@ -1830,9 +1831,9 @@ int ArgIteratorTemplate::GetNextOffset() m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg; m_argLocDescForStructInRegs.m_cGenReg = 1; - m_argLocDescForStructInRegs.m_structFields = info.ToFlags(); + m_argLocDescForStructInRegs.m_structFields = info.ToOldFlags(); m_hasArgLocDescForStructInRegs = true; - + int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; m_idxFPReg++; m_idxGenReg++; @@ -1842,16 +1843,18 @@ int ArgIteratorTemplate::GetNextOffset() else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) { int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; - if (info.isFloating1st && info.isFloating2nd && info.sizeShift1st == 2 && info.sizeShift2nd == 2) + if (info.flags == (FpStruct::BothFloat | (2 << FpStruct::PosSizeShift1st) | (2 << FpStruct::PosSizeShift2nd))) { // Two single-float arguments + assert(info.GetSize1st() == sizeof(float)); + assert(info.GetSize2nd() == sizeof(float)); m_argLocDescForStructInRegs.Init(); m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; m_argLocDescForStructInRegs.m_cFloatReg = 2; assert(cFPRegs == 2); - assert(argSize == 2 * 4); + assert(argSize == 2 * sizeof(float)); - m_argLocDescForStructInRegs.m_structFields = info.ToFlags(); + m_argLocDescForStructInRegs.m_structFields = info.ToOldFlags(); m_hasArgLocDescForStructInRegs = true; } m_idxFPReg += cFPRegs; diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index a150868b87905c..cbb862348ad913 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9653,7 +9653,7 @@ uint32_t CEEInfo::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) uint32_t size = STRUCT_NO_FLOAT_FIELD; #if defined(TARGET_RISCV64) - size = (uint32_t)MethodTable::GetRiscV64PassFpStructInRegistersInfo(TypeHandle(cls)).ToFlags(); + size = (uint32_t)MethodTable::GetRiscV64PassFpStructInRegistersInfo(TypeHandle(cls)).ToOldFlags(); #endif // TARGET_RISCV64 EE_TO_JIT_TRANSITION_LEAF(); diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 9ba75f1b2970ac..ba4fd772f79723 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2852,7 +2852,7 @@ static bool HandleInlineArrayOld(int elementTypeIndex, int nElements, StructFloa return true; } -static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], int& typeIndex) +static bool FlattenFieldTypesOld(TypeHandle th, StructFloatFieldInfoFlags types[2], int& typeIndex) { bool isManaged = !th.IsTypeDesc(); MethodTable* pMT = isManaged ? th.AsMethodTable() : th.AsNativeValueType(); @@ -2873,7 +2873,7 @@ static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], if (type == ELEMENT_TYPE_VALUETYPE) { MethodTable* nested = fields[i].GetApproxFieldTypeHandleThrowing().GetMethodTable(); - if (!FlattenFieldTypes(TypeHandle(nested), types, typeIndex)) + if (!FlattenFieldTypesOld(TypeHandle(nested), types, typeIndex)) return false; } else if (fields[i].GetSize() <= TARGET_POINTER_SIZE) @@ -2914,7 +2914,7 @@ static bool FlattenFieldTypes(TypeHandle th, StructFloatFieldInfoFlags types[2], int elementTypeIndex = typeIndex; MethodTable* nested = fields[i].GetNestedNativeMethodTable(); - if (!FlattenFieldTypes(TypeHandle(nested), types, typeIndex)) + if (!FlattenFieldTypesOld(TypeHandle(nested), types, typeIndex)) return false; // In native layout fixed arrays are marked as NESTED just like structs @@ -2950,7 +2950,7 @@ int MethodTable::GetLoongArch64PassStructInRegisterFlags(TypeHandle th) StructFloatFieldInfoFlags types[2] = {STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD}; int nFields = 0; - if (!FlattenFieldTypes(th, types, nFields) || nFields == 0) + if (!FlattenFieldTypesOld(th, types, nFields) || nFields == 0) return STRUCT_NO_FLOAT_FIELD; assert(nFields == 1 || nFields == 2); @@ -2984,7 +2984,7 @@ int MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle th) { StructFloatFieldInfoFlags types[2] = {STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD}; int nFields = 0; - if (!FlattenFieldTypes(th, types, nFields) || nFields == 0) + if (!FlattenFieldTypesOld(th, types, nFields) || nFields == 0) return STRUCT_NO_FLOAT_FIELD; assert(nFields == 1 || nFields == 2); @@ -3027,19 +3027,14 @@ static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int i -1; assert(sizeShift != -1); - info.hasTwoFields = index; - if (index == 0) - { - info.isFloating1st = isFloating; - info.sizeShift1st = sizeShift; - info.offset1st = offset; - } - else - { - info.isFloating2nd = isFloating; - info.sizeShift2nd = sizeShift; - info.offset2nd = offset; - } + using namespace FpStruct; + static const int typeSize = PosFloat2nd - PosFloat1st; + static_assert((Float2nd | SizeShift2nd) == (Float1st | SizeShift1st) << typeSize, + "1st flags need to be 2nd flags shifted by typeSize"); + + int type = (isFloating << PosFloat1st) | (sizeShift << PosSizeShift1st); + info.flags = FpStruct::Flags(info.flags | (type << (typeSize * index))); + info.offsets[index] = offset; } static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInRegistersInfo& info, int& typeIndex DEBUG_ARG(const char* fieldNames[2])) @@ -3061,11 +3056,10 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg assert(elementTypeIndex == 0); assert(typeIndex == 1); - // duplicate the array element type - info.hasTwoFields = 1; - info.isFloating2nd = info.isFloating1st; - info.sizeShift2nd = info.sizeShift1st; - info.offset2nd = info.offset1st + (1u << info.sizeShift1st); + // duplicate the array element info + static const int typeSize = FpStruct::PosFloat2nd - FpStruct::PosFloat1st; + info.flags = FpStruct::Flags((info.flags << typeSize) | info.flags); + info.offsets[1] = info.offsets[0] + info.GetSize1st(); INDEBUG(fieldNames[1] = fieldNames[0];) } return true; @@ -3104,8 +3098,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf SetFpStructInRegistersInfoField(info, typeIndex++, CorTypeInfo::IsFloat_NoThrow(type), CorTypeInfo::Size_NoThrow(type), - offset + fields[i].GetOffset() - ); + offset + fields[i].GetOffset()); } else { @@ -3152,8 +3145,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf SetFpStructInRegistersInfoField(info, typeIndex++, (category == NativeFieldCategory::FLOAT), fields[i].NativeSize(), - offset + fields[i].GetExternalOffset() - ); + offset + fields[i].GetExternalOffset()); } else { @@ -3171,11 +3163,8 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan { #ifdef _DEBUG const char* fieldNames[2] = {}; - SString nameStr; - th.GetName(nameStr); - const char* name = nameStr.GetUTF8(); - if (name == nullptr) - name = "?"; + MethodTable* pMT = !th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType(); + const char* name = pMT->GetDebugClassName(); #endif FpStructInRegistersInfo info = {}; @@ -3187,28 +3176,47 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan return FpStructInRegistersInfo{}; } - if (!info.isFloating1st && !info.isFloating2nd) + using namespace FpStruct; + if ((info.flags & (Float1st | Float2nd)) == 0) { LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " "Struct %s does not have any floating fields\n", name)); return FpStructInRegistersInfo{}; } - - assert((nFields == 2) == info.hasTwoFields); assert(nFields == 1 || nFields == 2); - assert(info.hasTwoFields || (!info.isFloating2nd && info.sizeShift2nd == 0 && info.offset2nd == 0)); - assert(!info.isFloating1st || info.sizeShift1st > 1); - assert(!info.isFloating2nd || info.sizeShift2nd > 1); + if ((info.flags & (Float1st | Float2nd)) == (Float1st | Float2nd)) + { + assert(nFields == 2); + info.flags = FpStruct::Flags(info.flags ^ (Float1st | Float2nd | BothFloat)); // replace (1st|2nd)Float with BothFloat + } + else if (nFields == 1) + { + assert((info.flags & Float1st) != 0); + assert((info.flags & (Float2nd | SizeShift2nd)) == 0); + assert(info.offsets[1] == 0); + info.flags = FpStruct::Flags(info.flags ^ (Float1st | OnlyOne)); // replace Float1st with OnlyOne + } + assert(nFields == 1+ !(info.flags & OnlyOne)); + int floatFlags = info.flags & (OnlyOne | BothFloat | Float1st | Float2nd); + assert(floatFlags != 0); + assert((floatFlags & (floatFlags - 1)) == 0); // there can be only one of the above flags + +#ifdef _DEBUG LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " - "Struct %s can be passed with floating-point calling convention, %u fields:\n", name, 1 + info.hasTwoFields)); + "Struct %s can be passed with floating-point calling convention, flags: %#02x, %i fields:\n", + name, info.flags, nFields)); + const char* type1st = (info.flags & (Float1st | OnlyOne | BothFloat)) ? "floating" : "integer"; LOG((LF_JIT, LL_EVERYTHING, "\t1st field %s: %s, %u bytes at offset %u\n", - fieldNames[0], (info.isFloating1st ? "floating" : "integer"), 1 << info.sizeShift1st, info.offset1st)); - if (info.hasTwoFields) + fieldNames[0], type1st, info.GetSize1st(), info.offsets[0])); + if (nFields == 2) { + const char* type2nd = (info.flags & (Float2nd | BothFloat)) ? "floating" : "integer"; LOG((LF_JIT, LL_EVERYTHING, "\t2nd field %s: %s, %u bytes at offset %u\n", - fieldNames[1], (info.isFloating2nd ? "floating" : "integer"), 1 << info.sizeShift2nd, info.offset2nd)); + fieldNames[1], type2nd, info.GetSize2nd(), info.offsets[1])); } +#endif + return info; } @@ -3217,31 +3225,11 @@ FpStructInRegistersInfo MethodTable::GetRiscV64PassFpStructInRegistersInfo(TypeH FpStructInRegistersInfo info = GetRiscV64PassFpStructInRegistersInfoImpl(th); int flags = GetRiscV64PassStructInRegisterFlags(th); - if (info.IsPassedWithIntegerCallConv()) - { - assert(flags == STRUCT_NO_FLOAT_FIELD); - } - else if (info.IsFloatingOnly()) - { - unsigned n = 1 + info.hasTwoFields; - assert((n == 1) == !!(flags & STRUCT_FLOAT_FIELD_ONLY_ONE)); - assert((n == 2) == !!(flags & STRUCT_FLOAT_FIELD_ONLY_TWO)); - } - else // mixed - { - assert(info.hasTwoFields); - assert(info.isFloating1st == !!(flags & STRUCT_FLOAT_FIELD_FIRST)); - assert(info.isFloating2nd == !!(flags & STRUCT_FLOAT_FIELD_SECOND)); - } - - assert((info.sizeShift1st == 3) == !!(flags & STRUCT_FIRST_FIELD_SIZE_IS8)); - assert((info.sizeShift2nd == 3) == !!(flags & STRUCT_SECOND_FIELD_SIZE_IS8)); - - assert(flags == info.ToFlags()); + assert(flags == info.ToOldFlags()); return info; } -#endif +#endif // TARGET_RISCV64 #if !defined(DACCESS_COMPILE) namespace From 98e18615cc1ba051f212ac7d5243380a5dddc6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 21 May 2024 12:29:47 +0200 Subject: [PATCH 22/60] Update C# version of GetRiscV64PassFpStructInRegistersInfo --- .../tools/Common/JitInterface/CorInfoImpl.cs | 2 +- .../tools/Common/JitInterface/CorInfoTypes.cs | 74 +++++++++ .../RISCV64PassStructInRegister.cs | 156 +++++++++++++++++- .../ReadyToRun/ArgIterator.cs | 22 ++- .../ReadyToRun/TransitionBlock.cs | 11 +- src/coreclr/vm/callingconvention.h | 2 +- 6 files changed, 247 insertions(+), 20 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 31a5dd68927bd1..3292844f60dbd3 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3448,7 +3448,7 @@ private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) private uint getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) { TypeDesc typeDesc = HandleToObject(cls); - return RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(typeDesc); + return (uint)RISCV64PassStructInRegister.GetRiscV64PassFpStructInRegistersInfo(typeDesc).ToOldFlags(); } private uint getThreadTLSIndex(ref void* ppIndirection) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 960b2e8e8113b3..4099c8dc47be26 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1258,6 +1258,80 @@ public enum StructFloatFieldInfoFlags STRUCT_HAS_8BYTES_FIELDS_MASK = (STRUCT_FIRST_FIELD_SIZE_IS8 | STRUCT_SECOND_FIELD_SIZE_IS8), }; + + // Bitfields for FpStructInRegistersInfo.flags + [Flags] + public enum FpStruct + { + // Positions of bitfields + PosOnlyOne = 0, + PosBothFloat = 1, + PosFloat1st = 2, + PosSizeShift1st = 3, + PosFloat2nd = 5, + PosSizeShift2nd = 6, + SizeShiftMask = 0b11, + + UseIntCallConv = 0, // struct is passed according to integer calling convention + + // The bitfields + OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point + BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point + Float1st = 1 << PosFloat1st, // has two fields, 1st is floating (and 2nd is integer) + SizeShift1st = SizeShiftMask << PosSizeShift1st, // log2(size) of 1st field + Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) + SizeShift2nd = SizeShiftMask << PosSizeShift2nd, // log2(size) of 2nd field + // Note: flags OnlyOne, BothFloat, Float1st, and Float2nd are mutually exclusive + }; + + // On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating-point, + // can be passed in registers according to hardware FP calling convention. FpStructInRegistersInfo represents + // passing information for such parameters. + public struct FpStructInRegistersInfo + { + [InlineArray(2)] + public struct Offsets + { + private uint _offset; + } + + public FpStruct flags; + public Offsets offsets; // field offsets in bytes, [0] for 1st, [1] for 2nd + + public uint GetSize1st() + { + int shift = ((int)flags >> (int)FpStruct.PosSizeShift1st) & (int)FpStruct.SizeShiftMask; + return 1u << shift; + } + + public uint GetSize2nd() + { + int shift = ((int)flags >> (int)FpStruct.PosSizeShift2nd) & (int)FpStruct.SizeShiftMask; + return 1u << shift; + } + + public bool IsSize1st8() + { + return (flags & FpStruct.SizeShift1st) == (FpStruct)(3 << (int)FpStruct.PosSizeShift1st); + } + + public bool IsSize2nd8() + { + return (flags & FpStruct.SizeShift2nd) == (FpStruct)(3 << (int)FpStruct.PosSizeShift2nd); + } + + public StructFloatFieldInfoFlags ToOldFlags() + { + return + ((flags & FpStruct.OnlyOne) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE : 0) | + ((flags & FpStruct.BothFloat) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO : 0) | + ((flags & FpStruct.Float1st) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST : 0) | + ((flags & FpStruct.Float2nd) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND : 0) | + (IsSize1st8() ? StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | + (IsSize2nd8() ? StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8 : 0); + } + }; + // DEBUGGER DATA public enum MappingTypes { diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index d7f1001b4e2146..3d686e40840423 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -6,6 +6,7 @@ using ILCompiler; using Internal.TypeSystem; using static Internal.JitInterface.StructFloatFieldInfoFlags; +using static Internal.JitInterface.FpStruct; namespace Internal.JitInterface { @@ -13,7 +14,7 @@ internal static class RISCV64PassStructInRegister { private const int TARGET_POINTER_SIZE = 8; - private static bool HandleInlineArray(int elementTypeIndex, int nElements, Span types, ref int typeIndex) + private static bool HandleInlineArrayOld(int elementTypeIndex, int nElements, Span types, ref int typeIndex) { int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; if (nFlattenedFieldsPerElement == 0) @@ -36,7 +37,7 @@ private static bool HandleInlineArray(int elementTypeIndex, int nElements, Span< return true; } - private static bool FlattenFieldTypes(TypeDesc td, Span types, ref int typeIndex) + private static bool FlattenFieldTypesOld(TypeDesc td, Span types, ref int typeIndex) { IEnumerable fields = td.GetFields(); int nFields = 0; @@ -57,7 +58,7 @@ private static bool FlattenFieldTypes(TypeDesc td, Span 2) + return false; + + if (nElements == 2) + { + if (typeIndex + nFlattenedFieldsPerElement > 2) + return false; + + Debug.Assert(elementTypeIndex == 0); + Debug.Assert(typeIndex == 1); + + // duplicate the array element info + const int typeSize = (int)PosFloat2nd - (int)PosFloat1st; + info.flags = (FpStruct)((int)info.flags << typeSize) | info.flags; + info.offsets[1] = info.offsets[0] + info.GetSize1st(); + } + return true; + } + + private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegistersInfo info, ref int typeIndex) + { + IEnumerable fields = td.GetFields(); + int nFields = 0; + int elementTypeIndex = typeIndex; + FieldDesc prevField = null; + foreach (FieldDesc field in fields) + { + if (field.IsStatic) + continue; + nFields++; + + if (prevField != null && prevField.Offset.AsInt + prevField.FieldType.GetElementSize().AsInt > field.Offset.AsInt) + return false; // overlapping fields + + prevField = field; + + TypeFlags category = field.FieldType.Category; + if (category == TypeFlags.ValueType) + { + TypeDesc nested = field.FieldType; + if (!FlattenFields(nested, offset + (uint)field.Offset.AsInt, ref info, ref typeIndex)) + return false; + } + else if (field.FieldType.GetElementSize().AsInt <= TARGET_POINTER_SIZE) + { + if (typeIndex >= 2) + return false; + + SetFpStructInRegistersInfoField(ref info, typeIndex++, + (category is TypeFlags.Single or TypeFlags.Double), + (uint)field.FieldType.GetElementSize().AsInt, + (uint)field.Offset.AsInt); + } + else + { + return false; + } + } + + if ((td as MetadataType).HasImpliedRepeatedFields()) + { + Debug.Assert(nFields == 1); + int nElements = td.GetElementSize().AsInt / prevField.FieldType.GetElementSize().AsInt; + if (!HandleInlineArray(elementTypeIndex, nElements, ref info, ref typeIndex)) + return false; + } + return true; + } + + private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeDesc td) + { + FpStructInRegistersInfo info = new FpStructInRegistersInfo{}; + int nFields = 0; + if (!FlattenFields(td, 0, ref info, ref nFields)) + { + return new FpStructInRegistersInfo{}; + } + + if ((info.flags & (Float1st | Float2nd)) == 0) + { + return new FpStructInRegistersInfo{}; + } + Debug.Assert(nFields == 1 || nFields == 2); + + if ((info.flags & (Float1st | Float2nd)) == (Float1st | Float2nd)) + { + Debug.Assert(nFields == 2); + info.flags ^= (Float1st | Float2nd | BothFloat); // replace (1st|2nd)Float with BothFloat + } + else if (nFields == 1) + { + Debug.Assert((info.flags & Float1st) != 0); + Debug.Assert((info.flags & (Float2nd | SizeShift2nd)) == 0); + Debug.Assert(info.offsets[1] == 0); + info.flags ^= (Float1st | OnlyOne); // replace Float1st with OnlyOne + } + Debug.Assert(nFields == 1 + Convert.ToInt32((info.flags & OnlyOne) == 0)); + FpStruct floatFlags = info.flags & (OnlyOne | BothFloat | Float1st | Float2nd); + Debug.Assert(floatFlags != 0); + Debug.Assert(((uint)floatFlags & ((uint)floatFlags - 1)) == 0); // there can be only one of the above flags + + return info; + } + + public static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfo(TypeDesc td) + { + FpStructInRegistersInfo info = GetRiscV64PassFpStructInRegistersInfoImpl(td); + uint flags = GetRISCV64PassStructInRegisterFlags(td); + + Debug.Assert(flags == (uint)info.ToOldFlags()); + + return info; + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index 63c42fbef099ce..5292b41d60a9a1 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -1449,7 +1449,7 @@ public int GetNextOffset() throw new NotImplementedException("Varargs on RISC-V not supported yet"); int cFPRegs = 0; - StructFloatFieldInfoFlags flags = STRUCT_NO_FLOAT_FIELD; + FpStructInRegistersInfo info = new FpStructInRegistersInfo{}; _hasArgLocDescForStructInRegs = false; switch (argType) @@ -1461,11 +1461,12 @@ public int GetNextOffset() break; case CorElementType.ELEMENT_TYPE_VALUETYPE: - flags = (StructFloatFieldInfoFlags)RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(_argTypeHandle.GetRuntimeTypeHandle()); - if (flags != STRUCT_NO_FLOAT_FIELD) + TypeDesc td = _argTypeHandle.GetRuntimeTypeHandle(); + info = RISCV64PassStructInRegister.GetRiscV64PassFpStructInRegistersInfo(td); + if (info.flags != FpStruct.UseIntCallConv) { // Struct may be passed according to hardware floating-point calling convention - cFPRegs = ((flags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) ? 2 : 1; + cFPRegs = ((info.flags & FpStruct.BothFloat) != 0) ? 2 : 1; } break; @@ -1476,7 +1477,7 @@ public int GetNextOffset() if (cFPRegs > 0) { // Pass according to hardware floating-point calling convention iff the argument can be fully enregistered - if ((flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) != 0) + if ((info.flags & (FpStruct.Float1st | FpStruct.Float2nd)) != 0) { Debug.Assert(cFPRegs == 1); @@ -1489,7 +1490,7 @@ public int GetNextOffset() _argLocDescForStructInRegs.m_idxGenReg = _riscv64IdxGenReg; _argLocDescForStructInRegs.m_cGenReg = 1; - _argLocDescForStructInRegs.m_floatFlags = (uint)flags; + _argLocDescForStructInRegs.m_floatFlags = (uint)info.ToOldFlags(); _hasArgLocDescForStructInRegs = true; int regOffset = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * _transitionBlock.FloatRegisterSize; @@ -1501,15 +1502,18 @@ public int GetNextOffset() else if (cFPRegs + _riscv64IdxFPReg <= _transitionBlock.NumArgumentRegisters) { int regOffset = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * _transitionBlock.FloatRegisterSize; - if (flags == STRUCT_FLOAT_FIELD_ONLY_TWO) // struct with two single-float fields + if (info.flags == (FpStruct.BothFloat | (FpStruct)(2 << (int)FpStruct.PosSizeShift1st) | (FpStruct)(2 << (int)FpStruct.PosSizeShift2nd))) { + // Struct with two single-float fields + Debug.Assert(info.GetSize1st() == sizeof(float)); + Debug.Assert(info.GetSize2nd() == sizeof(float)); _argLocDescForStructInRegs = new ArgLocDesc(); _argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg; _argLocDescForStructInRegs.m_cFloatReg = 2; Debug.Assert(cFPRegs == 2); - Debug.Assert(argSize == 2 * 4); + Debug.Assert(argSize == 2 * sizeof(float)); - _argLocDescForStructInRegs.m_floatFlags = (uint)STRUCT_FLOAT_FIELD_ONLY_TWO; + _argLocDescForStructInRegs.m_floatFlags = (uint)info.ToOldFlags(); _hasArgLocDescForStructInRegs = true; } _riscv64IdxFPReg += cFPRegs; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index 950eaaeac28725..231ff8ca04cd7f 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -396,9 +396,11 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp } else if (IsRiscV64) { - fpReturnSize = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(thRetType.GetRuntimeTypeHandle()) & 0xff; + TypeDesc td = thRetType.GetRuntimeTypeHandle(); + FpStructInRegistersInfo info = RISCV64PassStructInRegister.GetRiscV64PassFpStructInRegistersInfo(td); + fpReturnSize = (uint)info.ToOldFlags() & 0xff; - if (fpReturnSize != (uint)STRUCT_NO_FLOAT_FIELD || size <= EnregisteredReturnTypeIntegerMaxSize) + if (info.flags != FpStruct.UseIntCallConv || size <= EnregisteredReturnTypeIntegerMaxSize) break; } @@ -724,8 +726,9 @@ public override bool IsArgPassedByRef(TypeHandle th) return false; // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields or more padding - uint flags = RISCV64PassStructInRegister.GetRISCV64PassStructInRegisterFlags(th.GetRuntimeTypeHandle()); - return (flags == (uint)STRUCT_NO_FLOAT_FIELD); + TypeDesc td = th.GetRuntimeTypeHandle(); + FpStructInRegistersInfo info = RISCV64PassStructInRegister.GetRiscV64PassFpStructInRegistersInfo(td); + return (info.flags == FpStruct.UseIntCallConv); } public sealed override int GetRetBuffArgOffset(bool hasThis) => OffsetOfFirstGCRefMapSlot + (hasThis ? 8 : 0); diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 0a8d56123a91da..f3945457c2aaa3 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -1845,7 +1845,7 @@ int ArgIteratorTemplate::GetNextOffset() int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; if (info.flags == (FpStruct::BothFloat | (2 << FpStruct::PosSizeShift1st) | (2 << FpStruct::PosSizeShift2nd))) { - // Two single-float arguments + // Struct with two single-float fields assert(info.GetSize1st() == sizeof(float)); assert(info.GetSize2nd() == sizeof(float)); m_argLocDescForStructInRegs.Init(); From 623a2a6118a078769e92e030391d44c80702258f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 21 May 2024 14:25:07 +0200 Subject: [PATCH 23/60] Replace getRISCV64PassStructInRegisterFlags with getRiscV64PassFpStructInRegistersInfo in JIT interface because the new structure also contains the field offsets --- src/coreclr/inc/corinfo.h | 2 +- src/coreclr/inc/icorjitinfoimpl_generated.h | 2 +- src/coreclr/inc/jiteeversionguid.h | 10 +++---- src/coreclr/jit/ICorJitInfo_names_generated.h | 2 +- .../jit/ICorJitInfo_wrapper_generated.hpp | 8 +++--- src/coreclr/jit/compiler.cpp | 4 +-- src/coreclr/jit/gentree.cpp | 3 ++- src/coreclr/jit/lclvars.cpp | 2 +- src/coreclr/jit/morph.cpp | 3 ++- src/coreclr/jit/targetriscv64.cpp | 4 +-- .../tools/Common/JitInterface/CorInfoImpl.cs | 4 +-- .../JitInterface/CorInfoImpl_generated.cs | 6 ++--- .../RISCV64PassStructInRegister.cs | 2 +- .../ThunkGenerator/ThunkInput.txt | 3 ++- .../aot/jitinterface/jitinterface_generated.h | 6 ++--- .../tools/superpmi/superpmi-shared/lwmlist.h | 2 +- .../superpmi-shared/methodcontext.cpp | 27 ++++++++++++------- .../superpmi/superpmi-shared/methodcontext.h | 7 ++++- .../superpmi-shim-collector/icorjitinfo.cpp | 8 +++--- .../icorjitinfo_generated.cpp | 6 ++--- .../icorjitinfo_generated.cpp | 4 +-- .../tools/superpmi/superpmi/icorjitinfo.cpp | 6 ++--- src/coreclr/vm/callingconvention.h | 6 ++--- src/coreclr/vm/jitinterface.cpp | 8 +++--- src/coreclr/vm/methodtable.cpp | 2 +- src/coreclr/vm/methodtable.h | 1 - 26 files changed, 76 insertions(+), 62 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 8246a24fee652d..40855d30ea1abe 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -3133,7 +3133,7 @@ class ICorStaticInfo virtual void getSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering) = 0; virtual uint32_t getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) = 0; - virtual uint32_t getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) = 0; + virtual FpStructInRegistersInfo getRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE cls) = 0; }; /***************************************************************************** diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 5572a044b9b0aa..9eabbb646d0a1f 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -517,7 +517,7 @@ void getSwiftLowering( uint32_t getLoongArch64PassStructInRegisterFlags( CORINFO_CLASS_HANDLE structHnd) override; -uint32_t getRISCV64PassStructInRegisterFlags( +FpStructInRegistersInfo getRiscV64PassFpStructInRegistersInfo( CORINFO_CLASS_HANDLE structHnd) override; uint32_t getThreadTLSIndex( diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index 762656a3dbbcff..ef353413e15a25 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -43,11 +43,11 @@ typedef const GUID *LPCGUID; #define GUID_DEFINED #endif // !GUID_DEFINED -constexpr GUID JITEEVersionIdentifier = { /* 43854594-cd60-45df-a89f-5b7697586f46 */ - 0x43854594, - 0xcd60, - 0x45df, - {0xa8, 0x9f, 0x5b, 0x76, 0x97, 0x58, 0x6f, 0x46} +constexpr GUID JITEEVersionIdentifier = { /* 8b2708f9-615f-4c1c-b24f-1208c050bc2b */ + 0x8b2708f9, + 0x615f, + 0x4c1c, + {0xb2, 0x4f, 0x12, 0x08, 0xc0, 0x50, 0xbc, 0x2b} }; ////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 2e2573a911993d..668057acb08c0b 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -128,7 +128,7 @@ DEF_CLR_API(getMethodHash) DEF_CLR_API(getSystemVAmd64PassStructInRegisterDescriptor) DEF_CLR_API(getSwiftLowering) DEF_CLR_API(getLoongArch64PassStructInRegisterFlags) -DEF_CLR_API(getRISCV64PassStructInRegisterFlags) +DEF_CLR_API(getRiscV64PassFpStructInRegistersInfo) DEF_CLR_API(getThreadTLSIndex) DEF_CLR_API(getAddrOfCaptureThreadGlobal) DEF_CLR_API(getHelperFtn) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index e001c56c26dcbd..db8092e6a605c3 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1223,12 +1223,12 @@ uint32_t WrapICorJitInfo::getLoongArch64PassStructInRegisterFlags( return temp; } -uint32_t WrapICorJitInfo::getRISCV64PassStructInRegisterFlags( +FpStructInRegistersInfo WrapICorJitInfo::getRiscV64PassFpStructInRegistersInfo( CORINFO_CLASS_HANDLE structHnd) { - API_ENTER(getRISCV64PassStructInRegisterFlags); - uint32_t temp = wrapHnd->getRISCV64PassStructInRegisterFlags(structHnd); - API_LEAVE(getRISCV64PassStructInRegisterFlags); + API_ENTER(getRiscV64PassFpStructInRegistersInfo); + FpStructInRegistersInfo temp = wrapHnd->getRiscV64PassFpStructInRegistersInfo(structHnd); + API_LEAVE(getRiscV64PassFpStructInRegistersInfo); return temp; } diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index bdf2d02e65b206..05be6705486da7 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -769,7 +769,7 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, #ifdef TARGET_RISCV64 // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields // or more padding - uint32_t flags = info.compCompHnd->getRISCV64PassStructInRegisterFlags(clsHnd); + uint32_t flags = info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(clsHnd).ToOldFlags(); if (flags != STRUCT_NO_FLOAT_FIELD) { howToPassStruct = SPK_ByValue; @@ -956,7 +956,7 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, } #elif defined(TARGET_RISCV64) - uint32_t floatFieldFlags = info.compCompHnd->getRISCV64PassStructInRegisterFlags(clsHnd); + uint32_t floatFieldFlags = (uint32_t)info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(clsHnd).ToOldFlags(); if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) { howToReturnStruct = SPK_PrimitiveType; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index ec6413d907c25e..eb5297693da5bb 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -27365,7 +27365,8 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, uint32_t floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(retClsHnd); BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; #else // TARGET_RISCV64 - uint32_t floatFieldFlags = comp->info.compCompHnd->getRISCV64PassStructInRegisterFlags(retClsHnd); + uint32_t floatFieldFlags = + (uint32_t)comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(retClsHnd).ToOldFlags(); // Most cases will be up to 2 floating/integer fields with an occasional empty field BYTE gcPtrsStack[4] = {TYPE_GC_NONE, TYPE_GC_NONE, TYPE_GC_NONE, TYPE_GC_NONE}; unsigned wordCount = roundUp(structSize, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index d1704c6e199b61..be3595cd139aaa 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -906,7 +906,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un #if defined(TARGET_LOONGARCH64) floatFlags = info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd); #else - floatFlags = info.compCompHnd->getRISCV64PassStructInRegisterFlags(typeHnd); + floatFlags = info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(typeHnd).ToOldFlags(); #endif } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 4f52247e508514..c51c5cdeedbd65 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2439,7 +2439,8 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call #if defined(TARGET_LOONGARCH64) floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(argSigClass); #else - floatFieldFlags = comp->info.compCompHnd->getRISCV64PassStructInRegisterFlags(argSigClass); + floatFieldFlags = + comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(argSigClass).ToOldFlags(); #endif passUsingFloatRegs = (floatFieldFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) ? true : false; diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index 2f0cbb91fbaea6..c3e32bcdfe54bc 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -67,8 +67,8 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, passedSize = structLayout->GetSize(); if (!structLayout->IsBlockLayout()) { - flags = (StructFloatFieldInfoFlags)comp->info.compCompHnd->getRISCV64PassStructInRegisterFlags( - structLayout->GetClassHandle()); + flags = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(structLayout->GetClassHandle()) + .ToOldFlags(); if ((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 3292844f60dbd3..dea3032e91e840 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3445,10 +3445,10 @@ private uint getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) return LoongArch64PassStructInRegister.GetLoongArch64PassStructInRegisterFlags(typeDesc); } - private uint getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_STRUCT_* cls) + private FpStructInRegistersInfo getRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_STRUCT_* cls) { TypeDesc typeDesc = HandleToObject(cls); - return (uint)RISCV64PassStructInRegister.GetRiscV64PassFpStructInRegistersInfo(typeDesc).ToOldFlags(); + return RISCV64PassStructInRegister.GetRiscV64PassFpStructInRegistersInfo(typeDesc); } private uint getThreadTLSIndex(ref void* ppIndirection) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index e4ef2264da2556..f528916aa359b5 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -1844,12 +1844,12 @@ private static uint _getLoongArch64PassStructInRegisterFlags(IntPtr thisHandle, } [UnmanagedCallersOnly] - private static uint _getRISCV64PassStructInRegisterFlags(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* structHnd) + private static FpStructInRegistersInfo _getRiscV64PassFpStructInRegistersInfo(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* structHnd) { var _this = GetThis(thisHandle); try { - return _this.getRISCV64PassStructInRegisterFlags(structHnd); + return _this.getRiscV64PassFpStructInRegistersInfo(structHnd); } catch (Exception ex) { @@ -2720,7 +2720,7 @@ private static IntPtr GetUnmanagedCallbacks() callbacks[121] = (delegate* unmanaged)&_getSystemVAmd64PassStructInRegisterDescriptor; callbacks[122] = (delegate* unmanaged)&_getSwiftLowering; callbacks[123] = (delegate* unmanaged)&_getLoongArch64PassStructInRegisterFlags; - callbacks[124] = (delegate* unmanaged)&_getRISCV64PassStructInRegisterFlags; + callbacks[124] = (delegate* unmanaged)&_getRiscV64PassFpStructInRegistersInfo; callbacks[125] = (delegate* unmanaged)&_getThreadTLSIndex; callbacks[126] = (delegate* unmanaged)&_getAddrOfCaptureThreadGlobal; callbacks[127] = (delegate* unmanaged)&_getHelperFtn; diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index 3d686e40840423..979f2025575903 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -87,7 +87,7 @@ private static bool FlattenFieldTypesOld(TypeDesc td, Span types = stackalloc StructFloatFieldInfoFlags[] { STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 2b78b23547432c..42cb478860e890 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -100,6 +100,7 @@ CORINFO_HELPER_DESC*,ref CORINFO_HELPER_DESC const CORINFO_HELPER_DESC*,ref CORINFO_HELPER_DESC int*,ref int unsigned int*,ref uint +FpStructInRegistersInfo,FpStructInRegistersInfo CORINFO_JUST_MY_CODE_HANDLE**,ref CORINFO_JUST_MY_CODE_HANDLE_* @@ -287,7 +288,7 @@ FUNCTIONS bool getSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); void getSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering); uint32_t getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); - uint32_t getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); + FpStructInRegistersInfo getRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHnd); uint32_t getThreadTLSIndex(void **ppIndirection); int32_t * getAddrOfCaptureThreadGlobal(void **ppIndirection); void* getHelperFtn (CorInfoHelpFunc ftnNum, void **ppIndirection); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 905ddd41d82b5e..58bdfe8e96a142 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -135,7 +135,7 @@ struct JitInterfaceCallbacks bool (* getSystemVAmd64PassStructInRegisterDescriptor)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); void (* getSwiftLowering)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering); uint32_t (* getLoongArch64PassStructInRegisterFlags)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); - uint32_t (* getRISCV64PassStructInRegisterFlags)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); + FpStructInRegistersInfo (* getRiscV64PassFpStructInRegistersInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); uint32_t (* getThreadTLSIndex)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); int32_t* (* getAddrOfCaptureThreadGlobal)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); void* (* getHelperFtn)(void * thisHandle, CorInfoExceptionClass** ppException, CorInfoHelpFunc ftnNum, void** ppIndirection); @@ -1400,11 +1400,11 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } - virtual uint32_t getRISCV64PassStructInRegisterFlags( + virtual FpStructInRegistersInfo getRiscV64PassFpStructInRegistersInfo( CORINFO_CLASS_HANDLE structHnd) { CorInfoExceptionClass* pException = nullptr; - uint32_t temp = _callbacks->getRISCV64PassStructInRegisterFlags(_thisHandle, &pException, structHnd); + FpStructInRegistersInfo temp = _callbacks->getRiscV64PassFpStructInRegistersInfo(_thisHandle, &pException, structHnd); if (pException != nullptr) throw pException; return temp; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 75c1ac1b6f52a2..02568237121e86 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -127,7 +127,7 @@ LWM(GetStringConfigValue, DWORD, DWORD) LWM(GetSystemVAmd64PassStructInRegisterDescriptor, DWORDLONG, Agnostic_GetSystemVAmd64PassStructInRegisterDescriptor) LWM(GetSwiftLowering, DWORDLONG, Agnostic_GetSwiftLowering) LWM(GetLoongArch64PassStructInRegisterFlags, DWORDLONG, DWORD) -LWM(GetRISCV64PassStructInRegisterFlags, DWORDLONG, DWORD) +LWM(GetRiscV64PassFpStructInRegistersInfo, DWORDLONG, FpStructInRegistersInfo) LWM(GetTailCallHelpers, Agnostic_GetTailCallHelpers, Agnostic_CORINFO_TAILCALL_HELPERS) LWM(UpdateEntryPointForTailCall, Agnostic_CORINFO_CONST_LOOKUP, Agnostic_CORINFO_CONST_LOOKUP) LWM(GetThreadTLSIndex, DWORD, DLD) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 25e3b1c7744150..a5f0a0c4bd1a35 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6339,28 +6339,35 @@ DWORD MethodContext::repGetLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HA return value; } -void MethodContext::recGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd, DWORD value) +void MethodContext::recGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHnd, FpStructInRegistersInfo value) { - if (GetRISCV64PassStructInRegisterFlags == nullptr) - GetRISCV64PassStructInRegisterFlags = new LightWeightMap(); + if (GetRiscV64PassFpStructInRegistersInfo == nullptr) + GetRiscV64PassFpStructInRegistersInfo = new LightWeightMap(); DWORDLONG key = CastHandle(structHnd); - GetRISCV64PassStructInRegisterFlags->Add(key, value); - DEBUG_REC(dmpGetRISCV64PassStructInRegisterFlags(key, value)); + GetRiscV64PassFpStructInRegistersInfo->Add(key, value); + DEBUG_REC(dmpGetRiscV64PassFpStructInRegistersInfo(key, value)); } -void MethodContext::dmpGetRISCV64PassStructInRegisterFlags(DWORDLONG key, DWORD value) +void MethodContext::dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpStructInRegistersInfo value) { - printf("GetRISCV64PassStructInRegisterFlags key %016" PRIX64 " value-%08X", key, value); + printf("GetRiscV64PassFpStructInRegistersInfo key %016" PRIX64 " value-%#02x-" + "{OnlyOne=%i, BothFloat=%i, Float1st=%i, Size1st=%u, Float2nd=%i, Size2nd=%u, offset1st=%u, offset2nd=%u}", + key, value.flags, + (value.flags & FpStruct::OnlyOne) != 0, + (value.flags & FpStruct::BothFloat) != 0, + (value.flags & FpStruct::Float1st) != 0, value.GetSize1st(), + (value.flags & FpStruct::Float2nd) != 0, value.GetSize2nd(), + value.offsets[0], value.offsets[1]); } -DWORD MethodContext::repGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) +FpStructInRegistersInfo MethodContext::repGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHnd) { DWORDLONG key = CastHandle(structHnd); - DWORD value = LookupByKeyOrMissNoMessage(GetRISCV64PassStructInRegisterFlags, key); - DEBUG_REP(dmpGetRISCV64PassStructInRegisterFlags(key, value)); + FpStructInRegistersInfo value = LookupByKeyOrMissNoMessage(GetRiscV64PassFpStructInRegistersInfo, key); + DEBUG_REP(dmpGetRiscV64PassFpStructInRegistersInfo(key, value)); return value; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index 2c85988aa32e8e..49a320aaeeff0b 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -779,6 +779,10 @@ class MethodContext void dmpGetRISCV64PassStructInRegisterFlags(DWORDLONG key, DWORD value); DWORD repGetRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd); + void recGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHnd, FpStructInRegistersInfo value); + void dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpStructInRegistersInfo value); + FpStructInRegistersInfo repGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHnd); + void recGetRelocTypeHint(void* target, WORD result); void dmpGetRelocTypeHint(DWORDLONG key, DWORD value); WORD repGetRelocTypeHint(void* target); @@ -1162,7 +1166,7 @@ enum mcPackets Packet_GetThreadLocalFieldInfo = 207, Packet_GetThreadLocalStaticBlocksInfo = 208, Packet_GetThreadLocalStaticInfo_NativeAOT = 209, - Packet_GetRISCV64PassStructInRegisterFlags = 210, + // Packet_GetRISCV64PassStructInRegisterFlags = 210, Packet_GetObjectContent = 211, Packet_GetTypeLayout = 212, Packet_HaveSameMethodDefinition = 213, @@ -1170,6 +1174,7 @@ enum mcPackets Packet_IsExactType = 215, Packet_GetSwiftLowering = 216, Packet_IsNullableType = 217, + Packet_GetRiscV64PassFpStructInRegistersInfo = 218, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index ac8efbddcfeec5..5d2d95b189ec08 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1418,11 +1418,11 @@ uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS return temp; } -uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) +FpStructInRegistersInfo interceptor_ICJI::getRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHnd) { - mc->cr->AddCall("getRISCV64PassStructInRegisterFlags"); - uint32_t temp = original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); - mc->recGetRISCV64PassStructInRegisterFlags(structHnd, temp); + mc->cr->AddCall("getRiscV64PassFpStructInRegistersInfo"); + FpStructInRegistersInfo temp = original_ICorJitInfo->getRiscV64PassFpStructInRegistersInfo(structHnd); + mc->recGetRiscV64PassFpStructInRegistersInfo(structHnd, temp); return temp; } diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index a365cdb111e228..012be33f7b323d 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -1004,11 +1004,11 @@ uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags( return original_ICorJitInfo->getLoongArch64PassStructInRegisterFlags(structHnd); } -uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags( +FpStructInRegistersInfo interceptor_ICJI::getRiscV64PassFpStructInRegistersInfo( CORINFO_CLASS_HANDLE structHnd) { - mcs->AddCall("getRISCV64PassStructInRegisterFlags"); - return original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); + mcs->AddCall("getRiscV64PassFpStructInRegistersInfo"); + return original_ICorJitInfo->getRiscV64PassFpStructInRegistersInfo(structHnd); } uint32_t interceptor_ICJI::getThreadTLSIndex( diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 0d80993f52b676..b2265789b67f95 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -880,10 +880,10 @@ uint32_t interceptor_ICJI::getLoongArch64PassStructInRegisterFlags( return original_ICorJitInfo->getLoongArch64PassStructInRegisterFlags(structHnd); } -uint32_t interceptor_ICJI::getRISCV64PassStructInRegisterFlags( +FpStructInRegistersInfo interceptor_ICJI::getRiscV64PassFpStructInRegistersInfo( CORINFO_CLASS_HANDLE structHnd) { - return original_ICorJitInfo->getRISCV64PassStructInRegisterFlags(structHnd); + return original_ICorJitInfo->getRiscV64PassFpStructInRegistersInfo(structHnd); } uint32_t interceptor_ICJI::getThreadTLSIndex( diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index b263c1cafdebc9..6f0bd1efb8f9de 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1246,10 +1246,10 @@ uint32_t MyICJI::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE st return jitInstance->mc->repGetLoongArch64PassStructInRegisterFlags(structHnd); } -uint32_t MyICJI::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE structHnd) +FpStructInRegistersInfo MyICJI::getRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHnd) { - jitInstance->mc->cr->AddCall("getRISCV64PassStructInRegisterFlags"); - return jitInstance->mc->repGetRISCV64PassStructInRegisterFlags(structHnd); + jitInstance->mc->cr->AddCall("getRiscV64PassFpStructInRegistersInfo"); + return jitInstance->mc->repGetRiscV64PassFpStructInRegistersInfo(structHnd); } // Stuff on ICorDynamicInfo diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index f3945457c2aaa3..cc91fa04af8a6f 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -2019,10 +2019,10 @@ void ArgIteratorTemplate::ComputeReturnFlags() } #elif defined(TARGET_RISCV64) assert(!thValueType.IsTypeDesc()); - int structFlags = MethodTable::GetRiscV64PassStructInRegisterFlags(thValueType); - flags |= (structFlags & 0xff) << RETURN_FP_SIZE_SHIFT; + FpStructInRegistersInfo info = MethodTable::GetRiscV64PassFpStructInRegistersInfo(thValueType); + flags |= (info.ToOldFlags() & 0xff) << RETURN_FP_SIZE_SHIFT; - if (structFlags != STRUCT_NO_FLOAT_FIELD || size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) + if (info.flags != FpStruct::UseIntCallConv || size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) break; #else if (size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index cbb862348ad913..41d98274cda223 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -9640,7 +9640,7 @@ uint32_t CEEInfo::getLoongArch64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE c return size; } -uint32_t CEEInfo::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) +FpStructInRegistersInfo CEEInfo::getRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE cls) { CONTRACTL { NOTHROW; @@ -9650,15 +9650,15 @@ uint32_t CEEInfo::getRISCV64PassStructInRegisterFlags(CORINFO_CLASS_HANDLE cls) JIT_TO_EE_TRANSITION_LEAF(); - uint32_t size = STRUCT_NO_FLOAT_FIELD; + FpStructInRegistersInfo info = {}; #if defined(TARGET_RISCV64) - size = (uint32_t)MethodTable::GetRiscV64PassFpStructInRegistersInfo(TypeHandle(cls)).ToOldFlags(); + info = MethodTable::GetRiscV64PassFpStructInRegistersInfo(TypeHandle(cls)); #endif // TARGET_RISCV64 EE_TO_JIT_TRANSITION_LEAF(); - return size; + return info; } /*********************************************************************/ diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index ba4fd772f79723..b5085834706dc5 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2980,7 +2980,7 @@ int MethodTable::GetLoongArch64PassStructInRegisterFlags(TypeHandle th) #endif #if defined(TARGET_RISCV64) -int MethodTable::GetRiscV64PassStructInRegisterFlags(TypeHandle th) +static int GetRiscV64PassStructInRegisterFlags(TypeHandle th) { StructFloatFieldInfoFlags types[2] = {STRUCT_NO_FLOAT_FIELD, STRUCT_NO_FLOAT_FIELD}; int nFields = 0; diff --git a/src/coreclr/vm/methodtable.h b/src/coreclr/vm/methodtable.h index 9116c1f6a9112d..81e45f2741d25a 100644 --- a/src/coreclr/vm/methodtable.h +++ b/src/coreclr/vm/methodtable.h @@ -827,7 +827,6 @@ class MethodTable #if defined(TARGET_LOONGARCH64) static int GetLoongArch64PassStructInRegisterFlags(TypeHandle th); #elif defined(TARGET_RISCV64) - static int GetRiscV64PassStructInRegisterFlags(TypeHandle th); static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfo(TypeHandle th); #endif From f550c22ae3903136cf0c8c47ac4c858c845a392f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 22 May 2024 09:40:16 +0200 Subject: [PATCH 24/60] Fix offset assignment in C# GetRiscV64PassFpStructInRegistersInfo --- src/coreclr/inc/corinfo.h | 3 ++- .../tools/Common/JitInterface/CorInfoTypes.cs | 9 ++------- .../RISCV64PassStructInRegister.cs | 20 ++++++++++++++----- .../superpmi-shared/methodcontext.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 19 +++++++++++++----- 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 40855d30ea1abe..f5e5e6839adafd 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -389,7 +389,8 @@ namespace FpStruct struct FpStructInRegistersInfo { FpStruct::Flags flags; - uint32_t offsets[2]; // field offsets in bytes, [0] for 1st, [1] for 2nd + uint32_t offset1st; + uint32_t offset2nd; unsigned GetSize1st() const { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 4099c8dc47be26..8649061457e6b2 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1289,14 +1289,9 @@ public enum FpStruct // passing information for such parameters. public struct FpStructInRegistersInfo { - [InlineArray(2)] - public struct Offsets - { - private uint _offset; - } - public FpStruct flags; - public Offsets offsets; // field offsets in bytes, [0] for 1st, [1] for 2nd + public uint offset1st; + public uint offset2nd; public uint GetSize1st() { diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index 979f2025575903..c52c5f9843f844 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -125,6 +125,7 @@ private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo bool isFloating, uint size, uint offset) { Debug.Assert(index < 2); + Debug.Assert(!isFloating || size == sizeof(float) || size == sizeof(double)); int sizeShift = (size == 1) ? 0 : @@ -140,7 +141,7 @@ private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo int type = (Convert.ToInt32(isFloating) << (int)PosFloat1st) | (sizeShift << (int)PosSizeShift1st); info.flags |= (FpStruct)(type << (typeSize * index)); - info.offsets[index] = offset; + (index == 0 ? ref info.offset1st : ref info.offset2nd) = offset; } private static bool HandleInlineArray(int elementTypeIndex, int nElements, ref FpStructInRegistersInfo info, ref int typeIndex) @@ -165,7 +166,7 @@ private static bool HandleInlineArray(int elementTypeIndex, int nElements, ref F // duplicate the array element info const int typeSize = (int)PosFloat2nd - (int)PosFloat1st; info.flags = (FpStruct)((int)info.flags << typeSize) | info.flags; - info.offsets[1] = info.offsets[0] + info.GetSize1st(); + info.offset2nd = info.offset1st + info.GetSize1st(); } return true; } @@ -202,7 +203,7 @@ private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegist SetFpStructInRegistersInfoField(ref info, typeIndex++, (category is TypeFlags.Single or TypeFlags.Double), (uint)field.FieldType.GetElementSize().AsInt, - (uint)field.Offset.AsInt); + offset + (uint)field.Offset.AsInt); } else { @@ -244,13 +245,22 @@ private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl { Debug.Assert((info.flags & Float1st) != 0); Debug.Assert((info.flags & (Float2nd | SizeShift2nd)) == 0); - Debug.Assert(info.offsets[1] == 0); + Debug.Assert(info.offset2nd == 0); info.flags ^= (Float1st | OnlyOne); // replace Float1st with OnlyOne } Debug.Assert(nFields == 1 + Convert.ToInt32((info.flags & OnlyOne) == 0)); FpStruct floatFlags = info.flags & (OnlyOne | BothFloat | Float1st | Float2nd); Debug.Assert(floatFlags != 0); - Debug.Assert(((uint)floatFlags & ((uint)floatFlags - 1)) == 0); // there can be only one of the above flags + Debug.Assert(((uint)floatFlags & ((uint)floatFlags - 1)) == 0, + "there can be only one of (OnlyOne | BothFloat | Float1st | Float2nd)"); + if (nFields == 2) + { + uint end1st = info.offset1st + info.GetSize1st(); + uint end2nd = info.offset2nd + info.GetSize2nd(); + Debug.Assert(end1st <= info.offset2nd || end2nd <= info.offset1st, "fields must not overlap"); + } + Debug.Assert(info.offset1st + info.GetSize1st() <= td.GetElementSize().AsInt); + Debug.Assert(info.offset2nd + info.GetSize2nd() <= td.GetElementSize().AsInt); return info; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index a5f0a0c4bd1a35..63215cce880c39 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6359,7 +6359,7 @@ void MethodContext::dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpSt (value.flags & FpStruct::BothFloat) != 0, (value.flags & FpStruct::Float1st) != 0, value.GetSize1st(), (value.flags & FpStruct::Float2nd) != 0, value.GetSize2nd(), - value.offsets[0], value.offsets[1]); + value.offset1st, value.offset2nd); } FpStructInRegistersInfo MethodContext::repGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHnd) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index b5085834706dc5..b0bd68c14170c9 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3018,6 +3018,7 @@ static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int i bool isFloating, unsigned size, uint32_t offset) { assert(index < 2); + assert(!isFloating || size == sizeof(float) || size == sizeof(double)); int sizeShift = (size == 1) ? 0 : @@ -3034,7 +3035,7 @@ static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int i int type = (isFloating << PosFloat1st) | (sizeShift << PosSizeShift1st); info.flags = FpStruct::Flags(info.flags | (type << (typeSize * index))); - info.offsets[index] = offset; + (index == 0 ? info.offset1st : info.offset2nd) = offset; } static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInRegistersInfo& info, int& typeIndex DEBUG_ARG(const char* fieldNames[2])) @@ -3059,7 +3060,7 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg // duplicate the array element info static const int typeSize = FpStruct::PosFloat2nd - FpStruct::PosFloat1st; info.flags = FpStruct::Flags((info.flags << typeSize) | info.flags); - info.offsets[1] = info.offsets[0] + info.GetSize1st(); + info.offset2nd = info.offset1st + info.GetSize1st(); INDEBUG(fieldNames[1] = fieldNames[0];) } return true; @@ -3194,13 +3195,21 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan { assert((info.flags & Float1st) != 0); assert((info.flags & (Float2nd | SizeShift2nd)) == 0); - assert(info.offsets[1] == 0); + assert(info.offset2nd == 0); info.flags = FpStruct::Flags(info.flags ^ (Float1st | OnlyOne)); // replace Float1st with OnlyOne } assert(nFields == 1+ !(info.flags & OnlyOne)); int floatFlags = info.flags & (OnlyOne | BothFloat | Float1st | Float2nd); assert(floatFlags != 0); assert((floatFlags & (floatFlags - 1)) == 0); // there can be only one of the above flags + if (nFields == 2) + { + unsigned end1st = info.offset1st + info.GetSize1st(); + unsigned end2nd = info.offset2nd + info.GetSize2nd(); + assert(end1st <= info.offset2nd || end2nd <= info.offset1st); // fields must not overlap + } + assert(info.offset1st + info.GetSize1st() <= th.GetSize()); + assert(info.offset2nd + info.GetSize2nd() <= th.GetSize()); #ifdef _DEBUG LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " @@ -3208,12 +3217,12 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan name, info.flags, nFields)); const char* type1st = (info.flags & (Float1st | OnlyOne | BothFloat)) ? "floating" : "integer"; LOG((LF_JIT, LL_EVERYTHING, "\t1st field %s: %s, %u bytes at offset %u\n", - fieldNames[0], type1st, info.GetSize1st(), info.offsets[0])); + fieldNames[0], type1st, info.GetSize1st(), info.offset1st)); if (nFields == 2) { const char* type2nd = (info.flags & (Float2nd | BothFloat)) ? "floating" : "integer"; LOG((LF_JIT, LL_EVERYTHING, "\t2nd field %s: %s, %u bytes at offset %u\n", - fieldNames[1], type2nd, info.GetSize2nd(), info.offsets[1])); + fieldNames[1], type2nd, info.GetSize2nd(), info.offset2nd)); } #endif From 338e244594fbe9e9df45ee1820e3c565d044833d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 22 May 2024 10:56:50 +0200 Subject: [PATCH 25/60] Use enregistered struct field offsets in JIT new ABI classifiers --- src/coreclr/jit/targetriscv64.cpp | 46 +++++++++++++++---------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index c3e32bcdfe54bc..91f3c396e4ab43 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -58,29 +58,29 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, ClassLayout* structLayout, WellKnownArg /*wellKnownParam*/) { - StructFloatFieldInfoFlags flags = STRUCT_NO_FLOAT_FIELD; - unsigned intFields = 0, floatFields = 0; - unsigned passedSize; + FpStructInRegistersInfo info = {}; + unsigned intFields = 0, floatFields = 0; + unsigned passedSize; + using namespace FpStruct; if (varTypeIsStruct(type)) { passedSize = structLayout->GetSize(); if (!structLayout->IsBlockLayout()) { - flags = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(structLayout->GetClassHandle()) - .ToOldFlags(); + info = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(structLayout->GetClassHandle()); - if ((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + if ((info.flags & OnlyOne) != 0) { floatFields = 1; } - else if ((flags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) + else if ((info.flags & BothFloat) != 0) { floatFields = 2; } - else if (flags != STRUCT_NO_FLOAT_FIELD) + else if (info.flags != UseIntCallConv) { - assert((flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) != 0); + assert((info.flags & (Float1st | Float2nd)) != 0); floatFields = 1; intFields = 1; } @@ -100,40 +100,38 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, // Hardware floating-point calling convention if ((floatFields == 1) && (intFields == 0)) { - if (flags == STRUCT_NO_FLOAT_FIELD) + unsigned offset = 0; + if (info.flags == UseIntCallConv) { assert(varTypeIsFloating(type)); // standalone floating-point real } else { - assert((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0); // struct containing just one FP real - passedSize = ((flags & STRUCT_FIRST_FIELD_SIZE_IS8) != 0) ? 8 : 4; + assert((info.flags & OnlyOne) != 0); // struct containing just one FP real + passedSize = info.GetSize1st(); + offset = info.offset1st; } - return ABIPassingInformation::FromSegment(comp, ABIPassingSegment::InRegister(m_floatRegs.Dequeue(), 0, + return ABIPassingInformation::FromSegment(comp, ABIPassingSegment::InRegister(m_floatRegs.Dequeue(), offset, passedSize)); } else { assert(varTypeIsStruct(type)); assert((floatFields + intFields) == 2); - assert(flags != STRUCT_NO_FLOAT_FIELD); - assert((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) == 0); + assert(info.flags != UseIntCallConv); + assert((info.flags & OnlyOne) == 0); - unsigned firstSize = ((flags & STRUCT_FIRST_FIELD_SIZE_IS8) != 0) ? 8 : 4; - unsigned secondSize = ((flags & STRUCT_SECOND_FIELD_SIZE_IS8) != 0) ? 8 : 4; - unsigned offset = max(firstSize, secondSize); // TODO: cover empty fields and custom offsets / alignments - - bool isFirstFloat = (flags & (STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_FIRST)) != 0; - bool isSecondFloat = (flags & (STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_SECOND)) != 0; + bool isFirstFloat = (info.flags & (BothFloat | Float1st)) != 0; + bool isSecondFloat = (info.flags & (BothFloat | Float2nd)) != 0; assert(isFirstFloat || isSecondFloat); regNumber firstReg = (isFirstFloat ? m_floatRegs : m_intRegs).Dequeue(); regNumber secondReg = (isSecondFloat ? m_floatRegs : m_intRegs).Dequeue(); - return {2, new (comp, CMK_ABI) - ABIPassingSegment[]{ABIPassingSegment::InRegister(firstReg, 0, firstSize), - ABIPassingSegment::InRegister(secondReg, offset, secondSize)}}; + ABIPassingSegment seg1st = ABIPassingSegment::InRegister(firstReg, info.offset1st, info.GetSize1st()); + ABIPassingSegment seg2nd = ABIPassingSegment::InRegister(secondReg, info.offset2nd, info.GetSize2nd()); + return {2, new (comp, CMK_ABI) ABIPassingSegment[]{seg1st, seg2nd}}; } } else From 4cdd2282571c6970dce36128489cc202238bbd04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 22 May 2024 16:26:25 +0200 Subject: [PATCH 26/60] Fix build: formatting and using static ordering --- src/coreclr/jit/lclvars.cpp | 2 +- .../tools/Common/JitInterface/RISCV64PassStructInRegister.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index d1ce451c3ba68d..4922dcd28d0d03 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1282,7 +1282,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un } printf("\n"); } -#endif // DEBUG +#endif // DEBUG } // end if (canPassArgInRegisters) else { diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index c52c5f9843f844..9d46a658df9de2 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -5,8 +5,8 @@ using System.Diagnostics; using ILCompiler; using Internal.TypeSystem; -using static Internal.JitInterface.StructFloatFieldInfoFlags; using static Internal.JitInterface.FpStruct; +using static Internal.JitInterface.StructFloatFieldInfoFlags; namespace Internal.JitInterface { From 99ec678e4cb7a04551425b1e734b132770a4a543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 23 May 2024 09:28:03 +0200 Subject: [PATCH 27/60] Include GC info in FpStructInRegistersInfo like on System V. While iterating through fields the Ref and ByRef properties are readily available so it's easier than loading a separate GC struct layout and matching it --- src/coreclr/inc/corinfo.h | 47 ++++++++++--- src/coreclr/jit/gentree.cpp | 69 ++++++++++--------- .../tools/Common/JitInterface/CorInfoTypes.cs | 26 ++++--- .../RISCV64PassStructInRegister.cs | 20 +++++- .../superpmi-shared/methodcontext.cpp | 4 +- src/coreclr/vm/methodtable.cpp | 66 ++++++++++++------ 6 files changed, 152 insertions(+), 80 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 701e025b10c46b..e68323487ea9e1 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -365,21 +365,25 @@ namespace FpStruct PosOnlyOne = 0, PosBothFloat = 1, PosFloat1st = 2, - PosSizeShift1st = 3, + PosSizeShift1st = 3, // 2 bits PosFloat2nd = 5, - PosSizeShift2nd = 6, - SizeShiftMask = 0b11, + PosSizeShift2nd = 6, // 2 bits + PosGcRef = 8, + PosGcByRef = 9, UseIntCallConv = 0, // struct is passed according to integer calling convention // The bitfields - OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point - BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point - Float1st = 1 << PosFloat1st, // has two fields, 1st is floating (and 2nd is integer) - SizeShift1st = SizeShiftMask << PosSizeShift1st, // log2(size) of 1st field - Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) - SizeShift2nd = SizeShiftMask << PosSizeShift2nd, // log2(size) of 2nd field + OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point + BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point + Float1st = 1 << PosFloat1st, // has two fields, 1st is floating (and 2nd is integer) + SizeShift1st = 0b11 << PosSizeShift1st, // log2(size) of 1st field + Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) + SizeShift2nd = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field + GcRef = 1 << PosGcRef, // the integer field is a GC object reference + GcByRef = 1 << PosGcByRef, // the integer field is a GC interior pointer // Note: flags OnlyOne, BothFloat, Float1st, and Float2nd are mutually exclusive + // Note: flags GcRef, and ByRef are mutually exclusive and may only co-exist with either Float1st or Float2nd }; } @@ -394,13 +398,13 @@ struct FpStructInRegistersInfo unsigned GetSize1st() const { - unsigned shift = (flags >> FpStruct::PosSizeShift1st) & FpStruct::SizeShiftMask; + unsigned shift = (flags >> FpStruct::PosSizeShift1st) & 0b11; return 1u << shift; } unsigned GetSize2nd() const { - unsigned shift = (flags >> FpStruct::PosSizeShift2nd) & FpStruct::SizeShiftMask; + unsigned shift = (flags >> FpStruct::PosSizeShift2nd) & 0b11; return 1u << shift; } @@ -424,6 +428,27 @@ struct FpStructInRegistersInfo (IsSize1st8() ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | (IsSize2nd8() ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0)); } + + static FpStructInRegistersInfo FromOldFlags(StructFloatFieldInfoFlags flags) + { + unsigned sizeShift1st = (flags & STRUCT_FIRST_FIELD_SIZE_IS8) ? 3 : 2; + unsigned sizeShift2nd = (flags & STRUCT_SECOND_FIELD_SIZE_IS8) ? 3 : 2; + bool hasTwo = !(flags & STRUCT_FLOAT_FIELD_ONLY_ONE); + return { + .flags = FpStruct::Flags( + ((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) ? FpStruct::OnlyOne : 0) | + ((flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? FpStruct::BothFloat : 0) | + ((flags & STRUCT_FLOAT_FIELD_FIRST) ? FpStruct::Float1st : 0) | + ((flags & STRUCT_FLOAT_FIELD_SECOND) ? FpStruct::Float2nd : 0) | + (sizeShift1st << FpStruct::PosSizeShift1st) | + (hasTwo ? (sizeShift2nd << FpStruct::PosSizeShift2nd) : 0) + // No GC ref info in old flags + ), + // Lacking actual field offsets, assume fields are naturally aligned without empty fields or padding + .offset1st = 0, + .offset2nd = hasTwo ? (1u << (sizeShift1st > sizeShift2nd ? sizeShift1st : sizeShift2nd)) : 0, + }; + } }; static_assert(sizeof(FpStructInRegistersInfo) == 3 * sizeof(uint32_t), ""); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 5395de51396ed9..cd83b067c8ef66 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -27421,51 +27421,56 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, #ifdef TARGET_LOONGARCH64 assert(structSize <= (2 * TARGET_POINTER_SIZE)); uint32_t floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(retClsHnd); - BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; + FpStructInRegistersInfo fpInfo = + FpStructInRegistersInfo::FromOldFlags((StructFloatFieldInfoFlags)floatFieldFlags); #else // TARGET_RISCV64 - uint32_t floatFieldFlags = - (uint32_t)comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(retClsHnd).ToOldFlags(); - // Most cases will be up to 2 floating/integer fields with an occasional empty field - BYTE gcPtrsStack[4] = {TYPE_GC_NONE, TYPE_GC_NONE, TYPE_GC_NONE, TYPE_GC_NONE}; - unsigned wordCount = roundUp(structSize, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; - BYTE* gcPtrs = (wordCount <= 4) ? gcPtrsStack : new (comp, CMK_GC) BYTE[wordCount]; - // TODO: we need field offsets along with getRISCV64PassStructInRegisterFlags because we don't know which of - // gcPtrs to inspect below - // TODO: doesn't ClassLayout encapsulate GC info already? + FpStructInRegistersInfo fpInfo = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(retClsHnd); #endif - comp->info.compCompHnd->getClassGClayout(retClsHnd, &gcPtrs[0]); - - if (floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) + comp->compFloatingPointUsed = (fpInfo.flags != FpStruct::UseIntCallConv); + if ((fpInfo.flags & FpStruct::BothFloat) != 0) { - comp->compFloatingPointUsed = true; - m_regType[0] = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - m_regType[1] = (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + m_regType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; + m_regType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; } - else if (floatFieldFlags & STRUCT_FLOAT_FIELD_FIRST) + else if ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) { - comp->compFloatingPointUsed = true; - m_regType[0] = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - m_regType[1] = - (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? comp->getJitGCType(gcPtrs[1]) : TYP_INT; - } - else if (floatFieldFlags & STRUCT_FLOAT_FIELD_SECOND) - { - comp->compFloatingPointUsed = true; - m_regType[0] = - (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? comp->getJitGCType(gcPtrs[0]) : TYP_INT; - m_regType[1] = (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + var_types integerRegType; + if ((fpInfo.flags & FpStruct::GcRef) != 0) + { + integerRegType = TYP_REF; + } + else if ((fpInfo.flags & FpStruct::GcByRef) != 0) + { + integerRegType = TYP_BYREF; + } + else + { + bool is8 = ((fpInfo.flags & FpStruct::Float1st) == 0) ? fpInfo.IsSize1st8() : fpInfo.IsSize2nd8(); + integerRegType = is8 ? TYP_LONG : TYP_INT; + } + + if ((fpInfo.flags & FpStruct::Float1st) != 0) + { + m_regType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; + m_regType[1] = integerRegType; + } + else + { + m_regType[0] = integerRegType; + m_regType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; + } } else { + assert(fpInfo.flags == FpStruct::UseIntCallConv); + assert(structSize <= (2 * TARGET_POINTER_SIZE)); + BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; + comp->info.compCompHnd->getClassGClayout(retClsHnd, &gcPtrs[0]); for (unsigned i = 0; i < 2; ++i) { m_regType[i] = comp->getJitGCType(gcPtrs[i]); } } -#ifdef TARGET_RISCV64 - if (wordCount > 4) - delete[] gcPtrs; -#endif #elif defined(TARGET_X86) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 5f24d5f9ddb25e..24739d5e9fd31d 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1271,21 +1271,25 @@ public enum FpStruct PosOnlyOne = 0, PosBothFloat = 1, PosFloat1st = 2, - PosSizeShift1st = 3, + PosSizeShift1st = 3, // 2 bits PosFloat2nd = 5, - PosSizeShift2nd = 6, - SizeShiftMask = 0b11, + PosSizeShift2nd = 6, // 2 bits + PosGcRef = 8, + PosGcByRef = 9, UseIntCallConv = 0, // struct is passed according to integer calling convention // The bitfields - OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point - BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point - Float1st = 1 << PosFloat1st, // has two fields, 1st is floating (and 2nd is integer) - SizeShift1st = SizeShiftMask << PosSizeShift1st, // log2(size) of 1st field - Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) - SizeShift2nd = SizeShiftMask << PosSizeShift2nd, // log2(size) of 2nd field + OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point + BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point + Float1st = 1 << PosFloat1st, // has two fields, 1st is floating (and 2nd is integer) + SizeShift1st = 0b11 << PosSizeShift1st, // log2(size) of 1st field + Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) + SizeShift2nd = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field + GcRef = 1 << PosGcRef, // the integer field is a GC object reference + GcByRef = 1 << PosGcByRef, // the integer field is a GC interior pointer // Note: flags OnlyOne, BothFloat, Float1st, and Float2nd are mutually exclusive + // Note: flags GcRef, and ByRef are mutually exclusive and may only co-exist with either Float1st or Float2nd }; // On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating-point, @@ -1299,13 +1303,13 @@ public struct FpStructInRegistersInfo public uint GetSize1st() { - int shift = ((int)flags >> (int)FpStruct.PosSizeShift1st) & (int)FpStruct.SizeShiftMask; + int shift = ((int)flags >> (int)FpStruct.PosSizeShift1st) & 0b11; return 1u << shift; } public uint GetSize2nd() { - int shift = ((int)flags >> (int)FpStruct.PosSizeShift2nd) & (int)FpStruct.SizeShiftMask; + int shift = ((int)flags >> (int)FpStruct.PosSizeShift2nd) & 0b11; return 1u << shift; } diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index 9d46a658df9de2..1c4268e05fab41 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -122,7 +122,7 @@ private static uint GetRISCV64PassStructInRegisterFlags(TypeDesc td) private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo info, int index, - bool isFloating, uint size, uint offset) + bool isFloating, bool isGcRef, bool isGcByRef, uint size, uint offset) { Debug.Assert(index < 2); Debug.Assert(!isFloating || size == sizeof(float) || size == sizeof(double)); @@ -140,7 +140,10 @@ private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo "1st flags need to be 2nd flags shifted by typeSize"); int type = (Convert.ToInt32(isFloating) << (int)PosFloat1st) | (sizeShift << (int)PosSizeShift1st); - info.flags |= (FpStruct)(type << (typeSize * index)); + info.flags |= (FpStruct)( + (type << (typeSize * index)) | + (Convert.ToInt32(isGcRef) << (int)PosGcRef) | + (Convert.ToInt32(isGcByRef) << (int)PosGcByRef)); (index == 0 ? ref info.offset1st : ref info.offset2nd) = offset; } @@ -165,7 +168,7 @@ private static bool HandleInlineArray(int elementTypeIndex, int nElements, ref F // duplicate the array element info const int typeSize = (int)PosFloat2nd - (int)PosFloat1st; - info.flags = (FpStruct)((int)info.flags << typeSize) | info.flags; + info.flags |= (FpStruct)((int)info.flags << typeSize); info.offset2nd = info.offset1st + info.GetSize1st(); } return true; @@ -202,6 +205,8 @@ private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegist SetFpStructInRegistersInfoField(ref info, typeIndex++, (category is TypeFlags.Single or TypeFlags.Double), + (category is TypeFlags.Class or TypeFlags.Interface or TypeFlags.Array or TypeFlags.SzArray), + (category is TypeFlags.ByRef), (uint)field.FieldType.GetElementSize().AsInt, offset + (uint)field.Offset.AsInt); } @@ -221,6 +226,8 @@ private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegist return true; } + private static bool IsAligned(uint val, uint alignment) => 0 == (val & (alignment - 1)); + private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeDesc td) { FpStructInRegistersInfo info = new FpStructInRegistersInfo{}; @@ -261,6 +268,13 @@ private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl } Debug.Assert(info.offset1st + info.GetSize1st() <= td.GetElementSize().AsInt); Debug.Assert(info.offset2nd + info.GetSize2nd() <= td.GetElementSize().AsInt); + if ((info.flags & (GcRef | GcByRef)) != 0) + { + Debug.Assert((info.flags ^ (GcRef | GcByRef)) != 0, "either Ref or ByRef, not both"); + Debug.Assert((info.flags & (Float1st | Float2nd)) != 0); + Debug.Assert((info.flags & Float1st) != 0 || (info.IsSize1st8() && IsAligned(info.offset1st, TARGET_POINTER_SIZE))); + Debug.Assert((info.flags & Float2nd) != 0 || (info.IsSize2nd8() && IsAligned(info.offset2nd, TARGET_POINTER_SIZE))); + } return info; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index f4ec6da79edb06..722ee5b9ab22f4 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6327,12 +6327,14 @@ void MethodContext::recGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDL void MethodContext::dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpStructInRegistersInfo value) { printf("GetRiscV64PassFpStructInRegistersInfo key %016" PRIX64 " value-%#02x-" - "{OnlyOne=%i, BothFloat=%i, Float1st=%i, Size1st=%u, Float2nd=%i, Size2nd=%u, offset1st=%u, offset2nd=%u}", + "{OnlyOne=%i, BothFloat=%i, Float1st=%i, Size1st=%u, Float2nd=%i, Size2nd=%u, GcRef=%i, GcByRef=%i offset1st=%u, offset2nd=%u}", key, value.flags, (value.flags & FpStruct::OnlyOne) != 0, (value.flags & FpStruct::BothFloat) != 0, (value.flags & FpStruct::Float1st) != 0, value.GetSize1st(), (value.flags & FpStruct::Float2nd) != 0, value.GetSize2nd(), + (value.flags & FpStruct::GcRef) != 0, + (value.flags & FpStruct::GcByRef) != 0, value.offset1st, value.offset2nd); } diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index b0bd68c14170c9..622dde7bbd9922 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3015,7 +3015,7 @@ static int GetRiscV64PassStructInRegisterFlags(TypeHandle th) #if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int index, - bool isFloating, unsigned size, uint32_t offset) + bool isFloating, bool isGcRef, bool isGcByRef, unsigned size, uint32_t offset) { assert(index < 2); assert(!isFloating || size == sizeof(float) || size == sizeof(double)); @@ -3034,7 +3034,10 @@ static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int i "1st flags need to be 2nd flags shifted by typeSize"); int type = (isFloating << PosFloat1st) | (sizeShift << PosSizeShift1st); - info.flags = FpStruct::Flags(info.flags | (type << (typeSize * index))); + info.flags = FpStruct::Flags(info.flags | + (type << (typeSize * index)) | + (isGcRef << PosGcRef) | + (isGcByRef << PosGcByRef)); (index == 0 ? info.offset1st : info.offset2nd) = offset; } @@ -3059,7 +3062,7 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg // duplicate the array element info static const int typeSize = FpStruct::PosFloat2nd - FpStruct::PosFloat1st; - info.flags = FpStruct::Flags((info.flags << typeSize) | info.flags); + info.flags = FpStruct::Flags(info.flags | (info.flags << typeSize)); info.offset2nd = info.offset1st + info.GetSize1st(); INDEBUG(fieldNames[1] = fieldNames[0];) } @@ -3095,9 +3098,12 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf if (typeIndex >= 2) return false; + CorInfoGCType gcType = CorTypeInfo::GetGCType_NoThrow(type); INDEBUG(fields[i].GetName_NoThrow(&fieldNames[typeIndex]);) SetFpStructInRegistersInfoField(info, typeIndex++, CorTypeInfo::IsFloat_NoThrow(type), + (gcType == TYPE_GC_REF), + (gcType == TYPE_GC_BYREF), CorTypeInfo::Size_NoThrow(type), offset + fields[i].GetOffset()); } @@ -3145,6 +3151,8 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf INDEBUG(fields[i].GetFieldDesc()->GetName_NoThrow(&fieldNames[typeIndex]);) SetFpStructInRegistersInfoField(info, typeIndex++, (category == NativeFieldCategory::FLOAT), + false, + false, fields[i].NativeSize(), offset + fields[i].GetExternalOffset()); } @@ -3162,18 +3170,19 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHandle th) { -#ifdef _DEBUG - const char* fieldNames[2] = {}; - MethodTable* pMT = !th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType(); - const char* name = pMT->GetDebugClassName(); -#endif + const char* fieldNames[2] = {}, *name = nullptr; + if (LoggingOn(LF_JIT, LL_EVERYTHING)) + { + MethodTable* pMT = !th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType(); + name = pMT->GetDebugClassName(); + } FpStructInRegistersInfo info = {}; int nFields = 0; if (!FlattenFields(th, 0, info, nFields DEBUG_ARG(fieldNames))) { LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " - "Struct %s cannot be passed with floating-point calling convention\n", name)); + "Struct %s (%u bytes) cannot be passed with floating-point calling convention\n", name, th.GetSize())); return FpStructInRegistersInfo{}; } @@ -3181,9 +3190,10 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan if ((info.flags & (Float1st | Float2nd)) == 0) { LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " - "Struct %s does not have any floating fields\n", name)); + "Struct %s (%u bytes) does not have any floating fields\n", name, th.GetSize())); return FpStructInRegistersInfo{}; } + assert(nFields == 1 || nFields == 2); if ((info.flags & (Float1st | Float2nd)) == (Float1st | Float2nd)) @@ -3210,21 +3220,33 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan } assert(info.offset1st + info.GetSize1st() <= th.GetSize()); assert(info.offset2nd + info.GetSize2nd() <= th.GetSize()); + if (info.flags & (GcRef | GcByRef)) + { + assert(info.flags ^ (GcRef | GcByRef)); // either Ref or ByRef, not both + assert(info.flags & (Float1st | Float2nd)); + assert((info.flags & Float1st) || (info.IsSize1st8() && IS_ALIGNED(info.offset1st, TARGET_POINTER_SIZE))); + assert((info.flags & Float2nd) || (info.IsSize2nd8() && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); + } -#ifdef _DEBUG - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " - "Struct %s can be passed with floating-point calling convention, flags: %#02x, %i fields:\n", - name, info.flags, nFields)); - const char* type1st = (info.flags & (Float1st | OnlyOne | BothFloat)) ? "floating" : "integer"; - LOG((LF_JIT, LL_EVERYTHING, "\t1st field %s: %s, %u bytes at offset %u\n", - fieldNames[0], type1st, info.GetSize1st(), info.offset1st)); - if (nFields == 2) + if (LoggingOn(LF_JIT, LL_EVERYTHING)) { - const char* type2nd = (info.flags & (Float2nd | BothFloat)) ? "floating" : "integer"; - LOG((LF_JIT, LL_EVERYTHING, "\t2nd field %s: %s, %u bytes at offset %u\n", - fieldNames[1], type2nd, info.GetSize2nd(), info.offset2nd)); + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " + "Struct %s (%u bytes) can be passed with floating-point calling convention, flags: %#02x, %i fields:\n", + name, th.GetSize(), info.flags, nFields)); + const char* integerType = + (info.flags & GcRef) ? "GC ref" : + (info.flags & GcByRef) ? "GC byRef" : + "integer"; + const char* type1st = (info.flags & (Float1st | OnlyOne | BothFloat)) ? "floating" : integerType; + LOG((LF_JIT, LL_EVERYTHING, "\t1st field %s: %s, %u bytes at offset %u\n", + fieldNames[0], type1st, info.GetSize1st(), info.offset1st)); + if (nFields == 2) + { + const char* type2nd = (info.flags & (Float2nd | BothFloat)) ? "floating" : integerType; + LOG((LF_JIT, LL_EVERYTHING, "\t2nd field %s: %s, %u bytes at offset %u\n", + fieldNames[1], type2nd, info.GetSize2nd(), info.offset2nd)); + } } -#endif return info; } From b5e1cdcbb1eea27b787174a34e25588e0077b386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 23 May 2024 10:10:59 +0200 Subject: [PATCH 28/60] Nicer size shift calculation routine --- .../JitInterface/RISCV64PassStructInRegister.cs | 11 ++++------- src/coreclr/vm/methodtable.cpp | 11 ++++------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index 1c4268e05fab41..3ae3a6077fdae2 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -127,13 +127,10 @@ private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo Debug.Assert(index < 2); Debug.Assert(!isFloating || size == sizeof(float) || size == sizeof(double)); - int sizeShift = - (size == 1) ? 0 : - (size == 2) ? 1 : - (size == 4) ? 2 : - (size == 8) ? 3 : - -1; - Debug.Assert(sizeShift != -1); + Debug.Assert(size >= 1 && size <= 8); + Debug.Assert((size & (size - 1)) == 0, "size needs to be a power of 2"); + const int sizeShiftLUT = (0 << (1*2)) | (1 << (2*2)) | (2 << (4*2)) | (3 << (8*2)); + int sizeShift = (sizeShiftLUT >> ((int)size * 2)) & 0b11; const int typeSize = (int)PosFloat2nd - (int)PosFloat1st; Debug.Assert((Float2nd | SizeShift2nd) == (FpStruct)((uint)(Float1st | SizeShift1st) << typeSize), diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 622dde7bbd9922..7380a47d4cae30 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3020,13 +3020,10 @@ static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int i assert(index < 2); assert(!isFloating || size == sizeof(float) || size == sizeof(double)); - int sizeShift = - (size == 1) ? 0 : - (size == 2) ? 1 : - (size == 4) ? 2 : - (size == 8) ? 3 : - -1; - assert(sizeShift != -1); + assert(size >= 1 && size <= 8); + assert((size & (size - 1)) == 0); // size needs to be a power of 2 + static const int sizeShiftLUT = (0 << (1*2)) | (1 << (2*2)) | (2 << (4*2)) | (3 << (8*2)); + int sizeShift = (sizeShiftLUT >> (size * 2)) & 0b11; using namespace FpStruct; static const int typeSize = PosFloat2nd - PosFloat1st; From 95e787d5065506e2a495be2e77408b64fbbe8e6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 24 May 2024 08:22:59 +0200 Subject: [PATCH 29/60] Fix build --- src/coreclr/inc/corinfo.h | 6 +++--- src/coreclr/jit/gentree.cpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index e68323487ea9e1..a5ddefabcbfc25 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -435,7 +435,7 @@ struct FpStructInRegistersInfo unsigned sizeShift2nd = (flags & STRUCT_SECOND_FIELD_SIZE_IS8) ? 3 : 2; bool hasTwo = !(flags & STRUCT_FLOAT_FIELD_ONLY_ONE); return { - .flags = FpStruct::Flags( + FpStruct::Flags( ((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) ? FpStruct::OnlyOne : 0) | ((flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? FpStruct::BothFloat : 0) | ((flags & STRUCT_FLOAT_FIELD_FIRST) ? FpStruct::Float1st : 0) | @@ -445,8 +445,8 @@ struct FpStructInRegistersInfo // No GC ref info in old flags ), // Lacking actual field offsets, assume fields are naturally aligned without empty fields or padding - .offset1st = 0, - .offset2nd = hasTwo ? (1u << (sizeShift1st > sizeShift2nd ? sizeShift1st : sizeShift2nd)) : 0, + 0, + hasTwo ? (1u << (sizeShift1st > sizeShift2nd ? sizeShift1st : sizeShift2nd)) : 0, }; } }; diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index cd83b067c8ef66..4c8400524843ab 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -27464,7 +27464,7 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, { assert(fpInfo.flags == FpStruct::UseIntCallConv); assert(structSize <= (2 * TARGET_POINTER_SIZE)); - BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; + BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; comp->info.compCompHnd->getClassGClayout(retClsHnd, &gcPtrs[0]); for (unsigned i = 0; i < 2; ++i) { From 48e7944ef1427210d873dc9d0225bc1a69ca9e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 28 May 2024 10:09:42 +0200 Subject: [PATCH 30/60] Fix Empty8Float test Add field offset to routines in JIT where single-register structs are lowered to its only field --- src/coreclr/jit/gentree.cpp | 77 ++++++++++++++++---------- src/coreclr/jit/gentree.h | 30 ++++++++++- src/coreclr/jit/lower.cpp | 6 +-- src/coreclr/jit/morph.cpp | 104 +++++++++++++++++------------------- 4 files changed, 131 insertions(+), 86 deletions(-) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 4c8400524843ab..7f72b38b1910e1 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -27351,6 +27351,21 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, assert(returnType != TYP_UNKNOWN); assert(returnType != TYP_STRUCT); m_regType[0] = returnType; + +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) +#ifdef TARGET_LOONGARCH64 + uint32_t floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(retClsHnd); + FpStructInRegistersInfo fpInfo = + FpStructInRegistersInfo::FromOldFlags((StructFloatFieldInfoFlags)floatFieldFlags); +#else // TARGET_RISCV64 + FpStructInRegistersInfo fpInfo = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(retClsHnd); +#endif + if (fpInfo.flags != FpStruct::UseIntCallConv) + { + assert((fpInfo.flags & FpStruct::OnlyOne) != 0); + m_fieldOffset[0] = fpInfo.offset1st; + } +#endif // defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) break; } @@ -27426,43 +27441,51 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, #else // TARGET_RISCV64 FpStructInRegistersInfo fpInfo = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(retClsHnd); #endif - comp->compFloatingPointUsed = (fpInfo.flags != FpStruct::UseIntCallConv); - if ((fpInfo.flags & FpStruct::BothFloat) != 0) - { - m_regType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; - m_regType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; - } - else if ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) + if (fpInfo.flags != FpStruct::UseIntCallConv) { - var_types integerRegType; - if ((fpInfo.flags & FpStruct::GcRef) != 0) - { - integerRegType = TYP_REF; - } - else if ((fpInfo.flags & FpStruct::GcByRef) != 0) - { - integerRegType = TYP_BYREF; - } - else - { - bool is8 = ((fpInfo.flags & FpStruct::Float1st) == 0) ? fpInfo.IsSize1st8() : fpInfo.IsSize2nd8(); - integerRegType = is8 ? TYP_LONG : TYP_INT; - } + comp->compFloatingPointUsed = true; + + m_fieldOffset[0] = fpInfo.offset1st; + m_fieldOffset[1] = fpInfo.offset2nd; - if ((fpInfo.flags & FpStruct::Float1st) != 0) + assert((fpInfo.flags & FpStruct::OnlyOne) == 0); + if ((fpInfo.flags & FpStruct::BothFloat) != 0) { m_regType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; - m_regType[1] = integerRegType; + m_regType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; } - else + else if ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) { - m_regType[0] = integerRegType; - m_regType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; + var_types integerRegType; + if ((fpInfo.flags & FpStruct::GcRef) != 0) + { + integerRegType = TYP_REF; + } + else if ((fpInfo.flags & FpStruct::GcByRef) != 0) + { + integerRegType = TYP_BYREF; + } + else + { + bool is8 = + ((fpInfo.flags & FpStruct::Float1st) == 0) ? fpInfo.IsSize1st8() : fpInfo.IsSize2nd8(); + integerRegType = is8 ? TYP_LONG : TYP_INT; + } + + if ((fpInfo.flags & FpStruct::Float1st) != 0) + { + m_regType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; + m_regType[1] = integerRegType; + } + else + { + m_regType[0] = integerRegType; + m_regType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; + } } } else { - assert(fpInfo.flags == FpStruct::UseIntCallConv); assert(structSize <= (2 * TARGET_POINTER_SIZE)); BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; comp->info.compCompHnd->getClassGClayout(retClsHnd, &gcPtrs[0]); diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 6a1b50e1569c90..16cbf40213dfc5 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4256,6 +4256,13 @@ struct ReturnTypeDesc private: var_types m_regType[MAX_RET_REG_COUNT]; +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + // Structs according to hardware floating-point calling convention are passed as two logical fields, each in + // separate register, disregarding struct layout such as packing, custom alignment, padding with empty structs, etc. + // We need size (can be derived from m_regType) & offset of each field for memory load/stores + unsigned m_fieldOffset[MAX_RET_REG_COUNT]; +#endif + #ifdef DEBUG bool m_inited; #endif @@ -4287,6 +4294,9 @@ struct ReturnTypeDesc for (unsigned i = 0; i < MAX_RET_REG_COUNT; ++i) { m_regType[i] = TYP_UNKNOWN; +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + m_fieldOffset[i] = 0; +#endif } #ifdef DEBUG m_inited = false; @@ -4332,8 +4342,11 @@ struct ReturnTypeDesc for (unsigned i = regCount + 1; i < MAX_RET_REG_COUNT; ++i) { assert(m_regType[i] == TYP_UNKNOWN); - } +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + assert(m_fieldOffset[i] == 0); #endif + } +#endif // DEBUG return regCount; } @@ -4382,6 +4395,17 @@ struct ReturnTypeDesc return result; } + unsigned GetSingleReturnFieldOffset() const + { + assert(!IsMultiRegRetType()); + assert(m_regType[0] != TYP_UNKNOWN); +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + return m_fieldOffset[0]; +#else + return 0; +#endif + } + // Get i'th ABI return register regNumber GetABIReturnReg(unsigned idx, CorInfoCallConvExtension callConv) const; @@ -4491,6 +4515,7 @@ struct CallArgABIInformation , ByteAlignment(0) #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) , StructFloatFieldType() + , StructFloatFieldOffset() #endif , ArgType(TYP_UNDEF) , IsBackFilled(false) @@ -4527,10 +4552,11 @@ struct CallArgABIInformation SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR StructDesc; #endif // UNIX_AMD64_ABI #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // For LoongArch64's ABI, the struct which has float field(s) and no more than two fields + // For LoongArch64's and RISC-V 64's ABI, the struct which has float field(s) and no more than two fields // may be passed by float register(s). // e.g `struct {int a; float b;}` passed by an integer register and a float register. var_types StructFloatFieldType[2]; + unsigned StructFloatFieldOffset[2]; #endif // The type used to pass this argument. This is generally the original // argument type, but when a struct is passed as a scalar type, this is diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 9777ddfdc029ac..8143a3490bfdec 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -4998,6 +4998,7 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret) // Otherwise we don't mind that we leave the upper bits undefined. lclVar->ChangeType(ret->TypeGet()); } + lclVar->AsLclFld()->SetLclOffs(comp->compRetTypeDesc.GetSingleReturnFieldOffset()); } else { @@ -5140,7 +5141,6 @@ void Lowering::LowerStoreSingleRegCallStruct(GenTreeBlk* store) const ClassLayout* layout = store->GetLayout(); var_types regType = layout->GetRegisterType(); - if (regType != TYP_UNDEF) { #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) @@ -5186,8 +5186,8 @@ GenTreeLclVar* Lowering::SpillStructCallResult(GenTreeCall* call) const comp->lvaSetVarDoNotEnregister(spillNum DEBUGARG(DoNotEnregisterReason::LocalField)); CORINFO_CLASS_HANDLE retClsHnd = call->gtRetClsHnd; comp->lvaSetStruct(spillNum, retClsHnd, false); - GenTreeLclFld* spill = comp->gtNewStoreLclFldNode(spillNum, call->TypeGet(), 0, call); - + unsigned offset = call->GetReturnTypeDesc()->GetSingleReturnFieldOffset(); + GenTreeLclFld* spill = comp->gtNewStoreLclFldNode(spillNum, call->TypeGet(), offset, call); BlockRange().InsertAfter(call, spill); ContainCheckStoreLoc(spill); GenTreeLclVar* loadCallResult = comp->gtNewLclvNode(spillNum, TYP_STRUCT)->AsLclVar(); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 50193964af6e05..9b83f81b111ae8 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2322,8 +2322,8 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) assert(!callIsVararg && !isHfaArg); - passUsingFloatRegs = varTypeUsesFloatReg(argSigType); - DWORD floatFieldFlags = STRUCT_NO_FLOAT_FIELD; + passUsingFloatRegs = varTypeUsesFloatReg(argSigType); + FpStructInRegistersInfo fpInfo = {}; #else #error Unsupported or unset target architecture @@ -2435,32 +2435,27 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call assert((howToPassStruct == Compiler::SPK_ByValue) || (howToPassStruct == Compiler::SPK_PrimitiveType)); #if defined(TARGET_LOONGARCH64) - floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(argSigClass); + uint32_t floatFlagFields = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(argSigClass); + FpStructInRegistersInfo fpInfo = + FpStructInRegistersInfo::FromOldFlags((StructFloatFieldInfoFlags)floatFieldFlags); #else - floatFieldFlags = - comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(argSigClass).ToOldFlags(); + fpInfo = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(argSigClass); #endif - - passUsingFloatRegs = (floatFieldFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) ? true : false; - comp->compFloatingPointUsed |= passUsingFloatRegs; - - if ((floatFieldFlags & (STRUCT_HAS_FLOAT_FIELDS_MASK ^ STRUCT_FLOAT_FIELD_ONLY_ONE)) != 0) - { - // On LoongArch64, "getPrimitiveTypeForStruct" will incorrectly return "TYP_LONG" - // for "struct { float, float }", and retyping to a primitive here will cause the - // multi-reg morphing to not kick in (the struct in question needs to be passed in - // two FP registers). Here is just keep "structBaseType" as "TYP_STRUCT". - // TODO-LoongArch64: fix "getPrimitiveTypeForStruct". - structBaseType = TYP_STRUCT; - } - - if ((floatFieldFlags & (STRUCT_HAS_FLOAT_FIELDS_MASK ^ STRUCT_FLOAT_FIELD_ONLY_TWO)) != 0) - { - size = 1; - } - else if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) + if (fpInfo.flags != FpStruct::UseIntCallConv) { - size = 2; + passUsingFloatRegs = comp->compFloatingPointUsed = true; + if ((fpInfo.flags & FpStruct::OnlyOne) == 0) + { + assert((fpInfo.flags & (FpStruct::BothFloat | FpStruct::Float1st | FpStruct::Float2nd)) != 0); + // On LoongArch64 and RISC-V64, "getPrimitiveTypeForStruct" will incorrectly return "TYP_LONG" + // for "struct { float, float }", and retyping to a primitive here will cause the + // multi-reg morphing to not kick in (the struct in question needs to be passed in + // two FP registers). Here is just keep "structBaseType" as "TYP_STRUCT". + // TODO-LoongArch64, TODO-RISCV64: fix "getPrimitiveTypeForStruct". + structBaseType = TYP_STRUCT; + } + // 'size' for now means number of floating-point registers used + size = ((fpInfo.flags & FpStruct::BothFloat) != 0) ? 2 : 1; } } else // if (passStructByRef) @@ -2617,32 +2612,31 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call if (isStructArg) { - if ((floatFieldFlags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) && - passUsingFloatRegs) + if ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0 && passUsingFloatRegs) { passUsingFloatRegs = isRegArg = intArgRegNum < maxRegArgs; } if (!passUsingFloatRegs) { - size = structSize > 8 ? 2 : 1; - structBaseType = structSize <= 8 ? TYP_I_IMPL : TYP_STRUCT; - floatFieldFlags = 0; + size = structSize > 8 ? 2 : 1; + structBaseType = structSize <= 8 ? TYP_I_IMPL : TYP_STRUCT; + fpInfo = {}; } - else if (passUsingFloatRegs) + else // if (passUsingFloatRegs) { - if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) + if ((fpInfo.flags & FpStruct::BothFloat) != 0) { nextOtherRegNum = genMapFloatRegArgNumToRegNum(nextFltArgRegNum + 1); } - else if ((floatFieldFlags & STRUCT_FLOAT_FIELD_SECOND) != 0) + else if ((fpInfo.flags & FpStruct::Float2nd) != 0) { assert(size == 1); size = 2; passUsingFloatRegs = false; nextOtherRegNum = genMapFloatRegArgNumToRegNum(nextFltArgRegNum); } - else if ((floatFieldFlags & STRUCT_FLOAT_FIELD_FIRST) != 0) + else if ((fpInfo.flags & FpStruct::Float1st) != 0) { assert(size == 1); size = 2; @@ -2651,7 +2645,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call } } - assert(!isHfaArg); // LoongArch64 does not support HFA. + assert(!isHfaArg); // LoongArch64 and RISC-V64 do not support HFA. } // if we run out of floating-point argument registers, try the int argument registers. @@ -2868,7 +2862,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call assert(size == 2); intArgRegNum = maxRegArgs; } - else if ((floatFieldFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) == 0x0) + else if (fpInfo.flags == FpStruct::UseIntCallConv) { if (passUsingFloatRegs) { @@ -2879,38 +2873,37 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call intArgRegNum += size; } } - else if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + else if ((fpInfo.flags & FpStruct::OnlyOne) != 0) { - structBaseType = (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + structBaseType = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; fltArgRegNum += 1; - arg.AbiInfo.StructFloatFieldType[0] = structBaseType; + arg.AbiInfo.StructFloatFieldType[0] = structBaseType; + arg.AbiInfo.StructFloatFieldOffset[0] = fpInfo.offset1st; } - else if ((floatFieldFlags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_SECOND)) != 0) + else if ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) { fltArgRegNum += 1; intArgRegNum += 1; - if ((floatFieldFlags & STRUCT_FLOAT_FIELD_FIRST) != 0) + arg.AbiInfo.StructFloatFieldOffset[0] = fpInfo.offset1st; + arg.AbiInfo.StructFloatFieldOffset[1] = fpInfo.offset2nd; + if ((fpInfo.flags & FpStruct::Float1st) != 0) { - arg.AbiInfo.StructFloatFieldType[0] = - (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - arg.AbiInfo.StructFloatFieldType[1] = - (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT; + arg.AbiInfo.StructFloatFieldType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; + arg.AbiInfo.StructFloatFieldType[1] = fpInfo.IsSize2nd8() ? TYP_LONG : TYP_INT; } else { - arg.AbiInfo.StructFloatFieldType[0] = - (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT; - arg.AbiInfo.StructFloatFieldType[1] = - (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + arg.AbiInfo.StructFloatFieldType[0] = fpInfo.IsSize1st8() ? TYP_LONG : TYP_INT; + arg.AbiInfo.StructFloatFieldType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; } } - else if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) + else if ((fpInfo.flags & FpStruct::BothFloat) != 0) { fltArgRegNum += 2; - arg.AbiInfo.StructFloatFieldType[0] = - (floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - arg.AbiInfo.StructFloatFieldType[1] = - (floatFieldFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; + arg.AbiInfo.StructFloatFieldOffset[0] = fpInfo.offset1st; + arg.AbiInfo.StructFloatFieldOffset[1] = fpInfo.offset2nd; + arg.AbiInfo.StructFloatFieldType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; + arg.AbiInfo.StructFloatFieldType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; } } #else @@ -3361,6 +3354,9 @@ GenTreeCall* Compiler::fgMorphArgs(GenTreeCall* call) if (argObj->OperIs(GT_LCL_VAR)) { argObj->SetOper(GT_LCL_FLD); +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + argObj->AsLclFld()->SetLclOffs(arg.AbiInfo.StructFloatFieldOffset[0]); +#endif } lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::SwizzleArg)); } From 7102f3124c91de37cc311de69d6455698e036780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 28 May 2024 11:13:07 +0200 Subject: [PATCH 31/60] Add EmptyFloatEmpty5(U)Byte tests --- .../JIT/Directed/StructABI/StructABI.cpp | 28 ++++- src/tests/JIT/Directed/StructABI/StructABI.cs | 116 ++++++++++++++++-- 2 files changed, 135 insertions(+), 9 deletions(-) diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cpp b/src/tests/JIT/Directed/StructABI/StructABI.cpp index bd66b159452dde..d0ce6466d3a096 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cpp +++ b/src/tests/JIT/Directed/StructABI/StructABI.cpp @@ -20,6 +20,22 @@ struct Empty8Float float FieldF; }; +struct EmptyFloatEmpty5Byte +{ + Empty e; + float FieldF; + Empty e0, e1, e2, e3, e4; + int8_t FieldB; +}; + +struct EmptyFloatEmpty5UByte +{ + Empty e; + float FieldF; + Empty e0, e1, e2, e3, e4; + uint8_t FieldB; +}; + struct LongEmptyDouble { int64_t FieldL; @@ -115,6 +131,16 @@ DLLEXPORT Empty8Float EchoEmpty8FloatRiscV(Empty8Float fa0) return fa0; } +DLLEXPORT EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscV(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1) +{ + return fa1_a1; +} + +DLLEXPORT EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscV(int a0, float fa0, EmptyFloatEmpty5UByte fa1_a1) +{ + return fa1_a1; +} + DLLEXPORT LongEmptyDouble EchoLongEmptyDoubleRiscV(LongEmptyDouble a0_fa0) { return a0_fa0; @@ -147,7 +173,7 @@ DLLEXPORT LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(LongEmptyAndFloat a0_fa0) return a0_fa0; } -DLLEXPORT ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscV(ArrayOfEmptiesFloatDouble a0_a1) +DLLEXPORT ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscV(ArrayOfEmptiesFloatDouble a0_a1) { return a0_a1; } diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index ad1d8eb070023f..79a56b82fcec75 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -688,6 +688,42 @@ public bool Equals(Empty8Float other) } } +struct EmptyFloatEmpty5Byte +{ + Empty e; + float FieldF; + Empty e0, e1, e2, e3, e4; + sbyte FieldB; + + public static EmptyFloatEmpty5Byte Get() + { + return new EmptyFloatEmpty5Byte { FieldF = 3.14159f, FieldB = -123 }; + } + + public bool Equals(EmptyFloatEmpty5Byte other) + { + return FieldF.Equals(other.FieldF) && FieldB == other.FieldB; + } +} + +struct EmptyFloatEmpty5UByte +{ + Empty e; + float FieldF; + Empty e0, e1, e2, e3, e4; + byte FieldB; + + public static EmptyFloatEmpty5UByte Get() + { + return new EmptyFloatEmpty5UByte { FieldF = 3.14159f, FieldB = 234 }; + } + + public bool Equals(EmptyFloatEmpty5UByte other) + { + return FieldF.Equals(other.FieldF) && FieldB == other.FieldB; + } +} + struct LongEmptyDouble { long FieldL; @@ -988,6 +1024,12 @@ public static partial class StructABI [DllImport("StructABILib")] static extern Empty8Float EchoEmpty8FloatRiscV(Empty8Float fa0); + [DllImport("StructABILib")] + static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscV(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1); + + [DllImport("StructABILib")] + static extern EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscV(int a0, float fa0, EmptyFloatEmpty5UByte fa1_a1); + [DllImport("StructABILib")] static extern LongEmptyDouble EchoLongEmptyDoubleRiscV(LongEmptyDouble value); @@ -1009,7 +1051,7 @@ static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRisc static extern LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(LongEmptyAndFloat a0_fa0); [DllImport("StructABILib")] - static extern ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscV( + static extern ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscV( ArrayOfEmptiesFloatDouble a0_a1); [DllImport("StructABILib")] @@ -1291,6 +1333,18 @@ static Empty8Float EchoEmpty8FloatRiscVManaged(Empty8Float fa0) return fa0; } + [MethodImpl(MethodImplOptions.NoInlining)] + static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscVManaged(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1) + { + return fa1_a1; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscVManaged(int a0, float fa0, EmptyFloatEmpty5UByte fa1_a1) + { + return fa1_a1; + } + [MethodImpl(MethodImplOptions.NoInlining)] static LongEmptyDouble EchoLongEmptyDoubleRiscVManaged(LongEmptyDouble a0_fa0) { @@ -1330,7 +1384,7 @@ static LongEmptyAndFloat EchoLongEmptyAndFloatRiscVManaged(LongEmptyAndFloat a0_ } [MethodImpl(MethodImplOptions.NoInlining)] - static ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVManaged( + static ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscVManaged( ArrayOfEmptiesFloatDouble a0_a1) { return a0_a1; @@ -2469,6 +2523,50 @@ static bool EchoEmpty8FloatRiscVWrapper() return ok; } + static bool EchoEmptyFloatEmpty5ByteRiscVWrapper() + { + bool ok = true; + EmptyFloatEmpty5Byte expected = EmptyFloatEmpty5Byte.Get(); + EmptyFloatEmpty5Byte native = EchoEmptyFloatEmpty5ByteRiscV(0, 0.0f, expected); + EmptyFloatEmpty5Byte managed = EchoEmptyFloatEmpty5ByteRiscVManaged(0, 0.0f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmptyFloatEmpty5ByteRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmptyFloatEmpty5ByteRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoEmptyFloatEmpty5UByteRiscVWrapper() + { + bool ok = true; + EmptyFloatEmpty5UByte expected = EmptyFloatEmpty5UByte.Get(); + EmptyFloatEmpty5UByte native = EchoEmptyFloatEmpty5UByteRiscV(0, 0.0f, expected); + EmptyFloatEmpty5UByte managed = EchoEmptyFloatEmpty5UByteRiscVManaged(0, 0.0f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmptyFloatEmpty5UByteRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmptyFloatEmpty5UByteRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoLongEmptyDoubleRiscVWrapper() { bool ok = true; @@ -2601,22 +2699,22 @@ static bool EchoLongEmptyAndFloatRiscVWrapper() return ok; } - static bool EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVWrapper() + static bool EchoArrayOfEmptiesFloatDoubleRiscVWrapper() { bool ok = true; ArrayOfEmptiesFloatDouble expected = ArrayOfEmptiesFloatDouble.Get(); - ArrayOfEmptiesFloatDouble native = EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscV(expected); - ArrayOfEmptiesFloatDouble managed = EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVManaged(expected); + ArrayOfEmptiesFloatDouble native = EchoArrayOfEmptiesFloatDoubleRiscV(expected); + ArrayOfEmptiesFloatDouble managed = EchoArrayOfEmptiesFloatDoubleRiscVManaged(expected); if (!expected.Equals(native)) { - Console.WriteLine("Native call for EchoArrayOfEmptiesFloatDoubleInIntegerRegs failed"); + Console.WriteLine("Native call for EchoArrayOfEmptiesFloatDouble failed"); ok = false; } if (!expected.Equals(managed)) { - Console.WriteLine("Managed call for EchoArrayOfEmptiesFloatDoubleInIntegerRegs failed"); + Console.WriteLine("Managed call for EchoArrayOfEmptiesFloatDouble failed"); ok = false; } @@ -2738,12 +2836,14 @@ public static int TestEntryPoint() if (!EnoughRegistersSysV3Wrapper()) ok = false; if (!EnoughRegistersSysV4Wrapper()) ok = false; if (!EchoEmpty8FloatRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5ByteRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5UByteRiscVWrapper()) ok = false; if (!EchoLongEmptyDoubleRiscVWrapper()) ok = false; if (!EchoLongEmptyDoubleByImplicitRefRiscVWrapper()) ok = false; if (!EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper()) ok = false; if (!EchoEmptyIntAndFloatRiscVWrapper()) ok = false; if (!EchoLongEmptyAndFloatRiscVWrapper()) ok = false; - if (!EchoArrayOfEmptiesFloatDoubleInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoArrayOfEmptiesFloatDoubleRiscVWrapper()) ok = false; if (!EchoFloatEmpty32kIntRiscVWrapper()) ok = false; if (!EchoPackedEmptyFloatLongRiscVWrapper()) ok = false; if (!EchoExplicitFloatLongRiscVWrapper()) ok = false; From e5d67cd1c78569909b5ed49bac73e87e06b29ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 29 May 2024 09:34:54 +0200 Subject: [PATCH 32/60] Increase field visibility to match other tests --- src/tests/JIT/Directed/StructABI/StructABI.cs | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index 79a56b82fcec75..b4f6ecd3c479eb 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -674,8 +674,8 @@ struct Empty struct Empty8Float { - Empty e0, e1, e2, e3, e4, e5, e6, e7; - float FieldF; + public Empty e0, e1, e2, e3, e4, e5, e6, e7; + public float FieldF; public static Empty8Float Get() { @@ -690,10 +690,10 @@ public bool Equals(Empty8Float other) struct EmptyFloatEmpty5Byte { - Empty e; - float FieldF; - Empty e0, e1, e2, e3, e4; - sbyte FieldB; + public Empty e; + public float FieldF; + public Empty e0, e1, e2, e3, e4; + public sbyte FieldB; public static EmptyFloatEmpty5Byte Get() { @@ -708,10 +708,10 @@ public bool Equals(EmptyFloatEmpty5Byte other) struct EmptyFloatEmpty5UByte { - Empty e; - float FieldF; - Empty e0, e1, e2, e3, e4; - byte FieldB; + public Empty e; + public float FieldF; + public Empty e0, e1, e2, e3, e4; + public byte FieldB; public static EmptyFloatEmpty5UByte Get() { @@ -726,9 +726,9 @@ public bool Equals(EmptyFloatEmpty5UByte other) struct LongEmptyDouble { - long FieldL; - Empty FieldE; - double FieldD; + public long FieldL; + public Empty FieldE; + public double FieldD; public static LongEmptyDouble Get() { @@ -743,18 +743,18 @@ public bool Equals(LongEmptyDouble other) struct NestedEmpty { - struct InnerEmpty + public struct InnerEmpty { - Empty e; + public Empty e; } - InnerEmpty e; + public InnerEmpty e; } struct NestedEmptyFloatDouble { - NestedEmpty FieldNE; - float FieldF; - double FieldD; + public NestedEmpty FieldNE; + public float FieldF; + public double FieldD; public static NestedEmptyFloatDouble Get() { @@ -769,13 +769,13 @@ public bool Equals(NestedEmptyFloatDouble other) struct EmptyIntAndFloat { - struct EmptyInt + public struct EmptyInt { public Empty FieldE; public int FieldI; } - EmptyInt FieldEI; - float FieldF; + public EmptyInt FieldEI; + public float FieldF; public static EmptyIntAndFloat Get() { @@ -790,13 +790,13 @@ public bool Equals(EmptyIntAndFloat other) struct LongEmptyAndFloat { - struct LongEmpty + public struct LongEmpty { public long FieldL; public Empty FieldE; } - LongEmpty FieldLE; - float FieldF; + public LongEmpty FieldLE; + public float FieldF; public static LongEmptyAndFloat Get() { @@ -812,14 +812,14 @@ public bool Equals(LongEmptyAndFloat other) [InlineArray(1)] struct ArrayOfEmpties { - Empty e; + public Empty e; }; struct ArrayOfEmptiesFloatDouble { - ArrayOfEmpties FieldAoE; - float FieldF; - double FieldD; + public ArrayOfEmpties FieldAoE; + public float FieldF; + public double FieldD; public static ArrayOfEmptiesFloatDouble Get() { @@ -834,14 +834,14 @@ public bool Equals(ArrayOfEmptiesFloatDouble other) struct Eight { - T e1, e2, e3, e4, e5, e6, e7, e8; + public T e1, e2, e3, e4, e5, e6, e7, e8; } struct FloatEmpty32kInt { - float FieldF; - Eight>>>> FieldEmpty32k; - int FieldI; + public float FieldF; + public Eight>>>> FieldEmpty32k; + public int FieldI; public static FloatEmpty32kInt Get() { @@ -857,9 +857,9 @@ public bool Equals(FloatEmpty32kInt other) [StructLayout(LayoutKind.Sequential, Pack=1)] struct PackedEmptyFloatLong { - Empty FieldE; - float FieldF; - long FieldL; + public Empty FieldE; + public float FieldF; + public long FieldL; public static PackedEmptyFloatLong Get() { @@ -875,8 +875,8 @@ public bool Equals(PackedEmptyFloatLong other) [StructLayout(LayoutKind.Explicit, Pack=1)] struct ExplicitFloatLong { - [FieldOffset(1)] float FieldF; - [FieldOffset(5)] long FieldL; + [FieldOffset(1)] public float FieldF; + [FieldOffset(5)] public long FieldL; public static ExplicitFloatLong Get() { From e25552e3581fc80bf100b849d7c249ecfeac932f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 31 May 2024 12:55:44 +0200 Subject: [PATCH 33/60] Fix EmptyFloatEmpty5(U)Byte and LongEmptyDouble tests by using correct field offsets and sizes for loads and stores --- src/coreclr/inc/corinfo.h | 16 +++++++--- src/coreclr/jit/codegencommon.cpp | 28 ++++++++++------ src/coreclr/jit/compiler.cpp | 53 +++++++++++++++++++++++++++++++ src/coreclr/jit/compiler.h | 4 +++ src/coreclr/jit/gentree.cpp | 40 +++-------------------- src/coreclr/jit/gentree.h | 8 +++++ src/coreclr/jit/morph.cpp | 46 ++++++++------------------- 7 files changed, 114 insertions(+), 81 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index a5ddefabcbfc25..863c3e1bef15c4 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -396,16 +396,24 @@ struct FpStructInRegistersInfo uint32_t offset1st; uint32_t offset2nd; + unsigned GetSizeShift1st() const + { + return (flags >> FpStruct::PosSizeShift1st) & 0b11; + } + + unsigned GetSizeShift2nd() const + { + return (flags >> FpStruct::PosSizeShift2nd) & 0b11; + } + unsigned GetSize1st() const { - unsigned shift = (flags >> FpStruct::PosSizeShift1st) & 0b11; - return 1u << shift; + return 1u << GetSizeShift1st(); } unsigned GetSize2nd() const { - unsigned shift = (flags >> FpStruct::PosSizeShift2nd) & 0b11; - return 1u << shift; + return 1u << GetSizeShift2nd(); } bool IsSize1st8() const diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index a4c4bb9b45e902..99853dc98dfb99 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -3140,6 +3140,10 @@ var_types CodeGen::genParamStackType(LclVarDsc* dsc, const ABIPassingSegment& se // can always use the full register size here. This allows us to // use stp more often. return TYP_I_IMPL; +#elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + // On RISC-V/LoongArch struct { struct{} e1,e2,e3; byte b; float f; } is passed in 2 registers so the + // load/store instruction for 'b' needs to be exact in size or it will overlap 'f'. + return seg.GetRegisterType(); #else return genActualType(seg.GetRegisterType()); #endif @@ -7319,16 +7323,17 @@ void CodeGen::genStructReturn(GenTree* treeNode) #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // On LoongArch64, for a struct like "{ int, double }", "retTypeDesc" will be "{ TYP_INT, TYP_DOUBLE }", // i. e. not include the padding for the first field, and so the general loop below won't work. - var_types type = retTypeDesc.GetReturnRegType(0); - regNumber toReg = retTypeDesc.GetABIReturnReg(0, compiler->info.compCallConv); - GetEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), toReg, lclNode->GetLclNum(), 0); + var_types type = retTypeDesc.GetReturnRegType(0); + regNumber toReg = retTypeDesc.GetABIReturnReg(0, compiler->info.compCallConv); + unsigned offset = retTypeDesc.GetReturnFieldOffset(0); + GetEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), toReg, lclNode->GetLclNum(), offset); if (regCount > 1) { assert(regCount == 2); - int offset = genTypeSize(type); - type = retTypeDesc.GetReturnRegType(1); - offset = (int)((unsigned int)offset < genTypeSize(type) ? genTypeSize(type) : offset); - toReg = retTypeDesc.GetABIReturnReg(1, compiler->info.compCallConv); + assert(offset + genTypeSize(type) <= retTypeDesc.GetReturnFieldOffset(1)); + type = retTypeDesc.GetReturnRegType(1); + toReg = retTypeDesc.GetABIReturnReg(1, compiler->info.compCallConv); + offset = retTypeDesc.GetReturnFieldOffset(1); GetEmitter()->emitIns_R_S(ins_Load(type), emitTypeSize(type), toReg, lclNode->GetLclNum(), offset); } #else // !TARGET_LOONGARCH64 && !TARGET_RISCV64 @@ -7603,6 +7608,11 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) assert(regCount == varDsc->lvFieldCnt); } +#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) + // genMultiRegStoreToLocal is only used for calls on RISC-V and LoongArch + const ReturnTypeDesc* returnTypeDesc = actualOp1->AsCall()->GetReturnTypeDesc(); +#endif + #ifdef SWIFT_SUPPORT const uint32_t* offsets = nullptr; if (actualOp1->IsCall() && (actualOp1->AsCall()->GetUnmanagedCallConv() == CorInfoCallConvExtension::Swift)) @@ -7653,8 +7663,8 @@ void CodeGen::genMultiRegStoreToLocal(GenTreeLclVar* lclNode) else { #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // should consider the padding field within a struct. - offset = (offset % genTypeSize(srcType)) ? AlignUp(offset, genTypeSize(srcType)) : offset; + // Should consider the padding, empty struct fields, etc within a struct. + offset = returnTypeDesc->GetReturnFieldOffset(i); #endif #ifdef SWIFT_SUPPORT if (offsets != nullptr) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 5071462ba7342e..f10f16cf467950 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8266,6 +8266,59 @@ void Compiler::GetStructTypeOffset( GetStructTypeOffset(structDesc, type0, type1, offset0, offset1); } +#elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) +//------------------------------------------------------------------------ +// GetTypesFromFpStructInRegistersInfo: Gets the field types of a struct passed in registers according to hardware +// floating-point calling convention. +// +// Arguments: +// info - the structure contaning information about how the struct is passed +// type1st - out param; type of the first field +// type2nd - out param; type of the second field (written to iff the struct has two fields) +// +// Return value: +// None +// +// Notes: +// If the struct should be passed according to integer calling convention, none of the output pararmeters are +// written to. +// +void Compiler::GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, var_types* type1st, var_types* type2nd) +{ + if ((info.flags & (FpStruct::OnlyOne | FpStruct::BothFloat | FpStruct::Float1st)) != 0) + *type1st = info.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; + + if ((info.flags & (FpStruct::BothFloat | FpStruct::Float2nd)) != 0) + *type2nd = info.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; + + if ((info.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) + { + bool isInt1st = ((info.flags & FpStruct::Float1st) == 0); + var_types intType; + if ((info.flags & FpStruct::GcRef) != 0) + { + intType = TYP_REF; + } + else if ((info.flags & FpStruct::GcByRef) != 0) + { + intType = TYP_BYREF; + } + else + { + static const var_types intTypesLookUpTable[] = { + /*[0] =*/TYP_BYTE, + /*[1] =*/TYP_SHORT, + /*[2] =*/TYP_INT, + /*[3] =*/TYP_LONG, + }; + unsigned sizeShift = isInt1st ? info.GetSizeShift1st() : info.GetSizeShift2nd(); + intType = intTypesLookUpTable[sizeShift]; + } + var_types* typePtr = isInt1st ? type1st : type2nd; + *typePtr = intType; + } +} + #endif // defined(UNIX_AMD64_ABI) /*****************************************************************************/ diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f9c66b63d15afc..dfefe18c76bda8 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -11229,6 +11229,10 @@ class Compiler void GetStructTypeOffset( CORINFO_CLASS_HANDLE typeHnd, var_types* type0, var_types* type1, uint8_t* offset0, uint8_t* offset1); +#elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + static void GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, + var_types* type1st, + var_types* type2nd); #endif // defined(UNIX_AMD64_ABI) void fgMorphMultiregStructArgs(GenTreeCall* call); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 7f72b38b1910e1..5c5e652ad02b76 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -27445,44 +27445,10 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, { comp->compFloatingPointUsed = true; + assert((fpInfo.flags & FpStruct::OnlyOne) == 0); m_fieldOffset[0] = fpInfo.offset1st; m_fieldOffset[1] = fpInfo.offset2nd; - - assert((fpInfo.flags & FpStruct::OnlyOne) == 0); - if ((fpInfo.flags & FpStruct::BothFloat) != 0) - { - m_regType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; - m_regType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; - } - else if ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) - { - var_types integerRegType; - if ((fpInfo.flags & FpStruct::GcRef) != 0) - { - integerRegType = TYP_REF; - } - else if ((fpInfo.flags & FpStruct::GcByRef) != 0) - { - integerRegType = TYP_BYREF; - } - else - { - bool is8 = - ((fpInfo.flags & FpStruct::Float1st) == 0) ? fpInfo.IsSize1st8() : fpInfo.IsSize2nd8(); - integerRegType = is8 ? TYP_LONG : TYP_INT; - } - - if ((fpInfo.flags & FpStruct::Float1st) != 0) - { - m_regType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; - m_regType[1] = integerRegType; - } - else - { - m_regType[0] = integerRegType; - m_regType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; - } - } + Compiler::GetTypesFromFpStructInRegistersInfo(fpInfo, &m_regType[0], &m_regType[1]); } else { @@ -27493,6 +27459,8 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, { m_regType[i] = comp->getJitGCType(gcPtrs[i]); } + m_fieldOffset[0] = 0; + m_fieldOffset[1] = TARGET_POINTER_SIZE; } #elif defined(TARGET_X86) diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 16cbf40213dfc5..f3850f7a8dd535 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -4406,6 +4406,14 @@ struct ReturnTypeDesc #endif } +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + unsigned GetReturnFieldOffset(unsigned index) const + { + assert(m_regType[index] != TYP_UNKNOWN); + return m_fieldOffset[index]; + } +#endif + // Get i'th ABI return register regNumber GetABIReturnReg(unsigned idx, CorInfoCallConvExtension callConv) const; diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 9b83f81b111ae8..bcd3ab0d2e1c88 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2873,37 +2873,20 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call intArgRegNum += size; } } - else if ((fpInfo.flags & FpStruct::OnlyOne) != 0) - { - structBaseType = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; - fltArgRegNum += 1; - arg.AbiInfo.StructFloatFieldType[0] = structBaseType; - arg.AbiInfo.StructFloatFieldOffset[0] = fpInfo.offset1st; - } - else if ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) - { - fltArgRegNum += 1; - intArgRegNum += 1; - arg.AbiInfo.StructFloatFieldOffset[0] = fpInfo.offset1st; - arg.AbiInfo.StructFloatFieldOffset[1] = fpInfo.offset2nd; - if ((fpInfo.flags & FpStruct::Float1st) != 0) - { - arg.AbiInfo.StructFloatFieldType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; - arg.AbiInfo.StructFloatFieldType[1] = fpInfo.IsSize2nd8() ? TYP_LONG : TYP_INT; - } - else - { - arg.AbiInfo.StructFloatFieldType[0] = fpInfo.IsSize1st8() ? TYP_LONG : TYP_INT; - arg.AbiInfo.StructFloatFieldType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; - } - } - else if ((fpInfo.flags & FpStruct::BothFloat) != 0) + else { - fltArgRegNum += 2; arg.AbiInfo.StructFloatFieldOffset[0] = fpInfo.offset1st; arg.AbiInfo.StructFloatFieldOffset[1] = fpInfo.offset2nd; - arg.AbiInfo.StructFloatFieldType[0] = fpInfo.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; - arg.AbiInfo.StructFloatFieldType[1] = fpInfo.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; + Compiler::GetTypesFromFpStructInRegistersInfo(fpInfo, &arg.AbiInfo.StructFloatFieldType[0], + &arg.AbiInfo.StructFloatFieldType[1]); + + if ((fpInfo.flags & FpStruct::OnlyOne) != 0) + structBaseType = arg.AbiInfo.StructFloatFieldType[0]; + + unsigned fltRegs = ((fpInfo.flags & FpStruct::BothFloat) != 0) ? 2 : 1; + unsigned intRegs = ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) ? 1 : 0; + fltArgRegNum += fltRegs; + intArgRegNum += intRegs; } } #else @@ -3652,11 +3635,10 @@ GenTree* Compiler::fgMorphMultiregStructArg(CallArg* arg) } else #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - if ((arg->AbiInfo.StructFloatFieldType[inx] != TYP_UNDEF) && - !varTypeIsGC(getSlotType(offset / TARGET_POINTER_SIZE))) + if (arg->AbiInfo.StructFloatFieldType[inx] != TYP_UNDEF) { - elems[inx].Type = arg->AbiInfo.StructFloatFieldType[inx]; - offset += (structSize > TARGET_POINTER_SIZE) ? 8 : 4; + elems[inx].Type = arg->AbiInfo.StructFloatFieldType[inx]; + elems[inx].Offset = arg->AbiInfo.StructFloatFieldOffset[inx]; } else #endif // TARGET_LOONGARCH64 || TARGET_RISCV64 From 28a88b352401f353a12ec186651a0ccb0c866c5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 4 Jun 2024 10:12:17 +0200 Subject: [PATCH 34/60] Fix LongEmptyGDoubleByImplicitRef and the rest of tests in StructABI by covering the corner-case in which struct is eligible for passing according to hardware floating-point calling convention but there's not enough registers and the struct is larger than 16 bytes --- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/ee_il_dll.cpp | 14 ++++-- src/coreclr/jit/lclvars.cpp | 92 ++++++++++++++--------------------- src/coreclr/jit/morph.cpp | 15 +++++- 4 files changed, 61 insertions(+), 62 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index f10f16cf467950..93eb243afe5c65 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8285,7 +8285,7 @@ void Compiler::GetStructTypeOffset( // void Compiler::GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, var_types* type1st, var_types* type2nd) { - if ((info.flags & (FpStruct::OnlyOne | FpStruct::BothFloat | FpStruct::Float1st)) != 0) + if ((info.flags & (FpStruct::BothFloat | FpStruct::Float1st | FpStruct::OnlyOne)) != 0) *type1st = info.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; if ((info.flags & (FpStruct::BothFloat | FpStruct::Float2nd)) != 0) diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 2f86c8b5274536..8bd738c10d2fee 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -425,11 +425,19 @@ unsigned Compiler::eeGetArgSize(CorInfoType corInfoType, CORINFO_CLASS_HANDLE ty } } #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - // Any structs that are larger than MAX_PASS_MULTIREG_BYTES are always passed by reference + // Any structs that are larger than MAX_PASS_MULTIREG_BYTES are always passed by reference... if (structSize > MAX_PASS_MULTIREG_BYTES) { - // This struct is passed by reference using a single 'slot' - return TARGET_POINTER_SIZE; +#ifdef TARGET_RISCV64 + // ... unless on RISC-V they are still eligible for passing according to hardware floating-point calling + // convention, e.g. struct {long; struct{}; double; }. + FpStructInRegistersInfo fpInfo = info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(typeHnd); + if (fpInfo.flags == FpStruct::UseIntCallConv) +#endif + { + // This struct is passed by reference using a single 'slot' + return TARGET_POINTER_SIZE; + } } // otherwise will we pass this struct by value in multiple registers #elif !defined(TARGET_ARM) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 4922dcd28d0d03..832bf996b0edf4 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -897,82 +897,63 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un } else #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - uint32_t floatFlags = STRUCT_NO_FLOAT_FIELD; + FpStructInRegistersInfo fpInfo = {}; + var_types argRegTypeInStruct1 = TYP_UNKNOWN; var_types argRegTypeInStruct2 = TYP_UNKNOWN; if ((strip(corInfoType) == CORINFO_TYPE_VALUECLASS) LOONGARCH64_ONLY(&&(argSize <= MAX_PASS_MULTIREG_BYTES))) { #if defined(TARGET_LOONGARCH64) - floatFlags = info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd); + uint32_t floatFlagFields = info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd); + fpInfo = FpStructInRegistersInfo::FromOldFlags((StructFloatFieldInfoFlags)floatFieldFlags); #else - floatFlags = info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(typeHnd).ToOldFlags(); + fpInfo = info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(typeHnd); #endif } - if ((floatFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) != 0) + if (fpInfo.flags != FpStruct::UseIntCallConv) { assert(varTypeIsStruct(argType)); - int floatNum = 0; - if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) - { - assert(varDsc->lvExactSize() <= argSize); - - cSlotsToEnregister = 1; - floatNum = 1; - canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1); - - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - } - else if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0) - { - cSlotsToEnregister = 2; - floatNum = 2; - canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 2); - - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - } - else if ((floatFlags & STRUCT_FLOAT_FIELD_FIRST) != 0) - { - cSlotsToEnregister = 2; - floatNum = 1; - canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1); - canPassArgInRegisters = canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1); - - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT; - } - else if ((floatFlags & STRUCT_FLOAT_FIELD_SECOND) != 0) - { - cSlotsToEnregister = 2; - floatNum = 1; - canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1); - canPassArgInRegisters = canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1); + assert(((fpInfo.flags & FpStruct::OnlyOne) != 0) || varDsc->lvExactSize() <= argSize); + cSlotsToEnregister = ((fpInfo.flags & FpStruct::OnlyOne) != 0) ? 1 : 2; - argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT; - argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT; - } + int floatNum = ((fpInfo.flags & FpStruct::BothFloat) != 0) ? 2 : 1; + canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, floatNum); + if (canPassArgInRegisters && ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0)) + canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, 1); - assert((floatNum == 1) || (floatNum == 2)); + Compiler::GetTypesFromFpStructInRegistersInfo(fpInfo, &argRegTypeInStruct1, &argRegTypeInStruct2); if (!canPassArgInRegisters) { - // On LoongArch64, if there aren't any remaining floating-point registers to pass the argument, - // integer registers (if any) are used instead. - canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister); - argRegTypeInStruct1 = TYP_UNKNOWN; argRegTypeInStruct2 = TYP_UNKNOWN; - if (cSlotsToEnregister == 2) +#ifdef TARGET_RISCV64 + if (argSize > MAX_PASS_MULTIREG_BYTES) + { + // According to integer calling convention, structs > 16 bytes are passed by implicit reference. + cSlots = 1; + cSlotsToEnregister = 1; + varDsc->lvIsImplicitByRef = 1; // lvaSetStruct doesn't know about register availability so set here + canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, 1); + } + else +#endif // TARGET_RISCV64 { - if (!canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1)) + // On LoongArch64 and RISC-V64, if there aren't any remaining floating-point registers to pass the + // argument, integer registers (if any) are used instead. + canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister); + if (cSlotsToEnregister == 2) { - // Here a struct-arg which needs two registers but only one integer register available, - // it has to be split. - argRegTypeInStruct1 = TYP_I_IMPL; - canPassArgInRegisters = true; + if (!canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1)) + { + // Here a struct-arg which needs two registers but only one integer register available, + // it has to be split. + argRegTypeInStruct1 = TYP_I_IMPL; + canPassArgInRegisters = true; + } } } } @@ -1282,7 +1263,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un } printf("\n"); } -#endif // DEBUG +#endif // DEBUG } // end if (canPassArgInRegisters) else { @@ -3394,7 +3375,6 @@ void Compiler::lvaSetStruct(unsigned varNum, ClassLayout* layout, bool unsafeVal structPassingKind howToReturnStruct; getArgTypeForStruct(layout->GetClassHandle(), &howToReturnStruct, info.compIsVarArgs, varDsc->lvExactSize()); - if (howToReturnStruct == SPK_ByReference) { JITDUMP("Marking V%02i as a byref parameter\n", varNum); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index bcd3ab0d2e1c88..236957f1c371f6 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2443,6 +2443,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call #endif if (fpInfo.flags != FpStruct::UseIntCallConv) { + passUsingFloatRegs = comp->compFloatingPointUsed = true; if ((fpInfo.flags & FpStruct::OnlyOne) == 0) { @@ -2651,7 +2652,17 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call // if we run out of floating-point argument registers, try the int argument registers. if (!isRegArg) { - // Check if the last register needed is still in the int argument register range. +#ifdef TARGET_RISCV64 + if (structSize > MAX_PASS_MULTIREG_BYTES) + { + // According to integer calling convention, structs larger than 16 bytes are passed by implicit ref + size = 1; + byteSize = TARGET_POINTER_SIZE; + passUsingFloatRegs = false; + passStructByRef = true; + } +#endif // TARGET_RISCV64 + // Check if the last register needed is still in the int argument register range. isRegArg = (intArgRegNum + (size - 1)) < maxRegArgs; if (!passUsingFloatRegs && isRegArg && (size > 1)) { @@ -2670,7 +2681,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call nextOtherRegNum = REG_STK; } } -#else // not TARGET_ARM or TARGET_ARM64 or TARGET_LOONGARCH64 or TARGET_RISCV64 +#else // not TARGET_ARM or TARGET_ARM64 or TARGET_LOONGARCH64 or TARGET_RISCV64 #if defined(UNIX_AMD64_ABI) From 123eaaf3e423790ae1780b7f4aaddd1b193787ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 4 Jun 2024 11:50:20 +0200 Subject: [PATCH 35/60] Make tests harder On RISC-V the return value is passed like the first argument, so native compilers optimize out the passing and the test did not catch if the struct was passed incorrectly because it will be checking whatever intact registers were set on the call-site. Besides, it's easier to see how the native compiler is passing arguments by disassembling the binary. --- .../JIT/Directed/StructABI/StructABI.cpp | 44 +++--- src/tests/JIT/Directed/StructABI/StructABI.cs | 125 +++++++++--------- 2 files changed, 82 insertions(+), 87 deletions(-) diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cpp b/src/tests/JIT/Directed/StructABI/StructABI.cpp index d0ce6466d3a096..e7fbc9d0a495d5 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cpp +++ b/src/tests/JIT/Directed/StructABI/StructABI.cpp @@ -126,9 +126,9 @@ static_assert(offsetof(ExplicitFloatLong, s.FieldL) == 5, ""); extern "C" { -DLLEXPORT Empty8Float EchoEmpty8FloatRiscV(Empty8Float fa0) +DLLEXPORT Empty8Float EchoEmpty8FloatRiscV(int a0, float fa0, Empty8Float fa1) { - return fa0; + return fa1; } DLLEXPORT EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscV(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1) @@ -141,56 +141,56 @@ DLLEXPORT EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscV(int a0, float fa0 return fa1_a1; } -DLLEXPORT LongEmptyDouble EchoLongEmptyDoubleRiscV(LongEmptyDouble a0_fa0) +DLLEXPORT LongEmptyDouble EchoLongEmptyDoubleRiscV(int a0, float fa0, LongEmptyDouble a1_fa1) { - return a0_fa0; + return a1_fa1; } DLLEXPORT LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscV( - float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble byRef) + int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble a1) { - return byRef; + return a1; } -DLLEXPORT NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscV(NestedEmptyFloatDouble fa0_fa1) +DLLEXPORT NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscV(int a0, float fa0, NestedEmptyFloatDouble fa1_fa2) { - return fa0_fa1; + return fa1_fa2; } DLLEXPORT NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscV( - float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a0_a1) + int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a1_a2) { - return a0_a1; + return a1_a2; } -DLLEXPORT EmptyIntAndFloat EchoEmptyIntAndFloatRiscV(EmptyIntAndFloat a0_fa0) +DLLEXPORT EmptyIntAndFloat EchoEmptyIntAndFloatRiscV(int a0, float fa0, EmptyIntAndFloat a1_fa1) { - return a0_fa0; + return a1_fa1; } -DLLEXPORT LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(LongEmptyAndFloat a0_fa0) +DLLEXPORT LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(int a0, float fa0, LongEmptyAndFloat a1_fa1) { - return a0_fa0; + return a1_fa1; } -DLLEXPORT ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscV(ArrayOfEmptiesFloatDouble a0_a1) +DLLEXPORT ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscV(int a0, float fa0, ArrayOfEmptiesFloatDouble a1_a2) { - return a0_a1; + return a1_a2; } -DLLEXPORT FloatEmpty32kInt EchoFloatEmpty32kIntRiscV(FloatEmpty32kInt fa0_a0) +DLLEXPORT FloatEmpty32kInt EchoFloatEmpty32kIntRiscV(int a0, float fa0, FloatEmpty32kInt fa1_a1) { - return fa0_a0; + return fa1_a1; } -DLLEXPORT PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(PackedEmptyFloatLong fa0_a0) +DLLEXPORT PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(int a0, float fa0, PackedEmptyFloatLong fa1_a1) { - return fa0_a0; + return fa1_a1; } -DLLEXPORT ExplicitFloatLong EchoExplicitFloatLongRiscV(ExplicitFloatLong fa0_a0) +DLLEXPORT ExplicitFloatLong EchoExplicitFloatLongRiscV(int a0, float fa0, ExplicitFloatLong fa1_a1) { - return fa0_a0; + return fa1_a1; } } // extern "C" diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index b4f6ecd3c479eb..7d9fb26001a9b5 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -758,7 +758,7 @@ struct NestedEmptyFloatDouble public static NestedEmptyFloatDouble Get() { - return new NestedEmptyFloatDouble { FieldF = 3.14159f, FieldD = 3.14159 }; + return new NestedEmptyFloatDouble { FieldF = 3.14159f, FieldD = 3.14159d }; } public bool Equals(NestedEmptyFloatDouble other) @@ -1022,7 +1022,7 @@ public static partial class StructABI static extern DoubleAndByte EnoughRegistersSysV4(double a, double b, double c, double d, double e, double f, double g, DoubleAndByte value); [DllImport("StructABILib")] - static extern Empty8Float EchoEmpty8FloatRiscV(Empty8Float fa0); + static extern Empty8Float EchoEmpty8FloatRiscV(int a0, float fa0, Empty8Float fa1); [DllImport("StructABILib")] static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscV(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1); @@ -1031,38 +1031,36 @@ public static partial class StructABI static extern EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscV(int a0, float fa0, EmptyFloatEmpty5UByte fa1_a1); [DllImport("StructABILib")] - static extern LongEmptyDouble EchoLongEmptyDoubleRiscV(LongEmptyDouble value); + static extern LongEmptyDouble EchoLongEmptyDoubleRiscV(int a0, float fa0, LongEmptyDouble a1_fa1); [DllImport("StructABILib")] static extern LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscV( - float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble byRef); + int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble a1); [DllImport("StructABILib")] - static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscV(NestedEmptyFloatDouble fa0_fa1); + static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscV(int a0, float fa0, NestedEmptyFloatDouble fa1_fa2); [DllImport("StructABILib")] static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscV( - float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a0_a1); + int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a1_a2); [DllImport("StructABILib")] - static extern EmptyIntAndFloat EchoEmptyIntAndFloatRiscV(EmptyIntAndFloat a0_fa0); + static extern EmptyIntAndFloat EchoEmptyIntAndFloatRiscV(int a0, float fa0, EmptyIntAndFloat a1_fa1); [DllImport("StructABILib")] - static extern LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(LongEmptyAndFloat a0_fa0); + static extern LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(int a0, float fa0, LongEmptyAndFloat a1_fa1); [DllImport("StructABILib")] - static extern ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscV( - ArrayOfEmptiesFloatDouble a0_a1); + static extern ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscV(int a0, float fa0, ArrayOfEmptiesFloatDouble a1_a2); [DllImport("StructABILib")] - static extern FloatEmpty32kInt EchoFloatEmpty32kIntRiscV( - FloatEmpty32kInt fa0_a0); + static extern FloatEmpty32kInt EchoFloatEmpty32kIntRiscV(int a0, float fa0, FloatEmpty32kInt fa1_a1); [DllImport("StructABILib")] - static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(PackedEmptyFloatLong fa0_a0); + static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(int a0, float fa0, PackedEmptyFloatLong fa1_a1); [DllImport("StructABILib")] - static extern ExplicitFloatLong EchoExplicitFloatLongRiscV(ExplicitFloatLong fa0_a0); + static extern ExplicitFloatLong EchoExplicitFloatLongRiscV(int a0, float fa0, ExplicitFloatLong fa1_a1); //////////////////////////////////////////////////////////////////////////// // Managed echo tests. @@ -1328,9 +1326,9 @@ static DoubleAndByte EnoughRegistersSysV4Managed(double a, double b, double c, d } [MethodImpl(MethodImplOptions.NoInlining)] - static Empty8Float EchoEmpty8FloatRiscVManaged(Empty8Float fa0) + static Empty8Float EchoEmpty8FloatRiscVManaged(int a0, float fa0, Empty8Float fa1) { - return fa0; + return fa1; } [MethodImpl(MethodImplOptions.NoInlining)] @@ -1346,67 +1344,65 @@ static EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscVManaged(int a0, float } [MethodImpl(MethodImplOptions.NoInlining)] - static LongEmptyDouble EchoLongEmptyDoubleRiscVManaged(LongEmptyDouble a0_fa0) + static LongEmptyDouble EchoLongEmptyDoubleRiscVManaged(int a0, float fa0, LongEmptyDouble a1_fa1) { - return a0_fa0; + return a1_fa1; } [MethodImpl(MethodImplOptions.NoInlining)] static LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscVManaged( - float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble byRef) + int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble a1) { - return byRef; + return a1; } [MethodImpl(MethodImplOptions.NoInlining)] - static NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscVManaged(NestedEmptyFloatDouble fa0_fa1) + static NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscVManaged(int a0, float fa0, NestedEmptyFloatDouble fa1_fa2) { - return fa0_fa1; + return fa1_fa2; } [MethodImpl(MethodImplOptions.NoInlining)] static NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscVManaged( - float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a0_a1) + int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a1_a2) { - return a0_a1; + return a1_a2; } [MethodImpl(MethodImplOptions.NoInlining)] - static EmptyIntAndFloat EchoEmptyIntAndFloatRiscVManaged(EmptyIntAndFloat a0_fa0) + static EmptyIntAndFloat EchoEmptyIntAndFloatRiscVManaged(int a0, float fa0, EmptyIntAndFloat a1_fa1) { - return a0_fa0; + return a1_fa1; } [MethodImpl(MethodImplOptions.NoInlining)] - static LongEmptyAndFloat EchoLongEmptyAndFloatRiscVManaged(LongEmptyAndFloat a0_fa0) + static LongEmptyAndFloat EchoLongEmptyAndFloatRiscVManaged(int a0, float fa0, LongEmptyAndFloat a1_fa1) { - return a0_fa0; + return a1_fa1; } [MethodImpl(MethodImplOptions.NoInlining)] - static ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscVManaged( - ArrayOfEmptiesFloatDouble a0_a1) + static ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscVManaged(int a0, float fa0, ArrayOfEmptiesFloatDouble a1_a2) { - return a0_a1; + return a1_a2; } [MethodImpl(MethodImplOptions.NoInlining)] - static FloatEmpty32kInt EchoFloatEmpty32kIntRiscVManaged( - FloatEmpty32kInt fa0_a0) + static FloatEmpty32kInt EchoFloatEmpty32kIntRiscVManaged(int a0, float fa0, FloatEmpty32kInt fa1_a1) { - return fa0_a0; + return fa1_a1; } [MethodImpl(MethodImplOptions.NoInlining)] - static PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscVManaged(PackedEmptyFloatLong fa0_a0) + static PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscVManaged(int a0, float fa0, PackedEmptyFloatLong fa1_a1) { - return fa0_a0; + return fa1_a1; } [MethodImpl(MethodImplOptions.NoInlining)] - static ExplicitFloatLong EchoExplicitFloatLongRiscVManaged(ExplicitFloatLong fa0_a0) + static ExplicitFloatLong EchoExplicitFloatLongRiscVManaged(int a0, float fa0, ExplicitFloatLong fa1_a1) { - return fa0_a0; + return fa1_a1; } //////////////////////////////////////////////////////////////////////////// @@ -2505,8 +2501,8 @@ static bool EchoEmpty8FloatRiscVWrapper() { bool ok = true; Empty8Float expected = Empty8Float.Get(); - Empty8Float native = EchoEmpty8FloatRiscV(expected); - Empty8Float managed = EchoEmpty8FloatRiscVManaged(expected); + Empty8Float native = EchoEmpty8FloatRiscV(0, 0f, expected); + Empty8Float managed = EchoEmpty8FloatRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2527,8 +2523,8 @@ static bool EchoEmptyFloatEmpty5ByteRiscVWrapper() { bool ok = true; EmptyFloatEmpty5Byte expected = EmptyFloatEmpty5Byte.Get(); - EmptyFloatEmpty5Byte native = EchoEmptyFloatEmpty5ByteRiscV(0, 0.0f, expected); - EmptyFloatEmpty5Byte managed = EchoEmptyFloatEmpty5ByteRiscVManaged(0, 0.0f, expected); + EmptyFloatEmpty5Byte native = EchoEmptyFloatEmpty5ByteRiscV(0, 0f, expected); + EmptyFloatEmpty5Byte managed = EchoEmptyFloatEmpty5ByteRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2549,8 +2545,8 @@ static bool EchoEmptyFloatEmpty5UByteRiscVWrapper() { bool ok = true; EmptyFloatEmpty5UByte expected = EmptyFloatEmpty5UByte.Get(); - EmptyFloatEmpty5UByte native = EchoEmptyFloatEmpty5UByteRiscV(0, 0.0f, expected); - EmptyFloatEmpty5UByte managed = EchoEmptyFloatEmpty5UByteRiscVManaged(0, 0.0f, expected); + EmptyFloatEmpty5UByte native = EchoEmptyFloatEmpty5UByteRiscV(0, 0f, expected); + EmptyFloatEmpty5UByte managed = EchoEmptyFloatEmpty5UByteRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2571,8 +2567,8 @@ static bool EchoLongEmptyDoubleRiscVWrapper() { bool ok = true; LongEmptyDouble expected = LongEmptyDouble.Get(); - LongEmptyDouble native = EchoLongEmptyDoubleRiscV(expected); - LongEmptyDouble managed = EchoLongEmptyDoubleRiscVManaged(expected); + LongEmptyDouble native = EchoLongEmptyDoubleRiscV(0, 0f, expected); + LongEmptyDouble managed = EchoLongEmptyDoubleRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2593,9 +2589,8 @@ static bool EchoLongEmptyDoubleByImplicitRefRiscVWrapper() { bool ok = true; LongEmptyDouble expected = LongEmptyDouble.Get(); - LongEmptyDouble native = EchoLongEmptyDoubleByImplicitRefRiscV(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); - LongEmptyDouble managed = EchoLongEmptyDoubleByImplicitRefRiscVManaged(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); - + LongEmptyDouble native = EchoLongEmptyDoubleByImplicitRefRiscV(0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + LongEmptyDouble managed = EchoLongEmptyDoubleByImplicitRefRiscVManaged(0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); if (!expected.Equals(native)) { Console.WriteLine("Native call for EchoLongEmptyDoubleByImplicitRefRiscV failed"); @@ -2615,8 +2610,8 @@ static bool EchoNestedEmptyFloatDoubleRiscVWrapper() { bool ok = true; NestedEmptyFloatDouble expected = NestedEmptyFloatDouble.Get(); - NestedEmptyFloatDouble native = EchoNestedEmptyFloatDoubleRiscV(expected); - NestedEmptyFloatDouble managed = EchoNestedEmptyFloatDoubleRiscVManaged(expected); + NestedEmptyFloatDouble native = EchoNestedEmptyFloatDoubleRiscV(0, 0f, expected); + NestedEmptyFloatDouble managed = EchoNestedEmptyFloatDoubleRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2637,8 +2632,8 @@ static bool EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper() { bool ok = true; NestedEmptyFloatDouble expected = NestedEmptyFloatDouble.Get(); - NestedEmptyFloatDouble native = EchoNestedEmptyFloatDoubleInIntegerRegsRiscV(0f, 1f, 2f, 3f, 4f, 5f, 6f, expected); - NestedEmptyFloatDouble managed = EchoNestedEmptyFloatDoubleInIntegerRegsRiscVManaged(0f, 1f, 2f, 3f, 4f, 5f, 6f, expected); + NestedEmptyFloatDouble native = EchoNestedEmptyFloatDoubleInIntegerRegsRiscV(0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, expected); + NestedEmptyFloatDouble managed = EchoNestedEmptyFloatDoubleInIntegerRegsRiscVManaged(0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, expected); if (!expected.Equals(native)) { @@ -2659,8 +2654,8 @@ static bool EchoEmptyIntAndFloatRiscVWrapper() { bool ok = true; EmptyIntAndFloat expected = EmptyIntAndFloat.Get(); - EmptyIntAndFloat native = EchoEmptyIntAndFloatRiscV(expected); - EmptyIntAndFloat managed = EchoEmptyIntAndFloatRiscVManaged(expected); + EmptyIntAndFloat native = EchoEmptyIntAndFloatRiscV(0, 0f, expected); + EmptyIntAndFloat managed = EchoEmptyIntAndFloatRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2681,8 +2676,8 @@ static bool EchoLongEmptyAndFloatRiscVWrapper() { bool ok = true; LongEmptyAndFloat expected = LongEmptyAndFloat.Get(); - LongEmptyAndFloat native = EchoLongEmptyAndFloatRiscV(expected); - LongEmptyAndFloat managed = EchoLongEmptyAndFloatRiscVManaged(expected); + LongEmptyAndFloat native = EchoLongEmptyAndFloatRiscV(0, 0f, expected); + LongEmptyAndFloat managed = EchoLongEmptyAndFloatRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2703,8 +2698,8 @@ static bool EchoArrayOfEmptiesFloatDoubleRiscVWrapper() { bool ok = true; ArrayOfEmptiesFloatDouble expected = ArrayOfEmptiesFloatDouble.Get(); - ArrayOfEmptiesFloatDouble native = EchoArrayOfEmptiesFloatDoubleRiscV(expected); - ArrayOfEmptiesFloatDouble managed = EchoArrayOfEmptiesFloatDoubleRiscVManaged(expected); + ArrayOfEmptiesFloatDouble native = EchoArrayOfEmptiesFloatDoubleRiscV(0, 0f, expected); + ArrayOfEmptiesFloatDouble managed = EchoArrayOfEmptiesFloatDoubleRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2725,8 +2720,8 @@ static bool EchoFloatEmpty32kIntRiscVWrapper() { bool ok = true; FloatEmpty32kInt expected = FloatEmpty32kInt.Get(); - FloatEmpty32kInt native = EchoFloatEmpty32kIntRiscV(expected); - FloatEmpty32kInt managed = EchoFloatEmpty32kIntRiscVManaged(expected); + FloatEmpty32kInt native = EchoFloatEmpty32kIntRiscV(0, 0f, expected); + FloatEmpty32kInt managed = EchoFloatEmpty32kIntRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2747,8 +2742,8 @@ static bool EchoPackedEmptyFloatLongRiscVWrapper() { bool ok = true; PackedEmptyFloatLong expected = PackedEmptyFloatLong.Get(); - PackedEmptyFloatLong native = EchoPackedEmptyFloatLongRiscV(expected); - PackedEmptyFloatLong managed = EchoPackedEmptyFloatLongRiscVManaged(expected); + PackedEmptyFloatLong native = EchoPackedEmptyFloatLongRiscV(0, 0f, expected); + PackedEmptyFloatLong managed = EchoPackedEmptyFloatLongRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { @@ -2769,8 +2764,8 @@ static bool EchoExplicitFloatLongRiscVWrapper() { bool ok = true; ExplicitFloatLong expected = ExplicitFloatLong.Get(); - ExplicitFloatLong native = EchoExplicitFloatLongRiscV(expected); - ExplicitFloatLong managed = EchoExplicitFloatLongRiscVManaged(expected); + ExplicitFloatLong native = EchoExplicitFloatLongRiscV(0, 0f, expected); + ExplicitFloatLong managed = EchoExplicitFloatLongRiscVManaged(0, 0f, expected); if (!expected.Equals(native)) { From 1a85e0b688bbb54713c32b3f11386336a6623a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 5 Jun 2024 08:49:37 +0200 Subject: [PATCH 36/60] Improve logging for GetRiscV64PassFpStructInRegistersInfo, similar to System V eightbyte classification --- .../RISCV64PassStructInRegister.cs | 21 ++- .../superpmi-shared/methodcontext.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 125 +++++++++++------- 3 files changed, 90 insertions(+), 58 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index 3ae3a6077fdae2..7bff9b974a9f79 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -148,17 +148,17 @@ private static bool HandleInlineArray(int elementTypeIndex, int nElements, ref F { int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; if (nFlattenedFieldsPerElement == 0) - return true; + return true; // ignoring empty array element Debug.Assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2); if (nElements > 2) - return false; + return false; // array has too many elements if (nElements == 2) { if (typeIndex + nFlattenedFieldsPerElement > 2) - return false; + return false; // array has too many fields per element Debug.Assert(elementTypeIndex == 0); Debug.Assert(typeIndex == 1); @@ -184,7 +184,7 @@ private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegist nFields++; if (prevField != null && prevField.Offset.AsInt + prevField.FieldType.GetElementSize().AsInt > field.Offset.AsInt) - return false; // overlapping fields + return false; // fields overlap, treat as union prevField = field; @@ -198,7 +198,7 @@ private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegist else if (field.FieldType.GetElementSize().AsInt <= TARGET_POINTER_SIZE) { if (typeIndex >= 2) - return false; + return false; // too many fields SetFpStructInRegistersInfoField(ref info, typeIndex++, (category is TypeFlags.Single or TypeFlags.Double), @@ -209,7 +209,7 @@ private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegist } else { - return false; + return false; // field is too big } } @@ -230,20 +230,17 @@ private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl FpStructInRegistersInfo info = new FpStructInRegistersInfo{}; int nFields = 0; if (!FlattenFields(td, 0, ref info, ref nFields)) - { return new FpStructInRegistersInfo{}; - } if ((info.flags & (Float1st | Float2nd)) == 0) - { - return new FpStructInRegistersInfo{}; - } + return new FpStructInRegistersInfo{}; // struct has no floating fields + Debug.Assert(nFields == 1 || nFields == 2); if ((info.flags & (Float1st | Float2nd)) == (Float1st | Float2nd)) { Debug.Assert(nFields == 2); - info.flags ^= (Float1st | Float2nd | BothFloat); // replace (1st|2nd)Float with BothFloat + info.flags ^= (Float1st | Float2nd | BothFloat); // replace Float(1st|2nd) with BothFloat } else if (nFields == 1) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 722ee5b9ab22f4..833bcfc6bfd69f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6327,7 +6327,7 @@ void MethodContext::recGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDL void MethodContext::dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpStructInRegistersInfo value) { printf("GetRiscV64PassFpStructInRegistersInfo key %016" PRIX64 " value-%#02x-" - "{OnlyOne=%i, BothFloat=%i, Float1st=%i, Size1st=%u, Float2nd=%i, Size2nd=%u, GcRef=%i, GcByRef=%i offset1st=%u, offset2nd=%u}", + "{OnlyOne=%i, BothFloat=%i, Float1st=%i, Size1st=%u, Float2nd=%i, Size2nd=%u, GcRef=%i, GcByRef=%i, offset1st=%u, offset2nd=%u}", key, value.flags, (value.flags & FpStruct::OnlyOne) != 0, (value.flags & FpStruct::BothFloat) != 0, diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 7380a47d4cae30..6d8dee8df78dab 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3038,21 +3038,34 @@ static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int i (index == 0 ? info.offset1st : info.offset2nd) = offset; } -static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInRegistersInfo& info, int& typeIndex DEBUG_ARG(const char* fieldNames[2])) +static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInRegistersInfo& info, int& typeIndex + DEBUG_ARG(int nestingLevel)) { int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; if (nFlattenedFieldsPerElement == 0) + { + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * ignoring empty array element\n", + nestingLevel * 4, "")); return true; + } assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2); if (nElements > 2) + { + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * array has too many elements: %i\n", + nestingLevel * 4, "", nElements)); return false; + } if (nElements == 2) { if (typeIndex + nFlattenedFieldsPerElement > 2) + { + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * array has too many fields per element: %i, fields already found: %i\n", + nestingLevel * 4, "", nFlattenedFieldsPerElement, typeIndex)); return false; + } assert(elementTypeIndex == 0); assert(typeIndex == 1); @@ -3061,17 +3074,22 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg static const int typeSize = FpStruct::PosFloat2nd - FpStruct::PosFloat1st; info.flags = FpStruct::Flags(info.flags | (info.flags << typeSize)); info.offset2nd = info.offset1st + info.GetSize1st(); - INDEBUG(fieldNames[1] = fieldNames[0];) + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * duplicated array element type\n", + nestingLevel * 4, "")); } return true; } -static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInfo& info, int& typeIndex DEBUG_ARG(const char* fieldNames[2])) +static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInfo& info, int& typeIndex + DEBUG_ARG(int nestingLevel)) { bool isManaged = !th.IsTypeDesc(); MethodTable* pMT = isManaged ? th.AsMethodTable() : th.AsNativeValueType(); int nFields = isManaged ? pMT->GetNumIntroducedInstanceFields() : pMT->GetNativeLayoutInfo()->GetNumFields(); + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s flattening %s (%s, %i fields)\n", + nestingLevel * 4, "", pMT->GetDebugClassName(), (isManaged ? "managed" : "native"), nFields)); + // TODO: templatize isManaged and use if constexpr for differences when we migrate to C++17 // because the logic for both branches is nearly the same. if (isManaged) @@ -3081,31 +3099,48 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf for (int i = 0; i < nFields; ++i) { if (i > 0 && fields[i-1].GetOffset() + fields[i-1].GetSize() > fields[i].GetOffset()) - return false; // overlapping fields, treat as union + { + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s " + " * fields %s [%i..%i) and %s [%i..%i) overlap, treat as union\n", + nestingLevel * 4, "", + fields[i-1].GetDebugName(), fields[i-1].GetOffset(), fields[i-1].GetOffset() + fields[i-1].GetSize(), + fields[i].GetDebugName(), fields[i].GetOffset(), fields[i].GetOffset() + fields[i].GetSize())); + return false; + } CorElementType type = fields[i].GetFieldType(); if (type == ELEMENT_TYPE_VALUETYPE) { MethodTable* nested = fields[i].GetApproxFieldTypeHandleThrowing().GetMethodTable(); - if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetOffset(), info, typeIndex DEBUG_ARG(fieldNames))) + if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetOffset(), info, typeIndex DEBUG_ARG(nestingLevel + 1))) return false; } else if (fields[i].GetSize() <= TARGET_POINTER_SIZE) { if (typeIndex >= 2) + { + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * too many fields\n", + nestingLevel * 4, "")); return false; + } CorInfoGCType gcType = CorTypeInfo::GetGCType_NoThrow(type); - INDEBUG(fields[i].GetName_NoThrow(&fieldNames[typeIndex]);) SetFpStructInRegistersInfoField(info, typeIndex++, CorTypeInfo::IsFloat_NoThrow(type), (gcType == TYPE_GC_REF), (gcType == TYPE_GC_BYREF), CorTypeInfo::Size_NoThrow(type), offset + fields[i].GetOffset()); + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", + nestingLevel * 4, "", fields[i].GetDebugName(), + fields[i].GetOffset(), fields[i].GetOffset() + fields[i].GetSize(), CorTypeInfo::GetName(type))); } else { + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s " + " * field %s, type: %s, is too big (%i bytes)\n", + nestingLevel * 4, "", fields[i].GetDebugName(), + CorTypeInfo::GetName(type), fields[i].GetSize())); return false; } } @@ -3114,7 +3149,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf { assert(nFields == 1); int nElements = pMT->GetNumInstanceFieldBytes() / fields[0].GetSize(); - if (!HandleInlineArray(elementTypeIndex, nElements, info, typeIndex DEBUG_ARG(fieldNames))) + if (!HandleInlineArray(elementTypeIndex, nElements, info, typeIndex DEBUG_ARG(nestingLevel + 1))) return false; } } @@ -3124,37 +3159,55 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf for (int i = 0; i < nFields; ++i) { if (i > 0 && fields[i-1].GetExternalOffset() + fields[i-1].NativeSize() > fields[i].GetExternalOffset()) - return false; // overlapping fields, treat as union + { + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s " + " * fields %s [%i..%i) and %s [%i..%i) overlap, treat as union\n", + nestingLevel * 4, "", + fields[i-1].GetFieldDesc()->GetDebugName(), fields[i-1].GetExternalOffset(), fields[i-1].GetExternalOffset() + fields[i-1].NativeSize(), + fields[i].GetFieldDesc()->GetDebugName(), fields[i].GetExternalOffset(), fields[i].GetExternalOffset() + fields[i].NativeSize())); + return false; + } + static const char* categoryNames[] = {"FLOAT", "NESTED", "INTEGER", "ILLEGAL"}; NativeFieldCategory category = fields[i].GetCategory(); if (category == NativeFieldCategory::NESTED) { int elementTypeIndex = typeIndex; MethodTable* nested = fields[i].GetNestedNativeMethodTable(); - if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetExternalOffset(), info, typeIndex DEBUG_ARG(fieldNames))) + if (!FlattenFields(TypeHandle(nested), offset + fields[i].GetExternalOffset(), info, typeIndex DEBUG_ARG(nestingLevel + 1))) return false; // In native layout fixed arrays are marked as NESTED just like structs int nElements = fields[i].GetNumElements(); - if (!HandleInlineArray(elementTypeIndex, nElements, info, typeIndex DEBUG_ARG(fieldNames))) + if (!HandleInlineArray(elementTypeIndex, nElements, info, typeIndex DEBUG_ARG(nestingLevel + 1))) return false; } else if (fields[i].NativeSize() <= TARGET_POINTER_SIZE) { if (typeIndex >= 2) + { + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * too many fields\n", + nestingLevel * 4, "")); return false; + } - INDEBUG(fields[i].GetFieldDesc()->GetName_NoThrow(&fieldNames[typeIndex]);) SetFpStructInRegistersInfoField(info, typeIndex++, (category == NativeFieldCategory::FLOAT), false, false, fields[i].NativeSize(), offset + fields[i].GetExternalOffset()); + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", + nestingLevel * 4, "", fields[i].GetFieldDesc()->GetDebugName(), + fields[i].GetExternalOffset(), fields[i].GetExternalOffset() + fields[i].NativeSize(), categoryNames[(int)category])); } else { + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s " + " * field %s, type: %s, is too big (%i bytes)\n", + nestingLevel * 4, "", fields[i].GetFieldDesc()->GetDebugName(), + categoryNames[(int)category], fields[i].NativeSize())); return false; } } @@ -3167,27 +3220,16 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHandle th) { - const char* fieldNames[2] = {}, *name = nullptr; - if (LoggingOn(LF_JIT, LL_EVERYTHING)) - { - MethodTable* pMT = !th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType(); - name = pMT->GetDebugClassName(); - } - FpStructInRegistersInfo info = {}; int nFields = 0; - if (!FlattenFields(th, 0, info, nFields DEBUG_ARG(fieldNames))) - { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " - "Struct %s (%u bytes) cannot be passed with floating-point calling convention\n", name, th.GetSize())); + if (!FlattenFields(th, 0, info, nFields DEBUG_ARG(0))) return FpStructInRegistersInfo{}; - } using namespace FpStruct; if ((info.flags & (Float1st | Float2nd)) == 0) { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " - "Struct %s (%u bytes) does not have any floating fields\n", name, th.GetSize())); + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: struct %s (%u bytes) has no floating fields\n", + (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize())); return FpStructInRegistersInfo{}; } @@ -3196,7 +3238,7 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan if ((info.flags & (Float1st | Float2nd)) == (Float1st | Float2nd)) { assert(nFields == 2); - info.flags = FpStruct::Flags(info.flags ^ (Float1st | Float2nd | BothFloat)); // replace (1st|2nd)Float with BothFloat + info.flags = FpStruct::Flags(info.flags ^ (Float1st | Float2nd | BothFloat)); // replace Float(1st|2nd) with BothFloat } else if (nFields == 1) { @@ -3225,25 +3267,18 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan assert((info.flags & Float2nd) || (info.IsSize2nd8() && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); } - if (LoggingOn(LF_JIT, LL_EVERYTHING)) - { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " - "Struct %s (%u bytes) can be passed with floating-point calling convention, flags: %#02x, %i fields:\n", - name, th.GetSize(), info.flags, nFields)); - const char* integerType = - (info.flags & GcRef) ? "GC ref" : - (info.flags & GcByRef) ? "GC byRef" : - "integer"; - const char* type1st = (info.flags & (Float1st | OnlyOne | BothFloat)) ? "floating" : integerType; - LOG((LF_JIT, LL_EVERYTHING, "\t1st field %s: %s, %u bytes at offset %u\n", - fieldNames[0], type1st, info.GetSize1st(), info.offset1st)); - if (nFields == 2) - { - const char* type2nd = (info.flags & (Float2nd | BothFloat)) ? "floating" : integerType; - LOG((LF_JIT, LL_EVERYTHING, "\t2nd field %s: %s, %u bytes at offset %u\n", - fieldNames[1], type2nd, info.GetSize2nd(), info.offset2nd)); - } - } + LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " + "struct %s (%u bytes) can be passed with floating-point calling convention, flags=%#02x, " + "%s, sizes={%u, %u}, GcRef=%i, GcByRef=%i, offsets={%u, %u}\n", + (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize(), info.flags, + ( (info.flags & FpStruct::OnlyOne) ? "OnlyFloat" + : (info.flags & FpStruct::BothFloat) ? "BothFloat" + : (info.flags & FpStruct::Float1st) ? "Float1st" + : "Float2nd" ), + info.GetSize1st(), info.GetSize2nd(), + (info.flags & FpStruct::GcRef), + (info.flags & FpStruct::GcByRef), + info.offset1st, info.offset2nd)); return info; } From 6163536852d97704d9932428025099f077c946eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 5 Jun 2024 09:03:08 +0200 Subject: [PATCH 37/60] Format fix --- src/coreclr/jit/lclvars.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 832bf996b0edf4..cd7e587c82aa47 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1263,7 +1263,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un } printf("\n"); } -#endif // DEBUG +#endif // DEBUG } // end if (canPassArgInRegisters) else { From 6dc499d7f693a710ec5132fad260beebec24d496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 5 Jun 2024 12:42:36 +0200 Subject: [PATCH 38/60] Fix test EchoArrayOfEmptiesFloatDouble by returning FpStruct{ UseIntCallConv } if a struct contains an InlineArray of empty structs --- .../RISCV64PassStructInRegister.cs | 21 ++++++- src/coreclr/vm/methodtable.cpp | 55 +++++++++++++------ 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index 7bff9b974a9f79..73d021d3ed9881 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -81,6 +81,14 @@ private static bool FlattenFieldTypesOld(TypeDesc td, Span 0, "InlineArray length must be > 0"); + return false; // struct containing an array of empty structs is passed by integer calling convention + } + if (!HandleInlineArrayOld(elementTypeIndex, nElements, types, ref typeIndex)) return false; } @@ -148,7 +156,10 @@ private static bool HandleInlineArray(int elementTypeIndex, int nElements, ref F { int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; if (nFlattenedFieldsPerElement == 0) - return true; // ignoring empty array element + { + Debug.Assert(nElements == 1, "HasImpliedRepeatedFields must have returned a false positive"); + return true; // ignoring empty struct + } Debug.Assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2); @@ -217,6 +228,14 @@ private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegist { Debug.Assert(nFields == 1); int nElements = td.GetElementSize().AsInt / prevField.FieldType.GetElementSize().AsInt; + + // Only InlineArrays can have an element type of an empty struct, fixed-size buffers take only primitives + if ((typeIndex - elementTypeIndex) == 0 && (td as MetadataType).IsInlineArray) + { + Debug.Assert(nElements > 0, "InlineArray length must be > 0"); + return false; // struct containing an array of empty structs is passed by integer calling convention + } + if (!HandleInlineArray(elementTypeIndex, nElements, ref info, ref typeIndex)) return false; } diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 6d8dee8df78dab..60e384d0d42b1d 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2833,7 +2833,10 @@ static bool HandleInlineArrayOld(int elementTypeIndex, int nElements, StructFloa { int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; if (nFlattenedFieldsPerElement == 0) - return true; + { + assert(nElements == 1); // HasImpliedRepeatedFields must have returned a false positive + return true; // ignoring empty struct + } assert(nFlattenedFieldsPerElement == 1 || nFlattenedFieldsPerElement == 2); @@ -2896,6 +2899,14 @@ static bool FlattenFieldTypesOld(TypeHandle th, StructFloatFieldInfoFlags types[ { assert(nFields == 1); int nElements = pMT->GetNumInstanceFieldBytes() / fields[0].GetSize(); + + // Only InlineArrays can have an element type of an empty struct, fixed-size buffers take only primitives + if ((typeIndex - elementTypeIndex) == 0 && pMT->GetClass()->IsInlineArray()) + { + assert(nElements > 0); + return false; + } + if (!HandleInlineArrayOld(elementTypeIndex, nElements, types, typeIndex)) return false; } @@ -3044,7 +3055,8 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg int nFlattenedFieldsPerElement = typeIndex - elementTypeIndex; if (nFlattenedFieldsPerElement == 0) { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * ignoring empty array element\n", + assert(nElements == 1); // HasImpliedRepeatedFields must have returned a false positive + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * ignoring empty struct\n", nestingLevel * 4, "")); return true; } @@ -3053,7 +3065,7 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg if (nElements > 2) { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * array has too many elements: %i\n", + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * array has too many elements: %i\n", nestingLevel * 4, "", nElements)); return false; } @@ -3062,7 +3074,7 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg { if (typeIndex + nFlattenedFieldsPerElement > 2) { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * array has too many fields per element: %i, fields already found: %i\n", + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * array has too many fields per element: %i, fields already found: %i\n", nestingLevel * 4, "", nFlattenedFieldsPerElement, typeIndex)); return false; } @@ -3074,7 +3086,7 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg static const int typeSize = FpStruct::PosFloat2nd - FpStruct::PosFloat1st; info.flags = FpStruct::Flags(info.flags | (info.flags << typeSize)); info.offset2nd = info.offset1st + info.GetSize1st(); - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * duplicated array element type\n", + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * duplicated array element type\n", nestingLevel * 4, "")); } return true; @@ -3087,7 +3099,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf MethodTable* pMT = isManaged ? th.AsMethodTable() : th.AsNativeValueType(); int nFields = isManaged ? pMT->GetNumIntroducedInstanceFields() : pMT->GetNativeLayoutInfo()->GetNumFields(); - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s flattening %s (%s, %i fields)\n", + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s flattening %s (%s, %i fields)\n", nestingLevel * 4, "", pMT->GetDebugClassName(), (isManaged ? "managed" : "native"), nFields)); // TODO: templatize isManaged and use if constexpr for differences when we migrate to C++17 @@ -3100,7 +3112,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf { if (i > 0 && fields[i-1].GetOffset() + fields[i-1].GetSize() > fields[i].GetOffset()) { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s " + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s " " * fields %s [%i..%i) and %s [%i..%i) overlap, treat as union\n", nestingLevel * 4, "", fields[i-1].GetDebugName(), fields[i-1].GetOffset(), fields[i-1].GetOffset() + fields[i-1].GetSize(), @@ -3119,7 +3131,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf { if (typeIndex >= 2) { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * too many fields\n", + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * too many fields\n", nestingLevel * 4, "")); return false; } @@ -3131,13 +3143,13 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf (gcType == TYPE_GC_BYREF), CorTypeInfo::Size_NoThrow(type), offset + fields[i].GetOffset()); - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", nestingLevel * 4, "", fields[i].GetDebugName(), fields[i].GetOffset(), fields[i].GetOffset() + fields[i].GetSize(), CorTypeInfo::GetName(type))); } else { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s " + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s " " * field %s, type: %s, is too big (%i bytes)\n", nestingLevel * 4, "", fields[i].GetDebugName(), CorTypeInfo::GetName(type), fields[i].GetSize())); @@ -3149,6 +3161,17 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf { assert(nFields == 1); int nElements = pMT->GetNumInstanceFieldBytes() / fields[0].GetSize(); + + // Only InlineArrays can have an element type of an empty struct, fixed-size buffers take only primitives + if ((typeIndex - elementTypeIndex) == 0 && pMT->GetClass()->IsInlineArray()) + { + assert(nElements > 0); // InlineArray length must be > 0 + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s " + " * struct %s containing a %i-element array of empty structs %s is passed by integer calling convention\n", + nestingLevel * 4, "", pMT->GetDebugClassName(), nElements, fields[0].GetDebugName())); + return false; + } + if (!HandleInlineArray(elementTypeIndex, nElements, info, typeIndex DEBUG_ARG(nestingLevel + 1))) return false; } @@ -3160,7 +3183,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf { if (i > 0 && fields[i-1].GetExternalOffset() + fields[i-1].NativeSize() > fields[i].GetExternalOffset()) { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s " + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s " " * fields %s [%i..%i) and %s [%i..%i) overlap, treat as union\n", nestingLevel * 4, "", fields[i-1].GetFieldDesc()->GetDebugName(), fields[i-1].GetExternalOffset(), fields[i-1].GetExternalOffset() + fields[i-1].NativeSize(), @@ -3187,7 +3210,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf { if (typeIndex >= 2) { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * too many fields\n", + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * too many fields\n", nestingLevel * 4, "")); return false; } @@ -3198,13 +3221,13 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf false, fields[i].NativeSize(), offset + fields[i].GetExternalOffset()); - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", nestingLevel * 4, "", fields[i].GetFieldDesc()->GetDebugName(), fields[i].GetExternalOffset(), fields[i].GetExternalOffset() + fields[i].NativeSize(), categoryNames[(int)category])); } else { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo:%*s " + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s " " * field %s, type: %s, is too big (%i bytes)\n", nestingLevel * 4, "", fields[i].GetFieldDesc()->GetDebugName(), categoryNames[(int)category], fields[i].NativeSize())); @@ -3228,7 +3251,7 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan using namespace FpStruct; if ((info.flags & (Float1st | Float2nd)) == 0) { - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: struct %s (%u bytes) has no floating fields\n", + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: struct %s (%u bytes) has no floating fields\n", (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize())); return FpStructInRegistersInfo{}; } @@ -3267,7 +3290,7 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan assert((info.flags & Float2nd) || (info.IsSize2nd8() && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); } - LOG((LF_JIT, LL_EVERYTHING, "GetRiscV64PassFpStructInRegistersInfo: " + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: " "struct %s (%u bytes) can be passed with floating-point calling convention, flags=%#02x, " "%s, sizes={%u, %u}, GcRef=%i, GcByRef=%i, offsets={%u, %u}\n", (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize(), info.flags, From afd95cdb04c1cfaf96b9b0195419c7eecc810023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 6 Jun 2024 11:18:31 +0200 Subject: [PATCH 39/60] Fix false positive marking a struct split between register and stack --- src/coreclr/jit/lclvars.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index cd7e587c82aa47..ac4c814cad5bf6 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -944,8 +944,8 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un { // On LoongArch64 and RISC-V64, if there aren't any remaining floating-point registers to pass the // argument, integer registers (if any) are used instead. - canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister); - if (cSlotsToEnregister == 2) + canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlots); + if (cSlots == 2) { if (!canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1)) { @@ -961,13 +961,13 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un else #endif // defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) { - canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister); + canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlots); #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // On LoongArch64 and RISCV64, if there aren't any remaining floating-point registers to pass the // argument, integer registers (if any) are used instead. if (!canPassArgInRegisters && varTypeIsFloating(argType)) { - canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, cSlotsToEnregister); + canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, cSlots); argType = canPassArgInRegisters ? TYP_I_IMPL : argType; } if (!canPassArgInRegisters && (cSlots > 1)) From de83bc7004393f2193769fa1736f7bd931b19d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 6 Jun 2024 13:33:05 +0200 Subject: [PATCH 40/60] Add more tests for cases where structs eligible for hardware floating-point convention but are passed according to integer calling convention due to floating register shortage --- .../JIT/Directed/StructABI/StructABI.cpp | 63 ++++ src/tests/JIT/Directed/StructABI/StructABI.cs | 342 ++++++++++++++++++ 2 files changed, 405 insertions(+) diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cpp b/src/tests/JIT/Directed/StructABI/StructABI.cpp index e7fbc9d0a495d5..755ba1e9b59192 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cpp +++ b/src/tests/JIT/Directed/StructABI/StructABI.cpp @@ -131,6 +131,27 @@ DLLEXPORT Empty8Float EchoEmpty8FloatRiscV(int a0, float fa0, Empty8Float fa1) return fa1; } +DLLEXPORT Empty8Float EchoEmpty8FloatInIntegerRegsRiscV( + int a0, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a1_a2) +{ + return a1_a2; +} + +DLLEXPORT Empty8Float EchoEmpty8FloatSplitRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a7_stack0) +{ + return a7_stack0; +} + +DLLEXPORT Empty8Float EchoEmpty8FloatOnStackRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float stack0_stack1) +{ + return stack0_stack1; +} + DLLEXPORT EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscV(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1) { return fa1_a1; @@ -141,6 +162,27 @@ DLLEXPORT EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscV(int a0, float fa0 return fa1_a1; } +DLLEXPORT EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV( + int a0, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a1_a2) +{ + return a1_a2; +} + +DLLEXPORT EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteSplitRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a7_stack0) +{ + return a7_stack0; +} + +DLLEXPORT EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteOnStackRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte stack0_stack1) +{ + return stack0_stack1; +} + DLLEXPORT LongEmptyDouble EchoLongEmptyDoubleRiscV(int a0, float fa0, LongEmptyDouble a1_fa1) { return a1_fa1; @@ -188,6 +230,27 @@ DLLEXPORT PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(int a0, float fa0, return fa1_a1; } +DLLEXPORT PackedEmptyFloatLong EchoPackedEmptyFloatLongInIntegerRegsRiscV( + int a0, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a1_a2) +{ + return a1_a2; +} + +DLLEXPORT PackedEmptyFloatLong EchoPackedEmptyFloatLongSplitRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a7_stack0) +{ + return a7_stack0; +} + +DLLEXPORT PackedEmptyFloatLong EchoPackedEmptyFloatLongOnStackRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong stack0_stack1) +{ + return stack0_stack1; +} + DLLEXPORT ExplicitFloatLong EchoExplicitFloatLongRiscV(int a0, float fa0, ExplicitFloatLong fa1_a1) { return fa1_a1; diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index 7d9fb26001a9b5..5ccd8c2de12754 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -1024,12 +1024,42 @@ public static partial class StructABI [DllImport("StructABILib")] static extern Empty8Float EchoEmpty8FloatRiscV(int a0, float fa0, Empty8Float fa1); + [DllImport("StructABILib")] + static extern Empty8Float EchoEmpty8FloatInIntegerRegsRiscV( + int a0, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a1_a2); + + [DllImport("StructABILib")] + static extern Empty8Float EchoEmpty8FloatSplitRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a7_stack0); + + [DllImport("StructABILib")] + static extern Empty8Float EchoEmpty8FloatOnStackRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float stack0_stack1); + [DllImport("StructABILib")] static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscV(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1); [DllImport("StructABILib")] static extern EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscV(int a0, float fa0, EmptyFloatEmpty5UByte fa1_a1); + [DllImport("StructABILib")] + static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV( + int a0, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a1_a2); + + [DllImport("StructABILib")] + static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteSplitRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a7_stack0); + + [DllImport("StructABILib")] + static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteOnStackRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte stack0_stack1); + [DllImport("StructABILib")] static extern LongEmptyDouble EchoLongEmptyDoubleRiscV(int a0, float fa0, LongEmptyDouble a1_fa1); @@ -1059,6 +1089,21 @@ static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRisc [DllImport("StructABILib")] static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(int a0, float fa0, PackedEmptyFloatLong fa1_a1); + [DllImport("StructABILib")] + static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongInIntegerRegsRiscV( + int a0, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a1_a2); + + [DllImport("StructABILib")] + static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongSplitRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a7_stack0); + + [DllImport("StructABILib")] + static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongOnStackRiscV( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong stack0_stack1); + [DllImport("StructABILib")] static extern ExplicitFloatLong EchoExplicitFloatLongRiscV(int a0, float fa0, ExplicitFloatLong fa1_a1); @@ -1331,6 +1376,30 @@ static Empty8Float EchoEmpty8FloatRiscVManaged(int a0, float fa0, Empty8Float fa return fa1; } + [MethodImpl(MethodImplOptions.NoInlining)] + static Empty8Float EchoEmpty8FloatInIntegerRegsRiscVManaged( + int a0, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a1_a2) + { + return a1_a2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static Empty8Float EchoEmpty8FloatSplitRiscVManaged( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a7_stack0) + { + return a7_stack0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static Empty8Float EchoEmpty8FloatOnStackRiscVManaged( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float stack0_stack1) + { + return stack0_stack1; + } + [MethodImpl(MethodImplOptions.NoInlining)] static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscVManaged(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1) { @@ -1343,6 +1412,30 @@ static EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscVManaged(int a0, float return fa1_a1; } + [MethodImpl(MethodImplOptions.NoInlining)] + static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVManaged( + int a0, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a1_a2) + { + return a1_a2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteSplitRiscVManaged( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a7_stack0) + { + return a7_stack0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteOnStackRiscVManaged( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte stack0_stack1) + { + return stack0_stack1; + } + [MethodImpl(MethodImplOptions.NoInlining)] static LongEmptyDouble EchoLongEmptyDoubleRiscVManaged(int a0, float fa0, LongEmptyDouble a1_fa1) { @@ -1399,6 +1492,30 @@ static PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscVManaged(int a0, float f return fa1_a1; } + [MethodImpl(MethodImplOptions.NoInlining)] + static PackedEmptyFloatLong EchoPackedEmptyFloatLongInIntegerRegsRiscVManaged( + int a0, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a1_a2) + { + return a1_a2; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static PackedEmptyFloatLong EchoPackedEmptyFloatLongSplitRiscVManaged( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a7_stack0) + { + return a7_stack0; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static PackedEmptyFloatLong EchoPackedEmptyFloatLongOnStackRiscVManaged( + int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, + float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong stack0_stack1) + { + return stack0_stack1; + } + [MethodImpl(MethodImplOptions.NoInlining)] static ExplicitFloatLong EchoExplicitFloatLongRiscVManaged(int a0, float fa0, ExplicitFloatLong fa1_a1) { @@ -2519,6 +2636,78 @@ static bool EchoEmpty8FloatRiscVWrapper() return ok; } + static bool EchoEmpty8FloatInIntegerRegsRiscVWrapper() + { + bool ok = true; + Empty8Float expected = Empty8Float.Get(); + Empty8Float native = EchoEmpty8FloatInIntegerRegsRiscV( + 0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + Empty8Float managed = EchoEmpty8FloatInIntegerRegsRiscVManaged( + 0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmpty8FloatInIntegerRegsRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmpty8FloatInIntegerRegsRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoEmpty8FloatSplitRiscVWrapper() + { + bool ok = true; + Empty8Float expected = Empty8Float.Get(); + Empty8Float native = EchoEmpty8FloatSplitRiscV( + 0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + Empty8Float managed = EchoEmpty8FloatSplitRiscVManaged( + 0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmpty8FloatSplitRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmpty8FloatSplitRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoEmpty8FloatOnStackRiscVWrapper() + { + bool ok = true; + Empty8Float expected = Empty8Float.Get(); + Empty8Float native = EchoEmpty8FloatOnStackRiscV( + 0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + Empty8Float managed = EchoEmpty8FloatOnStackRiscVManaged( + 0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmpty8FloatOnStackRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmpty8FloatOnStackRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmptyFloatEmpty5ByteRiscVWrapper() { bool ok = true; @@ -2563,6 +2752,78 @@ static bool EchoEmptyFloatEmpty5UByteRiscVWrapper() return ok; } + static bool EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVWrapper() + { + bool ok = true; + EmptyFloatEmpty5Byte expected = EmptyFloatEmpty5Byte.Get(); + EmptyFloatEmpty5Byte native = EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV( + 0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + EmptyFloatEmpty5Byte managed = EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVManaged( + 0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoEmptyFloatEmpty5ByteSplitRiscVWrapper() + { + bool ok = true; + EmptyFloatEmpty5Byte expected = EmptyFloatEmpty5Byte.Get(); + EmptyFloatEmpty5Byte native = EchoEmptyFloatEmpty5ByteSplitRiscV( + 0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + EmptyFloatEmpty5Byte managed = EchoEmptyFloatEmpty5ByteSplitRiscVManaged( + 0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmptyFloatEmpty5ByteSplitRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmptyFloatEmpty5ByteSplitRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoEmptyFloatEmpty5ByteOnStackRiscVWrapper() + { + bool ok = true; + EmptyFloatEmpty5Byte expected = EmptyFloatEmpty5Byte.Get(); + EmptyFloatEmpty5Byte native = EchoEmptyFloatEmpty5ByteOnStackRiscV( + 0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + EmptyFloatEmpty5Byte managed = EchoEmptyFloatEmpty5ByteOnStackRiscVManaged( + 0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoEmptyFloatEmpty5ByteOnStackRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoEmptyFloatEmpty5ByteOnStackRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoLongEmptyDoubleRiscVWrapper() { bool ok = true; @@ -2760,6 +3021,78 @@ static bool EchoPackedEmptyFloatLongRiscVWrapper() return ok; } + static bool EchoPackedEmptyFloatLongInIntegerRegsRiscVWrapper() + { + bool ok = true; + PackedEmptyFloatLong expected = PackedEmptyFloatLong.Get(); + PackedEmptyFloatLong native = EchoPackedEmptyFloatLongInIntegerRegsRiscV( + 0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + PackedEmptyFloatLong managed = EchoPackedEmptyFloatLongInIntegerRegsRiscVManaged( + 0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoPackedEmptyFloatLongInIntegerRegsRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoPackedEmptyFloatLongInIntegerRegsRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoPackedEmptyFloatLongSplitRiscVWrapper() + { + bool ok = true; + PackedEmptyFloatLong expected = PackedEmptyFloatLong.Get(); + PackedEmptyFloatLong native = EchoPackedEmptyFloatLongSplitRiscV( + 0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + PackedEmptyFloatLong managed = EchoPackedEmptyFloatLongSplitRiscVManaged( + 0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoPackedEmptyFloatLongSplitRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoPackedEmptyFloatLongSplitRiscV failed"); + ok = false; + } + + return ok; + } + + static bool EchoPackedEmptyFloatLongOnStackRiscVWrapper() + { + bool ok = true; + PackedEmptyFloatLong expected = PackedEmptyFloatLong.Get(); + PackedEmptyFloatLong native = EchoPackedEmptyFloatLongOnStackRiscV( + 0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + PackedEmptyFloatLong managed = EchoPackedEmptyFloatLongOnStackRiscVManaged( + 0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call for EchoPackedEmptyFloatLongOnStackRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call for EchoPackedEmptyFloatLongOnStackRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoExplicitFloatLongRiscVWrapper() { bool ok = true; @@ -2831,8 +3164,14 @@ public static int TestEntryPoint() if (!EnoughRegistersSysV3Wrapper()) ok = false; if (!EnoughRegistersSysV4Wrapper()) ok = false; if (!EchoEmpty8FloatRiscVWrapper()) ok = false; + if (!EchoEmpty8FloatInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoEmpty8FloatSplitRiscVWrapper()) ok = false; + if (!EchoEmpty8FloatOnStackRiscVWrapper()) ok = false; if (!EchoEmptyFloatEmpty5ByteRiscVWrapper()) ok = false; if (!EchoEmptyFloatEmpty5UByteRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5ByteSplitRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5ByteOnStackRiscVWrapper()) ok = false; if (!EchoLongEmptyDoubleRiscVWrapper()) ok = false; if (!EchoLongEmptyDoubleByImplicitRefRiscVWrapper()) ok = false; if (!EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper()) ok = false; @@ -2841,6 +3180,9 @@ public static int TestEntryPoint() if (!EchoArrayOfEmptiesFloatDoubleRiscVWrapper()) ok = false; if (!EchoFloatEmpty32kIntRiscVWrapper()) ok = false; if (!EchoPackedEmptyFloatLongRiscVWrapper()) ok = false; + if (!EchoPackedEmptyFloatLongInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoPackedEmptyFloatLongSplitRiscVWrapper()) ok = false; + if (!EchoPackedEmptyFloatLongOnStackRiscVWrapper()) ok = false; if (!EchoExplicitFloatLongRiscVWrapper()) ok = false; return ok ? 100 : -1; From 978e85147a3f99b2196dcb67d1e0eedb40c4a233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 6 Jun 2024 13:34:02 +0200 Subject: [PATCH 41/60] Fix test EchoEmptyFloatEmpty5ByteSplitRiscV --- src/coreclr/jit/lclvars.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index ac4c814cad5bf6..e35572bab19190 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -944,6 +944,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un { // On LoongArch64 and RISC-V64, if there aren't any remaining floating-point registers to pass the // argument, integer registers (if any) are used instead. + cSlotsToEnregister = cSlots; canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlots); if (cSlots == 2) { From dab9167bcc534370c609c4bf215e8463213b19fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 6 Jun 2024 13:41:21 +0200 Subject: [PATCH 42/60] Remove LclVarDsc::lvIs4Field{1,2} because they were write-only --- src/coreclr/jit/compiler.h | 14 +++----------- src/coreclr/jit/lclvars.cpp | 6 +----- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index dfefe18c76bda8..e7f59bf6953c9c 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -555,17 +555,9 @@ class LclVarDsc unsigned char lvIsLastUseCopyOmissionCandidate : 1; #endif // FEATURE_IMPLICIT_BYREFS -#if defined(TARGET_LOONGARCH64) - unsigned char lvIs4Field1 : 1; // Set if the 1st field is int or float within struct for LA-ABI64. - unsigned char lvIs4Field2 : 1; // Set if the 2nd field is int or float within struct for LA-ABI64. - unsigned char lvIsSplit : 1; // Set if the argument is splited. -#endif // defined(TARGET_LOONGARCH64) - -#if defined(TARGET_RISCV64) - unsigned char lvIs4Field1 : 1; // Set if the 1st field is int or float within struct for RISCV64. - unsigned char lvIs4Field2 : 1; // Set if the 2nd field is int or float within struct for RISCV64. - unsigned char lvIsSplit : 1; // Set if the argument is splited. -#endif // defined(TARGET_RISCV64) +#if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + unsigned char lvIsSplit : 1; // Set if the argument is split across last integer register and stack. +#endif // defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) unsigned char lvSingleDef : 1; // variable has a single def. Used to identify ref type locals that can get type // updates diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index e35572bab19190..e8807948d019df 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1075,13 +1075,11 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un { varDsc->SetArgReg( genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argRegTypeInStruct1, info.compCallConv)); - varDsc->lvIs4Field1 = (genTypeSize(argRegTypeInStruct1) == 4) ? 1 : 0; if (argRegTypeInStruct2 != TYP_UNKNOWN) { secondAllocatedRegArgNum = varDscInfo->allocRegArg(argRegTypeInStruct2, 1); varDsc->SetOtherArgReg( genMapRegArgNumToRegNum(secondAllocatedRegArgNum, argRegTypeInStruct2, info.compCallConv)); - varDsc->lvIs4Field2 = (genTypeSize(argRegTypeInStruct2) == 4) ? 1 : 0; } else if (cSlotsToEnregister > 1) { @@ -1631,9 +1629,7 @@ void Compiler::lvaInitVarDsc(LclVarDsc* varDsc, varDsc->lvIsImplicitByRef = 0; #endif // FEATURE_IMPLICIT_BYREFS #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - varDsc->lvIs4Field1 = 0; - varDsc->lvIs4Field2 = 0; - varDsc->lvIsSplit = 0; + varDsc->lvIsSplit = 0; #endif // TARGET_LOONGARCH64 || TARGET_RISCV64 // Set the lvType (before this point it is TYP_UNDEF). From 2d24bee65423b947077cc255663815cf8ac25bc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 6 Jun 2024 16:09:04 +0200 Subject: [PATCH 43/60] Fix ArgIteratorTemplate::GetNextOffset() for struct{Arr[], float} --- .../DependencyAnalysis/ReadyToRun/ArgIterator.cs | 6 +++++- src/coreclr/vm/callingconvention.h | 9 ++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index eee92e23a86824..fd3fb9f4626f94 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -1512,7 +1512,11 @@ public int GetNextOffset() _hasArgLocDescForStructInRegs = true; _argLocDescForStructInRegs.m_floatFlags = floatFieldFlags; - int argOfsInner = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8; + int argOfsInner = + ((floatFieldFlags & (uint)StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND) != 0) + ? _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * 8 + : _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * 8; + _riscv64IdxFPReg++; _riscv64IdxGenReg++; return argOfsInner; diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 7ec2525d48ef2c..a663a038b80ebf 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -308,6 +308,11 @@ struct TransitionBlock { return argLocDescForStructInRegs->m_cFloatReg > 0; } + #elif defined(TARGET_RISCV64) + if (argLocDescForStructInRegs != NULL) + { + return argLocDescForStructInRegs->m_cFloatReg > 0; + } #endif return offset < 0; } @@ -1836,7 +1841,9 @@ int ArgIteratorTemplate::GetNextOffset() m_argLocDescForStructInRegs.Init(); m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; m_argLocDescForStructInRegs.m_cFloatReg = 1; - int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; + int argOfs = (flags & STRUCT_FLOAT_FIELD_SECOND) + ? TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * 8 + : TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * 8; m_idxFPReg += 1; m_argLocDescForStructInRegs.m_structFields = flags; From f469ef5899cb4bb29b32272c3c0e26f89749ffe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 10 Jun 2024 08:47:31 +0200 Subject: [PATCH 44/60] Fix arm32 build by reverting to using cSlotsToEnregister in lclvars. --- src/coreclr/jit/lclvars.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index e8807948d019df..ff5a13abb6dea1 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -945,7 +945,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un // On LoongArch64 and RISC-V64, if there aren't any remaining floating-point registers to pass the // argument, integer registers (if any) are used instead. cSlotsToEnregister = cSlots; - canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlots); + canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister); if (cSlots == 2) { if (!canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1)) @@ -962,13 +962,13 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un else #endif // defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) { - canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlots); + canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister); #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) // On LoongArch64 and RISCV64, if there aren't any remaining floating-point registers to pass the // argument, integer registers (if any) are used instead. if (!canPassArgInRegisters && varTypeIsFloating(argType)) { - canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, cSlots); + canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, cSlotsToEnregister); argType = canPassArgInRegisters ? TYP_I_IMPL : argType; } if (!canPassArgInRegisters && (cSlots > 1)) From 811f94756b278a8302813ddcd709e8417e7cfe2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 10 Jun 2024 14:22:56 +0200 Subject: [PATCH 45/60] Add failing tests for hardware floating-point calling convention by reflection --- src/tests/JIT/Directed/StructABI/StructABI.cs | 667 ++++++++++++++++-- 1 file changed, 608 insertions(+), 59 deletions(-) diff --git a/src/tests/JIT/Directed/StructABI/StructABI.cs b/src/tests/JIT/Directed/StructABI/StructABI.cs index 5ccd8c2de12754..033800d4384308 100644 --- a/src/tests/JIT/Directed/StructABI/StructABI.cs +++ b/src/tests/JIT/Directed/StructABI/StructABI.cs @@ -668,11 +668,11 @@ public bool Equals(Nested9 other) } } -struct Empty +public struct Empty { } -struct Empty8Float +public struct Empty8Float { public Empty e0, e1, e2, e3, e4, e5, e6, e7; public float FieldF; @@ -688,7 +688,7 @@ public bool Equals(Empty8Float other) } } -struct EmptyFloatEmpty5Byte +public struct EmptyFloatEmpty5Byte { public Empty e; public float FieldF; @@ -706,7 +706,7 @@ public bool Equals(EmptyFloatEmpty5Byte other) } } -struct EmptyFloatEmpty5UByte +public struct EmptyFloatEmpty5UByte { public Empty e; public float FieldF; @@ -724,7 +724,7 @@ public bool Equals(EmptyFloatEmpty5UByte other) } } -struct LongEmptyDouble +public struct LongEmptyDouble { public long FieldL; public Empty FieldE; @@ -741,7 +741,7 @@ public bool Equals(LongEmptyDouble other) } } -struct NestedEmpty +public struct NestedEmpty { public struct InnerEmpty { @@ -750,7 +750,7 @@ public struct InnerEmpty public InnerEmpty e; } -struct NestedEmptyFloatDouble +public struct NestedEmptyFloatDouble { public NestedEmpty FieldNE; public float FieldF; @@ -767,7 +767,7 @@ public bool Equals(NestedEmptyFloatDouble other) } } -struct EmptyIntAndFloat +public struct EmptyIntAndFloat { public struct EmptyInt { @@ -788,7 +788,7 @@ public bool Equals(EmptyIntAndFloat other) } } -struct LongEmptyAndFloat +public struct LongEmptyAndFloat { public struct LongEmpty { @@ -810,12 +810,12 @@ public bool Equals(LongEmptyAndFloat other) } [InlineArray(1)] -struct ArrayOfEmpties +public struct ArrayOfEmpties { public Empty e; }; -struct ArrayOfEmptiesFloatDouble +public struct ArrayOfEmptiesFloatDouble { public ArrayOfEmpties FieldAoE; public float FieldF; @@ -832,12 +832,12 @@ public bool Equals(ArrayOfEmptiesFloatDouble other) } } -struct Eight +public struct Eight { public T e1, e2, e3, e4, e5, e6, e7, e8; } -struct FloatEmpty32kInt +public struct FloatEmpty32kInt { public float FieldF; public Eight>>>> FieldEmpty32k; @@ -855,7 +855,7 @@ public bool Equals(FloatEmpty32kInt other) } [StructLayout(LayoutKind.Sequential, Pack=1)] -struct PackedEmptyFloatLong +public struct PackedEmptyFloatLong { public Empty FieldE; public float FieldF; @@ -873,7 +873,7 @@ public bool Equals(PackedEmptyFloatLong other) } [StructLayout(LayoutKind.Explicit, Pack=1)] -struct ExplicitFloatLong +public struct ExplicitFloatLong { [FieldOffset(1)] public float FieldF; [FieldOffset(5)] public long FieldL; @@ -1022,90 +1022,90 @@ public static partial class StructABI static extern DoubleAndByte EnoughRegistersSysV4(double a, double b, double c, double d, double e, double f, double g, DoubleAndByte value); [DllImport("StructABILib")] - static extern Empty8Float EchoEmpty8FloatRiscV(int a0, float fa0, Empty8Float fa1); + public static extern Empty8Float EchoEmpty8FloatRiscV(int a0, float fa0, Empty8Float fa1); [DllImport("StructABILib")] - static extern Empty8Float EchoEmpty8FloatInIntegerRegsRiscV( + public static extern Empty8Float EchoEmpty8FloatInIntegerRegsRiscV( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a1_a2); [DllImport("StructABILib")] - static extern Empty8Float EchoEmpty8FloatSplitRiscV( + public static extern Empty8Float EchoEmpty8FloatSplitRiscV( int a0, int a1, int a2, int a3, int a4, int a5, int a6, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a7_stack0); [DllImport("StructABILib")] - static extern Empty8Float EchoEmpty8FloatOnStackRiscV( + public static extern Empty8Float EchoEmpty8FloatOnStackRiscV( int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float stack0_stack1); [DllImport("StructABILib")] - static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscV(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1); + public static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscV(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1); [DllImport("StructABILib")] - static extern EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscV(int a0, float fa0, EmptyFloatEmpty5UByte fa1_a1); + public static extern EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscV(int a0, float fa0, EmptyFloatEmpty5UByte fa1_a1); [DllImport("StructABILib")] - static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV( + public static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a1_a2); [DllImport("StructABILib")] - static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteSplitRiscV( + public static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteSplitRiscV( int a0, int a1, int a2, int a3, int a4, int a5, int a6, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a7_stack0); [DllImport("StructABILib")] - static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteOnStackRiscV( + public static extern EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteOnStackRiscV( int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte stack0_stack1); [DllImport("StructABILib")] - static extern LongEmptyDouble EchoLongEmptyDoubleRiscV(int a0, float fa0, LongEmptyDouble a1_fa1); + public static extern LongEmptyDouble EchoLongEmptyDoubleRiscV(int a0, float fa0, LongEmptyDouble a1_fa1); [DllImport("StructABILib")] - static extern LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscV( + public static extern LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscV( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble a1); [DllImport("StructABILib")] - static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscV(int a0, float fa0, NestedEmptyFloatDouble fa1_fa2); + public static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscV(int a0, float fa0, NestedEmptyFloatDouble fa1_fa2); [DllImport("StructABILib")] - static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscV( + public static extern NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscV( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a1_a2); [DllImport("StructABILib")] - static extern EmptyIntAndFloat EchoEmptyIntAndFloatRiscV(int a0, float fa0, EmptyIntAndFloat a1_fa1); + public static extern EmptyIntAndFloat EchoEmptyIntAndFloatRiscV(int a0, float fa0, EmptyIntAndFloat a1_fa1); [DllImport("StructABILib")] - static extern LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(int a0, float fa0, LongEmptyAndFloat a1_fa1); + public static extern LongEmptyAndFloat EchoLongEmptyAndFloatRiscV(int a0, float fa0, LongEmptyAndFloat a1_fa1); [DllImport("StructABILib")] - static extern ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscV(int a0, float fa0, ArrayOfEmptiesFloatDouble a1_a2); + public static extern ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscV(int a0, float fa0, ArrayOfEmptiesFloatDouble a1_a2); [DllImport("StructABILib")] - static extern FloatEmpty32kInt EchoFloatEmpty32kIntRiscV(int a0, float fa0, FloatEmpty32kInt fa1_a1); + public static extern FloatEmpty32kInt EchoFloatEmpty32kIntRiscV(int a0, float fa0, FloatEmpty32kInt fa1_a1); [DllImport("StructABILib")] - static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(int a0, float fa0, PackedEmptyFloatLong fa1_a1); + public static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscV(int a0, float fa0, PackedEmptyFloatLong fa1_a1); [DllImport("StructABILib")] - static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongInIntegerRegsRiscV( + public static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongInIntegerRegsRiscV( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a1_a2); [DllImport("StructABILib")] - static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongSplitRiscV( + public static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongSplitRiscV( int a0, int a1, int a2, int a3, int a4, int a5, int a6, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a7_stack0); [DllImport("StructABILib")] - static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongOnStackRiscV( + public static extern PackedEmptyFloatLong EchoPackedEmptyFloatLongOnStackRiscV( int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong stack0_stack1); [DllImport("StructABILib")] - static extern ExplicitFloatLong EchoExplicitFloatLongRiscV(int a0, float fa0, ExplicitFloatLong fa1_a1); + public static extern ExplicitFloatLong EchoExplicitFloatLongRiscV(int a0, float fa0, ExplicitFloatLong fa1_a1); //////////////////////////////////////////////////////////////////////////// // Managed echo tests. @@ -1371,13 +1371,13 @@ static DoubleAndByte EnoughRegistersSysV4Managed(double a, double b, double c, d } [MethodImpl(MethodImplOptions.NoInlining)] - static Empty8Float EchoEmpty8FloatRiscVManaged(int a0, float fa0, Empty8Float fa1) + public static Empty8Float EchoEmpty8FloatRiscVManaged(int a0, float fa0, Empty8Float fa1) { return fa1; } [MethodImpl(MethodImplOptions.NoInlining)] - static Empty8Float EchoEmpty8FloatInIntegerRegsRiscVManaged( + public static Empty8Float EchoEmpty8FloatInIntegerRegsRiscVManaged( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a1_a2) { @@ -1385,7 +1385,7 @@ static Empty8Float EchoEmpty8FloatInIntegerRegsRiscVManaged( } [MethodImpl(MethodImplOptions.NoInlining)] - static Empty8Float EchoEmpty8FloatSplitRiscVManaged( + public static Empty8Float EchoEmpty8FloatSplitRiscVManaged( int a0, int a1, int a2, int a3, int a4, int a5, int a6, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float a7_stack0) { @@ -1393,7 +1393,7 @@ static Empty8Float EchoEmpty8FloatSplitRiscVManaged( } [MethodImpl(MethodImplOptions.NoInlining)] - static Empty8Float EchoEmpty8FloatOnStackRiscVManaged( + public static Empty8Float EchoEmpty8FloatOnStackRiscVManaged( int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, Empty8Float stack0_stack1) { @@ -1401,19 +1401,19 @@ static Empty8Float EchoEmpty8FloatOnStackRiscVManaged( } [MethodImpl(MethodImplOptions.NoInlining)] - static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscVManaged(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1) + public static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteRiscVManaged(int a0, float fa0, EmptyFloatEmpty5Byte fa1_a1) { return fa1_a1; } [MethodImpl(MethodImplOptions.NoInlining)] - static EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscVManaged(int a0, float fa0, EmptyFloatEmpty5UByte fa1_a1) + public static EmptyFloatEmpty5UByte EchoEmptyFloatEmpty5UByteRiscVManaged(int a0, float fa0, EmptyFloatEmpty5UByte fa1_a1) { return fa1_a1; } [MethodImpl(MethodImplOptions.NoInlining)] - static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVManaged( + public static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVManaged( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a1_a2) { @@ -1421,7 +1421,7 @@ static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVManaged( } [MethodImpl(MethodImplOptions.NoInlining)] - static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteSplitRiscVManaged( + public static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteSplitRiscVManaged( int a0, int a1, int a2, int a3, int a4, int a5, int a6, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte a7_stack0) { @@ -1429,7 +1429,7 @@ static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteSplitRiscVManaged( } [MethodImpl(MethodImplOptions.NoInlining)] - static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteOnStackRiscVManaged( + public static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteOnStackRiscVManaged( int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, EmptyFloatEmpty5Byte stack0_stack1) { @@ -1437,63 +1437,63 @@ static EmptyFloatEmpty5Byte EchoEmptyFloatEmpty5ByteOnStackRiscVManaged( } [MethodImpl(MethodImplOptions.NoInlining)] - static LongEmptyDouble EchoLongEmptyDoubleRiscVManaged(int a0, float fa0, LongEmptyDouble a1_fa1) + public static LongEmptyDouble EchoLongEmptyDoubleRiscVManaged(int a0, float fa0, LongEmptyDouble a1_fa1) { return a1_fa1; } [MethodImpl(MethodImplOptions.NoInlining)] - static LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscVManaged( + public static LongEmptyDouble EchoLongEmptyDoubleByImplicitRefRiscVManaged( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, LongEmptyDouble a1) { return a1; } [MethodImpl(MethodImplOptions.NoInlining)] - static NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscVManaged(int a0, float fa0, NestedEmptyFloatDouble fa1_fa2) + public static NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleRiscVManaged(int a0, float fa0, NestedEmptyFloatDouble fa1_fa2) { return fa1_fa2; } [MethodImpl(MethodImplOptions.NoInlining)] - static NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscVManaged( + public static NestedEmptyFloatDouble EchoNestedEmptyFloatDoubleInIntegerRegsRiscVManaged( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, NestedEmptyFloatDouble a1_a2) { return a1_a2; } [MethodImpl(MethodImplOptions.NoInlining)] - static EmptyIntAndFloat EchoEmptyIntAndFloatRiscVManaged(int a0, float fa0, EmptyIntAndFloat a1_fa1) + public static EmptyIntAndFloat EchoEmptyIntAndFloatRiscVManaged(int a0, float fa0, EmptyIntAndFloat a1_fa1) { return a1_fa1; } [MethodImpl(MethodImplOptions.NoInlining)] - static LongEmptyAndFloat EchoLongEmptyAndFloatRiscVManaged(int a0, float fa0, LongEmptyAndFloat a1_fa1) + public static LongEmptyAndFloat EchoLongEmptyAndFloatRiscVManaged(int a0, float fa0, LongEmptyAndFloat a1_fa1) { return a1_fa1; } [MethodImpl(MethodImplOptions.NoInlining)] - static ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscVManaged(int a0, float fa0, ArrayOfEmptiesFloatDouble a1_a2) + public static ArrayOfEmptiesFloatDouble EchoArrayOfEmptiesFloatDoubleRiscVManaged(int a0, float fa0, ArrayOfEmptiesFloatDouble a1_a2) { return a1_a2; } [MethodImpl(MethodImplOptions.NoInlining)] - static FloatEmpty32kInt EchoFloatEmpty32kIntRiscVManaged(int a0, float fa0, FloatEmpty32kInt fa1_a1) + public static FloatEmpty32kInt EchoFloatEmpty32kIntRiscVManaged(int a0, float fa0, FloatEmpty32kInt fa1_a1) { return fa1_a1; } [MethodImpl(MethodImplOptions.NoInlining)] - static PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscVManaged(int a0, float fa0, PackedEmptyFloatLong fa1_a1) + public static PackedEmptyFloatLong EchoPackedEmptyFloatLongRiscVManaged(int a0, float fa0, PackedEmptyFloatLong fa1_a1) { return fa1_a1; } [MethodImpl(MethodImplOptions.NoInlining)] - static PackedEmptyFloatLong EchoPackedEmptyFloatLongInIntegerRegsRiscVManaged( + public static PackedEmptyFloatLong EchoPackedEmptyFloatLongInIntegerRegsRiscVManaged( int a0, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a1_a2) { @@ -1501,7 +1501,7 @@ static PackedEmptyFloatLong EchoPackedEmptyFloatLongInIntegerRegsRiscVManaged( } [MethodImpl(MethodImplOptions.NoInlining)] - static PackedEmptyFloatLong EchoPackedEmptyFloatLongSplitRiscVManaged( + public static PackedEmptyFloatLong EchoPackedEmptyFloatLongSplitRiscVManaged( int a0, int a1, int a2, int a3, int a4, int a5, int a6, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong a7_stack0) { @@ -1509,7 +1509,7 @@ static PackedEmptyFloatLong EchoPackedEmptyFloatLongSplitRiscVManaged( } [MethodImpl(MethodImplOptions.NoInlining)] - static PackedEmptyFloatLong EchoPackedEmptyFloatLongOnStackRiscVManaged( + public static PackedEmptyFloatLong EchoPackedEmptyFloatLongOnStackRiscVManaged( int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, float fa0, float fa1, float fa2, float fa3, float fa4, float fa5, float fa6, float fa7, PackedEmptyFloatLong stack0_stack1) { @@ -1517,7 +1517,7 @@ static PackedEmptyFloatLong EchoPackedEmptyFloatLongOnStackRiscVManaged( } [MethodImpl(MethodImplOptions.NoInlining)] - static ExplicitFloatLong EchoExplicitFloatLongRiscVManaged(int a0, float fa0, ExplicitFloatLong fa1_a1) + public static ExplicitFloatLong EchoExplicitFloatLongRiscVManaged(int a0, float fa0, ExplicitFloatLong fa1_a1) { return fa1_a1; } @@ -2636,6 +2636,30 @@ static bool EchoEmpty8FloatRiscVWrapper() return ok; } + static bool EchoEmpty8FloatRiscVReflectionWrapper() + { + bool ok = true; + var expected = Empty8Float.Get(); + var native = (Empty8Float)typeof(StructABI).GetMethod("EchoEmpty8FloatRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (Empty8Float)typeof(StructABI).GetMethod("EchoEmpty8FloatRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmpty8FloatRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmpty8FloatRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmpty8FloatInIntegerRegsRiscVWrapper() { bool ok = true; @@ -2660,6 +2684,30 @@ static bool EchoEmpty8FloatInIntegerRegsRiscVWrapper() return ok; } + static bool EchoEmpty8FloatInIntegerRegsRiscVReflectionWrapper() + { + bool ok = true; + var expected = Empty8Float.Get(); + var native = (Empty8Float)typeof(StructABI).GetMethod("EchoEmpty8FloatInIntegerRegsRiscV") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (Empty8Float)typeof(StructABI).GetMethod("EchoEmpty8FloatInIntegerRegsRiscVManaged") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmpty8FloatInIntegerRegsRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmpty8FloatInIntegerRegsRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmpty8FloatSplitRiscVWrapper() { bool ok = true; @@ -2684,6 +2732,30 @@ static bool EchoEmpty8FloatSplitRiscVWrapper() return ok; } + static bool EchoEmpty8FloatSplitRiscVReflectionWrapper() + { + bool ok = true; + var expected = Empty8Float.Get(); + var native = (Empty8Float)typeof(StructABI).GetMethod("EchoEmpty8FloatSplitRiscV") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (Empty8Float)typeof(StructABI).GetMethod("EchoEmpty8FloatSplitRiscVManaged") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmpty8FloatSplitRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmpty8FloatSplitRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmpty8FloatOnStackRiscVWrapper() { bool ok = true; @@ -2708,6 +2780,30 @@ static bool EchoEmpty8FloatOnStackRiscVWrapper() return ok; } + static bool EchoEmpty8FloatOnStackRiscVReflectionWrapper() + { + bool ok = true; + var expected = Empty8Float.Get(); + var native = (Empty8Float)typeof(StructABI).GetMethod("EchoEmpty8FloatOnStackRiscV") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (Empty8Float)typeof(StructABI).GetMethod("EchoEmpty8FloatOnStackRiscVManaged") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmpty8FloatOnStackRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmpty8FloatOnStackRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmptyFloatEmpty5ByteRiscVWrapper() { bool ok = true; @@ -2730,6 +2826,30 @@ static bool EchoEmptyFloatEmpty5ByteRiscVWrapper() return ok; } + static bool EchoEmptyFloatEmpty5ByteRiscVReflectionWrapper() + { + bool ok = true; + var expected = EmptyFloatEmpty5Byte.Get(); + var native = (EmptyFloatEmpty5Byte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5ByteRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (EmptyFloatEmpty5Byte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5ByteRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmptyFloatEmpty5ByteRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmptyFloatEmpty5ByteRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmptyFloatEmpty5UByteRiscVWrapper() { bool ok = true; @@ -2752,6 +2872,30 @@ static bool EchoEmptyFloatEmpty5UByteRiscVWrapper() return ok; } + static bool EchoEmptyFloatEmpty5UByteRiscVReflectionWrapper() + { + bool ok = true; + var expected = EmptyFloatEmpty5UByte.Get(); + var native = (EmptyFloatEmpty5UByte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5UByteRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (EmptyFloatEmpty5UByte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5UByteRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmptyFloatEmpty5UByteRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmptyFloatEmpty5UByteRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVWrapper() { bool ok = true; @@ -2776,6 +2920,30 @@ static bool EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVWrapper() return ok; } + static bool EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVReflectionWrapper() + { + bool ok = true; + var expected = EmptyFloatEmpty5Byte.Get(); + var native = (EmptyFloatEmpty5Byte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (EmptyFloatEmpty5Byte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVManaged") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmptyFloatEmpty5ByteInIntegerRegsRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmptyFloatEmpty5ByteSplitRiscVWrapper() { bool ok = true; @@ -2800,6 +2968,30 @@ static bool EchoEmptyFloatEmpty5ByteSplitRiscVWrapper() return ok; } + static bool EchoEmptyFloatEmpty5ByteSplitRiscVReflectionWrapper() + { + bool ok = true; + var expected = EmptyFloatEmpty5Byte.Get(); + var native = (EmptyFloatEmpty5Byte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5ByteSplitRiscV") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (EmptyFloatEmpty5Byte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5ByteSplitRiscVManaged") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmptyFloatEmpty5ByteSplitRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmptyFloatEmpty5ByteSplitRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmptyFloatEmpty5ByteOnStackRiscVWrapper() { bool ok = true; @@ -2824,6 +3016,30 @@ static bool EchoEmptyFloatEmpty5ByteOnStackRiscVWrapper() return ok; } + static bool EchoEmptyFloatEmpty5ByteOnStackRiscVReflectionWrapper() + { + bool ok = true; + var expected = EmptyFloatEmpty5Byte.Get(); + var native = (EmptyFloatEmpty5Byte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5ByteOnStackRiscV") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (EmptyFloatEmpty5Byte)typeof(StructABI).GetMethod("EchoEmptyFloatEmpty5ByteOnStackRiscVManaged") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmptyFloatEmpty5ByteOnStackRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmptyFloatEmpty5ByteOnStackRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoLongEmptyDoubleRiscVWrapper() { bool ok = true; @@ -2846,6 +3062,30 @@ static bool EchoLongEmptyDoubleRiscVWrapper() return ok; } + static bool EchoLongEmptyDoubleRiscVReflectionWrapper() + { + bool ok = true; + var expected = LongEmptyDouble.Get(); + var native = (LongEmptyDouble)typeof(StructABI).GetMethod("EchoLongEmptyDoubleRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (LongEmptyDouble)typeof(StructABI).GetMethod("EchoLongEmptyDoubleRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoLongEmptyDoubleRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoLongEmptyDoubleRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoLongEmptyDoubleByImplicitRefRiscVWrapper() { bool ok = true; @@ -2867,6 +3107,30 @@ static bool EchoLongEmptyDoubleByImplicitRefRiscVWrapper() return ok; } + static bool EchoLongEmptyDoubleByImplicitRefRiscVReflectionWrapper() + { + bool ok = true; + var expected = LongEmptyDouble.Get(); + var native = (LongEmptyDouble)typeof(StructABI).GetMethod("EchoLongEmptyDoubleByImplicitRefRiscV") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (LongEmptyDouble)typeof(StructABI).GetMethod("EchoLongEmptyDoubleByImplicitRefRiscVManaged") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoLongEmptyDoubleByImplicitRefRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoLongEmptyDoubleByImplicitRefRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoNestedEmptyFloatDoubleRiscVWrapper() { bool ok = true; @@ -2889,6 +3153,30 @@ static bool EchoNestedEmptyFloatDoubleRiscVWrapper() return ok; } + static bool EchoNestedEmptyFloatDoubleRiscVReflectionWrapper() + { + bool ok = true; + var expected = NestedEmptyFloatDouble.Get(); + var native = (NestedEmptyFloatDouble)typeof(StructABI).GetMethod("EchoNestedEmptyFloatDoubleRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (NestedEmptyFloatDouble)typeof(StructABI).GetMethod("EchoNestedEmptyFloatDoubleRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoNestedEmptyFloatDoubleRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoNestedEmptyFloatDoubleRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper() { bool ok = true; @@ -2911,6 +3199,30 @@ static bool EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper() return ok; } + static bool EchoNestedEmptyFloatDoubleInIntegerRegsRiscVReflectionWrapper() + { + bool ok = true; + var expected = NestedEmptyFloatDouble.Get(); + var native = (NestedEmptyFloatDouble)typeof(StructABI).GetMethod("EchoNestedEmptyFloatDoubleInIntegerRegsRiscV") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, expected}); + var managed = (NestedEmptyFloatDouble)typeof(StructABI).GetMethod("EchoNestedEmptyFloatDoubleInIntegerRegsRiscVManaged") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoNestedEmptyFloatDoubleInIntegerRegsRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoNestedEmptyFloatDoubleInIntegerRegsRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoEmptyIntAndFloatRiscVWrapper() { bool ok = true; @@ -2933,6 +3245,30 @@ static bool EchoEmptyIntAndFloatRiscVWrapper() return ok; } + static bool EchoEmptyIntAndFloatRiscVReflectionWrapper() + { + bool ok = true; + var expected = EmptyIntAndFloat.Get(); + var native = (EmptyIntAndFloat)typeof(StructABI).GetMethod("EchoEmptyIntAndFloatRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (EmptyIntAndFloat)typeof(StructABI).GetMethod("EchoEmptyIntAndFloatRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoEmptyIntAndFloatRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoEmptyIntAndFloatRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoLongEmptyAndFloatRiscVWrapper() { bool ok = true; @@ -2955,6 +3291,30 @@ static bool EchoLongEmptyAndFloatRiscVWrapper() return ok; } + static bool EchoLongEmptyAndFloatRiscVReflectionWrapper() + { + bool ok = true; + var expected = LongEmptyAndFloat.Get(); + var native = (LongEmptyAndFloat)typeof(StructABI).GetMethod("EchoLongEmptyAndFloatRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (LongEmptyAndFloat)typeof(StructABI).GetMethod("EchoLongEmptyAndFloatRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoLongEmptyAndFloatRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoLongEmptyAndFloatRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoArrayOfEmptiesFloatDoubleRiscVWrapper() { bool ok = true; @@ -2977,6 +3337,30 @@ static bool EchoArrayOfEmptiesFloatDoubleRiscVWrapper() return ok; } + static bool EchoArrayOfEmptiesFloatDoubleRiscVReflectionWrapper() + { + bool ok = true; + var expected = ArrayOfEmptiesFloatDouble.Get(); + var native = (ArrayOfEmptiesFloatDouble)typeof(StructABI).GetMethod("EchoArrayOfEmptiesFloatDoubleRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (ArrayOfEmptiesFloatDouble)typeof(StructABI).GetMethod("EchoArrayOfEmptiesFloatDoubleRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoArrayOfEmptiesFloatDoubleRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoArrayOfEmptiesFloatDoubleRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoFloatEmpty32kIntRiscVWrapper() { bool ok = true; @@ -2999,6 +3383,30 @@ static bool EchoFloatEmpty32kIntRiscVWrapper() return ok; } + static bool EchoFloatEmpty32kIntRiscVReflectionWrapper() + { + bool ok = true; + var expected = FloatEmpty32kInt.Get(); + var native = (FloatEmpty32kInt)typeof(StructABI).GetMethod("EchoFloatEmpty32kIntRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (FloatEmpty32kInt)typeof(StructABI).GetMethod("EchoFloatEmpty32kIntRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoFloatEmpty32kIntRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoFloatEmpty32kIntRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoPackedEmptyFloatLongRiscVWrapper() { bool ok = true; @@ -3021,6 +3429,30 @@ static bool EchoPackedEmptyFloatLongRiscVWrapper() return ok; } + static bool EchoPackedEmptyFloatLongRiscVReflectionWrapper() + { + bool ok = true; + var expected = PackedEmptyFloatLong.Get(); + var native = (PackedEmptyFloatLong)typeof(StructABI).GetMethod("EchoPackedEmptyFloatLongRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (PackedEmptyFloatLong)typeof(StructABI).GetMethod("EchoPackedEmptyFloatLongRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoPackedEmptyFloatLongRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoPackedEmptyFloatLongRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoPackedEmptyFloatLongInIntegerRegsRiscVWrapper() { bool ok = true; @@ -3045,6 +3477,30 @@ static bool EchoPackedEmptyFloatLongInIntegerRegsRiscVWrapper() return ok; } + static bool EchoPackedEmptyFloatLongInIntegerRegsRiscVReflectionWrapper() + { + bool ok = true; + var expected = PackedEmptyFloatLong.Get(); + var native = (PackedEmptyFloatLong)typeof(StructABI).GetMethod("EchoPackedEmptyFloatLongInIntegerRegsRiscV") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (PackedEmptyFloatLong)typeof(StructABI).GetMethod("EchoPackedEmptyFloatLongInIntegerRegsRiscVManaged") + .Invoke(null, new object[] {0, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoPackedEmptyFloatLongInIntegerRegsRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoPackedEmptyFloatLongInIntegerRegsRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoPackedEmptyFloatLongSplitRiscVWrapper() { bool ok = true; @@ -3069,6 +3525,30 @@ static bool EchoPackedEmptyFloatLongSplitRiscVWrapper() return ok; } + static bool EchoPackedEmptyFloatLongSplitRiscVReflectionWrapper() + { + bool ok = true; + var expected = PackedEmptyFloatLong.Get(); + var native = (PackedEmptyFloatLong)typeof(StructABI).GetMethod("EchoPackedEmptyFloatLongSplitRiscV") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (PackedEmptyFloatLong)typeof(StructABI).GetMethod("EchoPackedEmptyFloatLongSplitRiscVManaged") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoPackedEmptyFloatLongSplitRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoPackedEmptyFloatLongSplitRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoPackedEmptyFloatLongOnStackRiscVWrapper() { bool ok = true; @@ -3093,6 +3573,30 @@ static bool EchoPackedEmptyFloatLongOnStackRiscVWrapper() return ok; } + static bool EchoPackedEmptyFloatLongOnStackRiscVReflectionWrapper() + { + bool ok = true; + var expected = PackedEmptyFloatLong.Get(); + var native = (PackedEmptyFloatLong)typeof(StructABI).GetMethod("EchoPackedEmptyFloatLongOnStackRiscV") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + var managed = (PackedEmptyFloatLong)typeof(StructABI).GetMethod("EchoPackedEmptyFloatLongOnStackRiscVManaged") + .Invoke(null, new object[] {0, 1, 2, 3, 4, 5, 6, 7, 0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoPackedEmptyFloatLongOnStackRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoPackedEmptyFloatLongOnStackRiscV failed"); + ok = false; + } + + return ok; + } + static bool EchoExplicitFloatLongRiscVWrapper() { bool ok = true; @@ -3115,6 +3619,30 @@ static bool EchoExplicitFloatLongRiscVWrapper() return ok; } + static bool EchoExplicitFloatLongRiscVReflectionWrapper() + { + bool ok = true; + var expected = ExplicitFloatLong.Get(); + var native = (ExplicitFloatLong)typeof(StructABI).GetMethod("EchoExplicitFloatLongRiscV") + .Invoke(null, new object[] {0, 0f, expected}); + var managed = (ExplicitFloatLong)typeof(StructABI).GetMethod("EchoExplicitFloatLongRiscVManaged") + .Invoke(null, new object[] {0, 0f, expected}); + + if (!expected.Equals(native)) + { + Console.WriteLine("Native call by reflection for EchoExplicitFloatLongRiscV failed"); + ok = false; + } + + if (!expected.Equals(managed)) + { + Console.WriteLine("Managed call by reflection for EchoExplicitFloatLongRiscV failed"); + ok = false; + } + + return ok; + } + [Fact] public static int TestEntryPoint() { @@ -3164,26 +3692,47 @@ public static int TestEntryPoint() if (!EnoughRegistersSysV3Wrapper()) ok = false; if (!EnoughRegistersSysV4Wrapper()) ok = false; if (!EchoEmpty8FloatRiscVWrapper()) ok = false; + if (!EchoEmpty8FloatRiscVReflectionWrapper()) ok = false; if (!EchoEmpty8FloatInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoEmpty8FloatInIntegerRegsRiscVReflectionWrapper()) ok = false; if (!EchoEmpty8FloatSplitRiscVWrapper()) ok = false; + if (!EchoEmpty8FloatSplitRiscVReflectionWrapper()) ok = false; if (!EchoEmpty8FloatOnStackRiscVWrapper()) ok = false; + if (!EchoEmpty8FloatOnStackRiscVReflectionWrapper()) ok = false; if (!EchoEmptyFloatEmpty5ByteRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5ByteRiscVReflectionWrapper()) ok = false; if (!EchoEmptyFloatEmpty5UByteRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5UByteRiscVReflectionWrapper()) ok = false; if (!EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5ByteInIntegerRegsRiscVReflectionWrapper()) ok = false; if (!EchoEmptyFloatEmpty5ByteSplitRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5ByteSplitRiscVReflectionWrapper()) ok = false; if (!EchoEmptyFloatEmpty5ByteOnStackRiscVWrapper()) ok = false; + if (!EchoEmptyFloatEmpty5ByteOnStackRiscVReflectionWrapper()) ok = false; if (!EchoLongEmptyDoubleRiscVWrapper()) ok = false; + if (!EchoLongEmptyDoubleRiscVReflectionWrapper()) ok = false; if (!EchoLongEmptyDoubleByImplicitRefRiscVWrapper()) ok = false; + if (!EchoLongEmptyDoubleByImplicitRefRiscVReflectionWrapper()) ok = false; if (!EchoNestedEmptyFloatDoubleInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoNestedEmptyFloatDoubleInIntegerRegsRiscVReflectionWrapper()) ok = false; if (!EchoEmptyIntAndFloatRiscVWrapper()) ok = false; + if (!EchoEmptyIntAndFloatRiscVReflectionWrapper()) ok = false; if (!EchoLongEmptyAndFloatRiscVWrapper()) ok = false; + if (!EchoLongEmptyAndFloatRiscVReflectionWrapper()) ok = false; if (!EchoArrayOfEmptiesFloatDoubleRiscVWrapper()) ok = false; + if (!EchoArrayOfEmptiesFloatDoubleRiscVReflectionWrapper()) ok = false; if (!EchoFloatEmpty32kIntRiscVWrapper()) ok = false; + if (!EchoFloatEmpty32kIntRiscVReflectionWrapper()) ok = false; if (!EchoPackedEmptyFloatLongRiscVWrapper()) ok = false; + if (!EchoPackedEmptyFloatLongRiscVReflectionWrapper()) ok = false; if (!EchoPackedEmptyFloatLongInIntegerRegsRiscVWrapper()) ok = false; + if (!EchoPackedEmptyFloatLongInIntegerRegsRiscVReflectionWrapper()) ok = false; if (!EchoPackedEmptyFloatLongSplitRiscVWrapper()) ok = false; + if (!EchoPackedEmptyFloatLongSplitRiscVReflectionWrapper()) ok = false; if (!EchoPackedEmptyFloatLongOnStackRiscVWrapper()) ok = false; + if (!EchoPackedEmptyFloatLongOnStackRiscVReflectionWrapper()) ok = false; if (!EchoExplicitFloatLongRiscVWrapper()) ok = false; + if (!EchoExplicitFloatLongRiscVReflectionWrapper()) ok = false; return ok ? 100 : -1; } From ac2670ba9cfa5693ae159b5e4c81c2bd77f67de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 11 Jun 2024 12:50:22 +0200 Subject: [PATCH 46/60] Fix small struct passing to calls by reflection: CopyStructToRegisters is now aware of field offsets and widths --- src/coreclr/vm/argdestination.h | 88 ++++++++++------------------- src/coreclr/vm/callingconvention.h | 20 +++---- src/coreclr/vm/comdelegate.cpp | 2 +- src/coreclr/vm/riscv64/profiler.cpp | 15 ++--- 4 files changed, 49 insertions(+), 76 deletions(-) diff --git a/src/coreclr/vm/argdestination.h b/src/coreclr/vm/argdestination.h index 57df0326648167..bb84fc41338f26 100644 --- a/src/coreclr/vm/argdestination.h +++ b/src/coreclr/vm/argdestination.h @@ -106,72 +106,46 @@ class ArgDestination #endif // TARGET_RISCV64 _ASSERTE(IsStructPassedInRegs()); - _ASSERTE(fieldBytes <= 16); + _ASSERTE(destOffset == 0); + LOONGARCH64_ONLY(_ASSERTE(fieldBytes <= 16);) - int argOfs = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_argLocDescForStructInRegs->m_idxFloatReg * 8; + using namespace FpStruct; + FpStructInRegistersInfo info = m_argLocDescForStructInRegs->m_structFields; + _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == ((info.flags & BothFloat) ? 2 : 1)); + _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == ((info.flags & (Float1st | Float2nd)) ? 1 : 0)); - if (m_argLocDescForStructInRegs->m_structFields == STRUCT_FLOAT_FIELD_ONLY_TWO) - { // struct with two floats. - _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == 2); - _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == 0); - *(INT64*)((char*)m_base + argOfs) = NanBox | *(INT32*)src; - *(INT64*)((char*)m_base + argOfs + 8) = NanBox | *((INT32*)src + 1); - } - else if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_FIRST) != 0) - { // the first field is float or double. - _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == 1); - _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == 1); - _ASSERTE((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_SECOND) == 0);//the second field is integer. + int floatRegOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + + m_argLocDescForStructInRegs->m_idxFloatReg * FLOAT_REGISTER_SIZE; + INT64* floatReg = (INT64*)((char*)m_base + floatRegOffset); - if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_FIRST_FIELD_SIZE_IS8) == 0) - { - *(INT64*)((char*)m_base + argOfs) = NanBox | *(INT32*)src; // the first field is float - } - else - { - *(UINT64*)((char*)m_base + argOfs) = *(UINT64*)src; // the first field is double. - } + if (info.flags & (OnlyOne | BothFloat | Float1st)) // copy first floating field + { + void* field = (char*)src + info.offset1st; + *floatReg++ = info.IsSize1st8() ? *(INT64*)field : NanBox | *(INT32*)field; + } - argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_argLocDescForStructInRegs->m_idxGenReg * 8; - if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_HAS_8BYTES_FIELDS_MASK) != 0) - { - *(UINT64*)((char*)m_base + argOfs) = *((UINT64*)src + 1); - } - else - { - *(INT64*)((char*)m_base + argOfs) = *((INT32*)src + 1); // the second field is int32. - } + if (info.flags & (BothFloat | Float2nd)) // copy second floating field + { + void* field = (char*)src + info.offset2nd; + *floatReg = info.IsSize2nd8() ? *(INT64*)field : NanBox | *(INT32*)field; } - else if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_SECOND) != 0) - { // the second field is float or double. - _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == 1); - _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == 1); - _ASSERTE((m_argLocDescForStructInRegs->m_structFields & STRUCT_FLOAT_FIELD_FIRST) == 0);//the first field is integer. - - // destOffset - nonzero when copying values into Nullable, it is the offset of the T value inside of the Nullable. - // here the first field maybe Nullable. - if ((m_argLocDescForStructInRegs->m_structFields & STRUCT_HAS_8BYTES_FIELDS_MASK) == 0) - { - // the second field is float. - *(INT64*)((char*)m_base + argOfs) = NanBox | (destOffset == 0 ? *((INT32*)src + 1) : *(INT32*)src); - } - else - { - // the second field is double. - *(UINT64*)((char*)m_base + argOfs) = destOffset == 0 ? *((UINT64*)src + 1) : *(UINT64*)src; - } - if (0 == destOffset) + if (info.flags & (Float1st | Float2nd)) // copy integer field + { + int intRegOffset = TransitionBlock::GetOffsetOfArgumentRegisters() + + m_argLocDescForStructInRegs->m_idxGenReg * TARGET_POINTER_SIZE; + INT64* intReg = (INT64*)((char*)m_base + intRegOffset); + + void* field = (char*)src + ((info.flags & Float2nd) ? info.offset1st : info.offset2nd); + switch ((info.flags & Float2nd) ? info.GetSizeShift1st() : info.GetSizeShift2nd()) { - // NOTE: here ignoring the first size. - argOfs = TransitionBlock::GetOffsetOfArgumentRegisters() + m_argLocDescForStructInRegs->m_idxGenReg * 8; - *(UINT64*)((char*)m_base + argOfs) = *(UINT64*)src; + case 0: *intReg = *(INT8* )field; break; + case 1: *intReg = *(INT16*)field; break; + case 2: *intReg = *(INT32*)field; break; + case 3: *intReg = *(INT64*)field; break; + default: _ASSERTE(false); } } - else - { - _ASSERTE(!"---------UNReachable-------LoongArch64/RISC-V64!!!"); - } } #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 6a99bdbf9c1df9..b630797968acea 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -42,7 +42,7 @@ struct ArgLocDesc int m_byteStackIndex; // Stack offset in bytes (or -1) int m_byteStackSize; // Stack size in bytes #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - int m_structFields; // Struct field info when using Float-register except two-doubles case. + FpStructInRegistersInfo m_structFields; // Struct field info when using floating-point register(s) #endif #if defined(UNIX_AMD64_ABI) @@ -97,7 +97,7 @@ struct ArgLocDesc m_hfaFieldSize = 0; #endif // defined(TARGET_ARM64) #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - m_structFields = STRUCT_NO_FLOAT_FIELD; + m_structFields = {}; #endif #if defined(UNIX_AMD64_ABI) m_eeClass = NULL; @@ -1844,7 +1844,7 @@ int ArgIteratorTemplate::GetNextOffset() m_argLocDescForStructInRegs.m_idxGenReg = m_idxGenReg; m_argLocDescForStructInRegs.m_cGenReg = 1; - m_argLocDescForStructInRegs.m_structFields = info.ToOldFlags(); + m_argLocDescForStructInRegs.m_structFields = info; m_hasArgLocDescForStructInRegs = true; int regOffset = (info.flags & FpStruct::Float2nd) @@ -1858,18 +1858,16 @@ int ArgIteratorTemplate::GetNextOffset() else if (cFPRegs + m_idxFPReg <= NUM_ARGUMENT_REGISTERS) { int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; - if (info.flags == (FpStruct::BothFloat | (2 << FpStruct::PosSizeShift1st) | (2 << FpStruct::PosSizeShift2nd))) + + if ((info.flags & (FpStruct::BothFloat | FpStruct::OnlyOne)) + && (info.offset1st != 0 || info.offset2nd != FLOAT_REGISTER_SIZE)) { - // Struct with two single-float fields - assert(info.GetSize1st() == sizeof(float)); - assert(info.GetSize2nd() == sizeof(float)); + // Struct with one or two floating-point fields laid out differently than in TransitionBlock::FloatArgumentRegisters m_argLocDescForStructInRegs.Init(); m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; - m_argLocDescForStructInRegs.m_cFloatReg = 2; - assert(cFPRegs == 2); - assert(argSize == 2 * sizeof(float)); + m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs; - m_argLocDescForStructInRegs.m_structFields = info.ToOldFlags(); + m_argLocDescForStructInRegs.m_structFields = info; m_hasArgLocDescForStructInRegs = true; } m_idxFPReg += cFPRegs; diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index 77664d54f96ac8..fa8e067406be42 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -172,7 +172,7 @@ class ShuffleIterator if (m_currentFloatRegIndex < m_argLocDesc->m_cFloatReg) { #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - if ((m_argLocDesc->m_structFields & STRUCT_FLOAT_FIELD_SECOND) && (m_currentGenRegIndex < m_argLocDesc->m_cGenReg)) + if ((m_argLocDesc->m_structFields.flags & FpStruct::Float2nd) && (m_currentGenRegIndex < m_argLocDesc->m_cGenReg)) { // the first field is integer so just skip this. } diff --git a/src/coreclr/vm/riscv64/profiler.cpp b/src/coreclr/vm/riscv64/profiler.cpp index fc8eff48456388..785344d0437689 100644 --- a/src/coreclr/vm/riscv64/profiler.cpp +++ b/src/coreclr/vm/riscv64/profiler.cpp @@ -111,13 +111,14 @@ LPVOID ProfileArgIterator::CopyStructFromRegisters(const ArgLocDesc* sir) _ASSERTE(m_handle); PROFILE_PLATFORM_SPECIFIC_DATA* pData = reinterpret_cast(m_handle); + StructFloatFieldInfoFlags flags = sir->m_structFields.ToOldFlags(); struct { bool isFloat, is8; } fields[] = { - { (bool) (sir->m_structFields & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_ONLY_ONE)), - (bool) (sir->m_structFields & STRUCT_FIRST_FIELD_SIZE_IS8) }, - { (bool) (sir->m_structFields & (STRUCT_FLOAT_FIELD_SECOND | STRUCT_FLOAT_FIELD_ONLY_TWO)), - (bool) (sir->m_structFields & STRUCT_SECOND_FIELD_SIZE_IS8) }, + { (bool) (flags & (STRUCT_FLOAT_FIELD_FIRST | STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FLOAT_FIELD_ONLY_ONE)), + (bool) (flags & STRUCT_FIRST_FIELD_SIZE_IS8) }, + { (bool) (flags & (STRUCT_FLOAT_FIELD_SECOND | STRUCT_FLOAT_FIELD_ONLY_TWO)), + (bool) (flags & STRUCT_SECOND_FIELD_SIZE_IS8) }, }; - int fieldCount = (sir->m_structFields & STRUCT_FLOAT_FIELD_ONLY_ONE) ? 1 : 2; + int fieldCount = (flags & STRUCT_FLOAT_FIELD_ONLY_ONE) ? 1 : 2; UINT64 bufferPosBegin = m_bufferPos; const double *fRegBegin = &pData->floatArgumentRegisters.f[sir->m_idxFloatReg], *fReg = fRegBegin; const double *fRegEnd = &pData->floatArgumentRegisters.f[0] + NUM_FLOAT_ARGUMENT_REGISTERS; @@ -185,7 +186,7 @@ LPVOID ProfileArgIterator::GetNextArgAddr() // If both fields are in registers of same kind (either float or general) and both are 8 bytes, no need to copy. // We can get away with returning a ptr to argumentRegisters since the struct would have the same layout. if ((sir->m_cFloatReg ^ sir->m_cGenReg) != 2 || - (sir->m_structFields & STRUCT_HAS_8BYTES_FIELDS_MASK) != STRUCT_HAS_8BYTES_FIELDS_MASK) + (sir->m_structFields.ToOldFlags() & STRUCT_HAS_8BYTES_FIELDS_MASK) != STRUCT_HAS_8BYTES_FIELDS_MASK) { return CopyStructFromRegisters(sir); } @@ -317,7 +318,7 @@ LPVOID ProfileArgIterator::GetReturnBufferAddr(void) sir.m_cGenReg = -1; sir.m_byteStackIndex = 0; sir.m_byteStackSize = -1; - sir.m_structFields = fpReturnSize; + sir.m_structFields = FpStructInRegistersInfo::FromOldFlags((StructFloatFieldInfoFlags)fpReturnSize); return CopyStructFromRegisters(&sir); } From 76533b6f1770801f364177ee0254ad3bee8a9a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 12 Jun 2024 11:20:37 +0200 Subject: [PATCH 47/60] Fix FP structs returned from calls by reflection Rework CallDescrWorkerInternal to do simple register saving into CallDescrWorker::returnValue rather than try to reconstruct the struct there. Reconstruction respecting the actual field layout is handled then by CopyReturnedFpStructFromRegisters. This fixes most reflection call tests in JIT/Directed/StructABI/StructABI. --- src/coreclr/vm/callhelpers.cpp | 40 ++++++ src/coreclr/vm/callhelpers.h | 7 + src/coreclr/vm/callingconvention.h | 24 +++- src/coreclr/vm/reflectioninvocation.cpp | 12 +- src/coreclr/vm/riscv64/asmconstants.h | 31 +---- .../vm/riscv64/calldescrworkerriscv64.S | 123 ++---------------- 6 files changed, 96 insertions(+), 141 deletions(-) diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index bc426b9c40b16b..cfd940e81ed96c 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -162,6 +162,46 @@ void DispatchCallDebuggerWrapper( PAL_ENDTRY } +#if defined(TARGET_RISCV64) +void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStructInRegistersInfo info) +{ + _ASSERTE(info.flags != FpStruct::UseIntCallConv); + unsigned reg1Offset, reg1Size; // offset and size of the field passed in the first saved register (fa0) + unsigned reg2Offset, reg2Size; // offset and size of the field passed in the second saved register (fa1 or a0) + if (info.flags & FpStruct::Float2nd) + { + reg1Offset = info.offset2nd; + reg2Offset = info.offset1st; + reg1Size = info.GetSize2nd(); + reg2Size = info.GetSize1st(); + } + else + { + reg1Offset = info.offset1st; + reg2Offset = info.offset2nd; + reg1Size = info.GetSize1st(); + reg2Size = info.GetSize2nd(); + } + + memcpyNoGCRefs((char*)dest + reg1Offset, &returnRegs[0], reg1Size); + + if ((info.flags & FpStruct::OnlyOne) == 0) + { + char* pField = (char*)dest + reg2Offset; + if (info.flags & FpStruct::GcRef) + { + _ASSERTE(info.flags & (FpStruct::Float1st | FpStruct::Float2nd)); + _ASSERTE(reg2Size == TARGET_POINTER_SIZE); + memmoveGCRefs(pField, &returnRegs[1], TARGET_POINTER_SIZE); + } + else + { + memcpyNoGCRefs(pField, &returnRegs[1], reg2Size); + } + } +} +#endif // TARGET_RISCV64 + // Helper for VM->managed calls with simple signatures. void * DispatchCallSimple( SIZE_T *pSrc, diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index f6dd105263b2f2..2291af9c80ff9d 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -73,6 +73,13 @@ void * DispatchCallSimple( PCODE pTargetAddress, DWORD dwDispatchCallSimpleFlags); +#if defined(TARGET_RISCV64) +// On RISC-V structs returned according to floating-point calling convention may be larger than 16 bytes. +// This routine copies such structs from 'returnRegs' containing fields (each in one register) as they are filled +// by CallDescrWorkerInternal, to the final destination 'dest' respecting the struct's layout described in 'info'. +void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStructInRegistersInfo info); +#endif // TARGET_RISCV64 + bool IsCerRootMethod(MethodDesc *pMD); class MethodDescCallSite diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index b630797968acea..c9f80d1692bcbb 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -436,6 +436,21 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE return m_dwFlags >> RETURN_FP_SIZE_SHIFT; } +#if defined(TARGET_RISCV64) + FpStructInRegistersInfo GetReturnFpStructInRegistersInfo() + { + WRAPPER_NO_CONTRACT; + if (!(m_dwFlags & RETURN_FLAGS_COMPUTED)) + ComputeReturnFlags(); + + return { + FpStruct::Flags(m_dwFlags >> RETURN_FP_SIZE_SHIFT), + m_returnedFpFieldOffsets[0], + m_returnedFpFieldOffsets[1], + }; + } +#endif // TARGET_RISCV64 + #ifdef TARGET_X86 //========================================================================= // Indicates whether an argument is to be put in a register using the @@ -923,6 +938,11 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE protected: DWORD m_dwFlags; // Cached flags int m_nSizeOfArgStack; // Cached value of SizeOfArgStack +#if defined(TARGET_RISCV64) + // Offsets of fields returned according to hardware floating-point calling convention + // (FpStruct::Flags are embedded in m_dwFlags, accessible with GetFPReturnSize()) + unsigned m_returnedFpFieldOffsets[2]; +#endif DWORD m_argNum; @@ -2033,7 +2053,9 @@ void ArgIteratorTemplate::ComputeReturnFlags() #elif defined(TARGET_RISCV64) assert(!thValueType.IsTypeDesc()); FpStructInRegistersInfo info = MethodTable::GetRiscV64PassFpStructInRegistersInfo(thValueType); - flags |= (info.ToOldFlags() & 0xff) << RETURN_FP_SIZE_SHIFT; + flags |= info.flags << RETURN_FP_SIZE_SHIFT; + m_returnedFpFieldOffsets[0] = info.offset1st; + m_returnedFpFieldOffsets[1] = info.offset2nd; if (info.flags != FpStruct::UseIntCallConv || size <= ENREGISTERED_RETURNTYPE_INTEGER_MAXSIZE) break; diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index a98dafd62a558b..19711cff4ebd60 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -704,7 +704,17 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, // we have allocated for this purpose. else if (!fHasRetBuffArg) { - CopyValueClass(gc.retVal->GetData(), &callDescrData.returnValue, gc.retVal->GetMethodTable()); + #if defined(TARGET_RISCV64) + FpStructInRegistersInfo info = argit.GetReturnFpStructInRegistersInfo(); + if (info.flags != FpStruct::UseIntCallConv) + { + CopyReturnedFpStructFromRegisters(gc.retVal->GetData(), callDescrData.returnValue, info); + } + else + #endif // TARGET_RISCV64 + { + CopyValueClass(gc.retVal->GetData(), &callDescrData.returnValue, gc.retVal->GetMethodTable()); + } } // From here on out, it is OK to have GCs since the return object (which may have had // GC pointers has been put into a GC object and thus protected. diff --git a/src/coreclr/vm/riscv64/asmconstants.h b/src/coreclr/vm/riscv64/asmconstants.h index d10d2d4dabc961..704a41fb6fca50 100644 --- a/src/coreclr/vm/riscv64/asmconstants.h +++ b/src/coreclr/vm/riscv64/asmconstants.h @@ -66,35 +66,8 @@ ASMCONSTANTS_C_ASSERT(CallDescrData__fpReturnSize == offsetof(CallDescrD ASMCONSTANTS_C_ASSERT(CallDescrData__pTarget == offsetof(CallDescrData, pTarget)) ASMCONSTANTS_C_ASSERT(CallDescrData__returnValue == offsetof(CallDescrData, returnValue)) -#define CallDescrData__flagOneFloat 0x1 -#define CallDescrData__flagOneDouble 0x11 -#define CallDescrData__flagFloatInt 0x2 -#define CallDescrData__flagFloatLong 0x22 -#define CallDescrData__flagDoubleInt 0x12 -#define CallDescrData__flagDoubleLong 0x32 -#define CallDescrData__flagIntFloat 0x4 -#define CallDescrData__flagIntDouble 0x24 -#define CallDescrData__flagLongFloat 0x14 -#define CallDescrData__flagLongDouble 0x34 -#define CallDescrData__flagFloatFloat 0x8 -#define CallDescrData__flagFloatDouble 0x28 -#define CallDescrData__flagDoubleFloat 0x18 -#define CallDescrData__flagDoubleDouble 0x38 - -ASMCONSTANTS_C_ASSERT(CallDescrData__flagOneFloat == (int)STRUCT_FLOAT_FIELD_ONLY_ONE) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagOneDouble == (int)(STRUCT_FLOAT_FIELD_ONLY_ONE | STRUCT_FIRST_FIELD_SIZE_IS8)) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagFloatInt == (int)STRUCT_FLOAT_FIELD_FIRST) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagFloatLong == (int)(STRUCT_FLOAT_FIELD_FIRST | STRUCT_SECOND_FIELD_SIZE_IS8)) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagDoubleInt == (int)(STRUCT_FLOAT_FIELD_FIRST | STRUCT_FIRST_FIELD_SIZE_IS8)) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagDoubleLong == (int)(CallDescrData__flagDoubleInt | STRUCT_SECOND_FIELD_SIZE_IS8)) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagIntFloat == (int)STRUCT_FLOAT_FIELD_SECOND) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagIntDouble == (int)(STRUCT_FLOAT_FIELD_SECOND | STRUCT_SECOND_FIELD_SIZE_IS8)) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagLongFloat == (int)(STRUCT_FLOAT_FIELD_SECOND | STRUCT_FIRST_FIELD_SIZE_IS8)) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagLongDouble == (int)(CallDescrData__flagLongFloat | STRUCT_SECOND_FIELD_SIZE_IS8)) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagFloatFloat == (int)STRUCT_FLOAT_FIELD_ONLY_TWO) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagFloatDouble == (int)(STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_SECOND_FIELD_SIZE_IS8)) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagDoubleFloat == (int)(STRUCT_FLOAT_FIELD_ONLY_TWO | STRUCT_FIRST_FIELD_SIZE_IS8)) -ASMCONSTANTS_C_ASSERT(CallDescrData__flagDoubleDouble == (int)(CallDescrData__flagDoubleFloat | STRUCT_SECOND_FIELD_SIZE_IS8)) +#define FpStruct__BothFloat 0b10 +ASMCONSTANTS_C_ASSERT(FpStruct__BothFloat == (int)FpStruct::BothFloat) #define CORINFO_NullReferenceException_ASM 0 ASMCONSTANTS_C_ASSERT( CORINFO_NullReferenceException_ASM diff --git a/src/coreclr/vm/riscv64/calldescrworkerriscv64.S b/src/coreclr/vm/riscv64/calldescrworkerriscv64.S index 2139787bee1490..607c523d66e18a 100644 --- a/src/coreclr/vm/riscv64/calldescrworkerriscv64.S +++ b/src/coreclr/vm/riscv64/calldescrworkerriscv64.S @@ -74,131 +74,34 @@ LOCAL_LABEL(CallDescrWorkerInternalReturnAddress): lw a3, CallDescrData__fpReturnSize(s1) - // Int return case beq a3, zero, LOCAL_LABEL(IntReturn) - // Struct with Float/Double field return case. - ori t4, zero, CallDescrData__flagOneFloat - beq t4, a3, LOCAL_LABEL(FloatReturn) + // Save struct returned according to hardware floating-point calling convention - ori t4, zero, CallDescrData__flagOneDouble - beq t4, a3, LOCAL_LABEL(DoubleReturn) + // Note: due to padding such structs may be larger than sizeof(CallDescrData::returnValue) (16 bytes) + // so just save the returned registers and let the routine copying the struct to the final memory location + // (CopyReturnedFpStructFromRegisters) worry about placing the fields as they were originally laid out. - ori t4, zero, CallDescrData__flagFloatInt - beq t4, a3, LOCAL_LABEL(FloatIntReturn) + fsd fa0, CallDescrData__returnValue(s1) // we have at least one floating field in fa0 - ori t4, zero, CallDescrData__flagDoubleInt - beq t4, a3, LOCAL_LABEL(DoubleIntReturn) + andi a3, a3, FpStruct__BothFloat + bne a3, zero, LOCAL_LABEL(SecondFieldFloatReturn) - ori t4, zero, CallDescrData__flagFloatLong - beq t4, a3, LOCAL_LABEL(FloatLongReturn) - - ori t4, zero, CallDescrData__flagDoubleLong - beq t4, a3, LOCAL_LABEL(DoubleLongReturn) - - ori t4, zero, CallDescrData__flagIntFloat - beq t4, a3, LOCAL_LABEL(IntFloatReturn) - - ori t4, zero, CallDescrData__flagLongFloat - beq t4, a3, LOCAL_LABEL(LongFloatReturn) - - ori t4, zero, CallDescrData__flagIntDouble - beq t4, a3, LOCAL_LABEL(IntDoubleReturn) - - ori t4, zero, CallDescrData__flagLongDouble - beq t4, a3, LOCAL_LABEL(LongDoubleReturn) - - ori t4, zero, CallDescrData__flagFloatFloat - beq t4, a3, LOCAL_LABEL(FloatFloatReturn) - - ori t4, zero, CallDescrData__flagDoubleFloat - beq t4, a3, LOCAL_LABEL(DoubleFloatReturn) - - ori t4, zero, CallDescrData__flagFloatDouble - beq t4, a3, LOCAL_LABEL(FloatDoubleReturn) - - ori t4, zero, CallDescrData__flagDoubleDouble - beq t4, a3, LOCAL_LABEL(DoubleDoubleReturn) - -LOCAL_LABEL(NotCorrectReturn): - sw ra, 0(zero) - EMIT_BREAKPOINT // Unreachable - -LOCAL_LABEL(FloatReturn): - fsw fa0, CallDescrData__returnValue(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(DoubleReturn): - fsd fa0, CallDescrData__returnValue(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(FloatIntReturn): - fsw fa0, CallDescrData__returnValue(s1) - sw a0, (CallDescrData__returnValue + 4)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(DoubleIntReturn): - fsd fa0, CallDescrData__returnValue(s1) - sw a0, (CallDescrData__returnValue + 8)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(FloatLongReturn): - fsw fa0, CallDescrData__returnValue(s1) - sd a0, (CallDescrData__returnValue + 8)(s1) + // The second returned field is integer (FpStruct::Float1st | FpStruct::Float2nd) + // Note: will go in here also for FpStruct::OnlyOne but storing a register of trash is probably faster than branching + sd a0, (CallDescrData__returnValue + 8)(s1) j LOCAL_LABEL(ReturnDone) -LOCAL_LABEL(DoubleLongReturn): - fsd fa0, CallDescrData__returnValue(s1) - sd a0, (CallDescrData__returnValue + 8)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(IntFloatReturn): - sw a0, CallDescrData__returnValue(s1) - fsw fa0, (CallDescrData__returnValue + 4)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(LongFloatReturn): - sd a0, CallDescrData__returnValue(s1) - fsw fa0, (CallDescrData__returnValue + 8)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(IntDoubleReturn): - sw a0, CallDescrData__returnValue(s1) - fsd fa0, (CallDescrData__returnValue + 8)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(LongDoubleReturn): - sd a0, CallDescrData__returnValue(s1) - fsd fa0, (CallDescrData__returnValue + 8)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(FloatFloatReturn): - fsw fa0, CallDescrData__returnValue(s1) - fsw fa1, (CallDescrData__returnValue + 4)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(DoubleFloatReturn): - fsd fa0, CallDescrData__returnValue(s1) - fsw fa1, (CallDescrData__returnValue + 8)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(FloatDoubleReturn): - fsw fa0, CallDescrData__returnValue(s1) - fsd fa1, (CallDescrData__returnValue + 8)(s1) - j LOCAL_LABEL(ReturnDone) - -LOCAL_LABEL(DoubleDoubleReturn): - fsd fa0, CallDescrData__returnValue(s1) - fsd fa1, (CallDescrData__returnValue + 8)(s1) +LOCAL_LABEL(SecondFieldFloatReturn): + fsd fa1, (CallDescrData__returnValue + 8)(s1) j LOCAL_LABEL(ReturnDone) LOCAL_LABEL(IntReturn): - // Save return value into retbuf for int + // Save struct returned according to integer calling convention sd a0, CallDescrData__returnValue(s1) sd a1, (CallDescrData__returnValue + 8)(s1) LOCAL_LABEL(ReturnDone): - EPILOG_STACK_RESTORE EPILOG_RESTORE_REG s1, 16 EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, 0x20 From c7e8a11adefbe127100eb95725cf494c735234e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 12 Jun 2024 15:28:35 +0200 Subject: [PATCH 48/60] Fix the rest of the reflection tests by properly determining whether an argument is passed by ref by looking at m_hasArgLocDescForStructInRegs in ArgIteratorTemplate::IsArgPassedByRef() --- src/coreclr/vm/callingconvention.h | 26 +++++++++++++------------ src/coreclr/vm/reflectioninvocation.cpp | 1 - 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index c9f80d1692bcbb..3a171fa0ad75ee 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -553,8 +553,15 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE { if (size <= ENREGISTERED_PARAMTYPE_MAXSIZE) return FALSE; - // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields or - // more padding, so make sure it's passed according to integer call conv which bounds struct to 16 bytes. + + // Structs larger than 16 bytes can still be passed in registers according to FP call conv if it has empty + // fields or more padding, so make sure it's passed according to integer call conv which bounds structs + // passed by value to 16 bytes. + // + // Note: if it's larger than 16 bytes and elegible for passing according to FP call conv, it still does not + // mean it will not be passed by reference. We need to know if there's enough free registers, otherwise + // it will fall back to passing according to integer calling convention (by implicit reference). + // (see the non-static overload of IsArgPassedByRef()) return MethodTable::GetRiscV64PassFpStructInRegistersInfo(th).flags == FpStruct::UseIntCallConv; } return FALSE; @@ -616,13 +623,10 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE if (m_argType == ELEMENT_TYPE_VALUETYPE) { _ASSERTE(!m_argTypeHandle.IsNull()); - #ifdef TARGET_RISCV64 - if (m_argSize <= ENREGISTERED_PARAMTYPE_MAXSIZE) - return FALSE; - return MethodTable::GetRiscV64PassFpStructInRegistersInfo(m_argTypeHandle).flags == FpStruct::UseIntCallConv; - #else - return (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE); - #endif + // On RISC-V structs larger than 16 bytes can still be passed in registers according to FP call conv if it + // has empty fields or more padding, so also check for m_hasArgLocDescForStructInRegs. + return (m_argSize > ENREGISTERED_PARAMTYPE_MAXSIZE) + RISCV64_ONLY(&& !m_hasArgLocDescForStructInRegs); } return FALSE; #else @@ -1879,10 +1883,8 @@ int ArgIteratorTemplate::GetNextOffset() { int regOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; - if ((info.flags & (FpStruct::BothFloat | FpStruct::OnlyOne)) - && (info.offset1st != 0 || info.offset2nd != FLOAT_REGISTER_SIZE)) + if (info.flags & (FpStruct::BothFloat | FpStruct::OnlyOne)) { - // Struct with one or two floating-point fields laid out differently than in TransitionBlock::FloatArgumentRegisters m_argLocDescForStructInRegs.Init(); m_argLocDescForStructInRegs.m_idxFloatReg = m_idxFPReg; m_argLocDescForStructInRegs.m_cFloatReg = cFPRegs; diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 19711cff4ebd60..8f33540a1091d1 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -632,7 +632,6 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, UINT structSize = argit.GetArgSize(); - bool needsStackCopy = false; ArgDestination argDest(pTransitionBlock, ofs, argit.GetArgLocDescForStructInRegs()); #ifdef ENREGISTERED_PARAMTYPE_MAXSIZE From 16b549f5c609cf472b983e1f2040e9e06a0f65aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 14 Jun 2024 09:46:49 +0200 Subject: [PATCH 49/60] Update crossgen2 C# version of ArgIterator to changes on the native side --- .../tools/Common/JitInterface/CorInfoTypes.cs | 10 +++++ .../ReadyToRun/ArgIterator.cs | 45 ++++++++++++++----- .../ReadyToRun/TransitionBlock.cs | 17 +++++-- src/coreclr/vm/callingconvention.h | 6 ++- 4 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 24739d5e9fd31d..076a7ba395e849 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1301,6 +1301,16 @@ public struct FpStructInRegistersInfo public uint offset1st; public uint offset2nd; + public uint GetSizeShift1st() + { + return (uint)((int)flags >> (int)FpStruct.PosSizeShift1st) & 0b11; + } + + public uint GetSizeShift2nd() + { + return (uint)((int)flags >> (int)FpStruct.PosSizeShift2nd) & 0b11; + } + public uint GetSize1st() { int shift = ((int)flags >> (int)FpStruct.PosSizeShift1st) & 0b11; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index 6cd2cc690c4148..c1552bc43dbf23 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -235,6 +235,8 @@ internal struct ArgLocDesc public int m_byteStackSize; // Stack size in bytes public uint m_floatFlags; // struct with two-fields can be passed by registers. + public FpStructInRegistersInfo m_structFields; // RISC-V - Struct field info when using floating-point register(s) + // Initialize to represent a non-placed argument (no register or stack slots referenced). public void Init() { @@ -245,6 +247,7 @@ public void Init() m_byteStackIndex = -1; m_byteStackSize = 0; m_floatFlags = 0; + m_structFields = new FpStructInRegistersInfo(); m_fRequires64BitAlignment = false; } @@ -593,6 +596,19 @@ public uint GetFPReturnSize() return _fpReturnSize; } + public FpStructInRegistersInfo GetReturnFpStructInRegistersInfo() + { + // WRAPPER_NO_CONTRACT; + Debug.Assert(_transitionBlock.Architecture == TargetArchitecture.RiscV64); + if (!_RETURN_FLAGS_COMPUTED) + ComputeReturnFlags(); + return new FpStructInRegistersInfo { + flags = (FpStruct)_fpReturnSize, + offset1st = _returnedFpFieldOffset1st, + offset2nd = _returnedFpFieldOffset2nd + }; + } + public bool IsArgPassedByRef() { // LIMITED_METHOD_CONTRACT; @@ -626,7 +642,15 @@ public bool IsArgPassedByRef() } return false; case TargetArchitecture.RiscV64: - return (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE) && _transitionBlock.IsArgPassedByRef(_argTypeHandle); + if (_argType == CorElementType.ELEMENT_TYPE_VALUETYPE) + { + Debug.Assert(!_argTypeHandle.IsNull()); + // On RISC-V structs larger than 16 bytes can still be passed in registers according to FP call conv if it + // has empty fields or more padding, so also check for _hasArgLocDescForStructInRegs. + return (_argSize > _transitionBlock.EnregisteredParamTypeMaxSize) + && !_hasArgLocDescForStructInRegs; + } + return false; default: throw new NotImplementedException(); } @@ -1499,7 +1523,7 @@ public int GetNextOffset() _argLocDescForStructInRegs.m_idxGenReg = _riscv64IdxGenReg; _argLocDescForStructInRegs.m_cGenReg = 1; - _argLocDescForStructInRegs.m_floatFlags = (uint)info.ToOldFlags(); + _argLocDescForStructInRegs.m_structFields = info; _hasArgLocDescForStructInRegs = true; int regOffset = ((info.flags & FpStruct.Float2nd) != 0) @@ -1514,18 +1538,13 @@ public int GetNextOffset() else if (cFPRegs + _riscv64IdxFPReg <= _transitionBlock.NumArgumentRegisters) { int regOffset = _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * _transitionBlock.FloatRegisterSize; - if (info.flags == (FpStruct.BothFloat | (FpStruct)(2 << (int)FpStruct.PosSizeShift1st) | (FpStruct)(2 << (int)FpStruct.PosSizeShift2nd))) + if ((info.flags & (FpStruct.BothFloat | FpStruct.OnlyOne)) != 0) { - // Struct with two single-float fields - Debug.Assert(info.GetSize1st() == sizeof(float)); - Debug.Assert(info.GetSize2nd() == sizeof(float)); _argLocDescForStructInRegs = new ArgLocDesc(); _argLocDescForStructInRegs.m_idxFloatReg = _riscv64IdxFPReg; _argLocDescForStructInRegs.m_cFloatReg = 2; - Debug.Assert(cFPRegs == 2); - Debug.Assert(argSize == 2 * sizeof(float)); - _argLocDescForStructInRegs.m_floatFlags = (uint)info.ToOldFlags(); + _argLocDescForStructInRegs.m_structFields = info; _hasArgLocDescForStructInRegs = true; } _riscv64IdxFPReg += cFPRegs; @@ -2048,8 +2067,14 @@ private void ForceSigWalk() private bool _SIZE_OF_ARG_STACK_COMPUTED; private bool _RETURN_FLAGS_COMPUTED; private bool _RETURN_HAS_RET_BUFFER; // Cached value of HasRetBuffArg + private uint _fpReturnSize; + // Offsets of fields returned according to RISC-V hardware floating-point calling convention + // (FpStruct flags are in _fpReturnSize) + private uint _returnedFpFieldOffset1st; + private uint _returnedFpFieldOffset2nd; + /* ITERATION_STARTED = 0x0001, SIZE_OF_ARG_STACK_COMPUTED = 0x0002, RETURN_FLAGS_COMPUTED = 0x0004, @@ -2079,7 +2104,7 @@ private void ComputeReturnFlags() if (!_RETURN_HAS_RET_BUFFER) { - _transitionBlock.ComputeReturnValueTreatment(type, thRetType, IsVarArg, out _RETURN_HAS_RET_BUFFER, out _fpReturnSize); + _transitionBlock.ComputeReturnValueTreatment(type, thRetType, IsVarArg, out _RETURN_HAS_RET_BUFFER, out _fpReturnSize, out _returnedFpFieldOffset1st, out _returnedFpFieldOffset2nd); } _RETURN_FLAGS_COMPUTED = true; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs index 231ff8ca04cd7f..cfa49e81f0e64e 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/TransitionBlock.cs @@ -304,10 +304,12 @@ public virtual bool IsVarArgPassedByRef(int size) return size > EnregisteredParamTypeMaxSize; } - public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetType, bool isVarArgMethod, out bool usesRetBuffer, out uint fpReturnSize) + public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetType, bool isVarArgMethod, out bool usesRetBuffer, out uint fpReturnSize, out uint returnedFpFieldOffset1st, out uint returnedFpFieldOffset2nd) { usesRetBuffer = false; fpReturnSize = 0; + returnedFpFieldOffset1st = 0; + returnedFpFieldOffset2nd = 0; switch (type) { @@ -398,7 +400,9 @@ public void ComputeReturnValueTreatment(CorElementType type, TypeHandle thRetTyp { TypeDesc td = thRetType.GetRuntimeTypeHandle(); FpStructInRegistersInfo info = RISCV64PassStructInRegister.GetRiscV64PassFpStructInRegistersInfo(td); - fpReturnSize = (uint)info.ToOldFlags() & 0xff; + fpReturnSize = (uint)info.flags; + returnedFpFieldOffset1st = info.offset1st; + returnedFpFieldOffset2nd = info.offset2nd; if (info.flags != FpStruct.UseIntCallConv || size <= EnregisteredReturnTypeIntegerMaxSize) break; @@ -725,7 +729,14 @@ public override bool IsArgPassedByRef(TypeHandle th) if (th.GetSize() <= EnregisteredParamTypeMaxSize) return false; - // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields or more padding + // Structs larger than 16 bytes can still be passed in registers according to FP call conv if it has empty + // fields or more padding, so make sure it's passed according to integer call conv which bounds structs + // passed by value to 16 bytes. + // + // Note: if it's larger than 16 bytes and elegible for passing according to FP call conv, it still does not + // mean it will not be passed by reference. We need to know if there's enough free registers, otherwise + // it will fall back to passing according to integer calling convention (by implicit reference). + // (see ArgIterator.IsArgPassedByRef()) TypeDesc td = th.GetRuntimeTypeHandle(); FpStructInRegistersInfo info = RISCV64PassStructInRegister.GetRiscV64PassFpStructInRegistersInfo(td); return (info.flags == FpStruct.UseIntCallConv); diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 3a171fa0ad75ee..0532f5c7e167da 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -374,6 +374,10 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE { WRAPPER_NO_CONTRACT; m_dwFlags = 0; +#if defined(TARGET_RISCV64) + m_returnedFpFieldOffsets[0] = 0; + m_returnedFpFieldOffsets[1] = 0; +#endif } UINT SizeOfArgStack() @@ -944,7 +948,7 @@ class ArgIteratorTemplate : public ARGITERATOR_BASE int m_nSizeOfArgStack; // Cached value of SizeOfArgStack #if defined(TARGET_RISCV64) // Offsets of fields returned according to hardware floating-point calling convention - // (FpStruct::Flags are embedded in m_dwFlags, accessible with GetFPReturnSize()) + // (FpStruct::Flags are embedded in m_dwFlags) unsigned m_returnedFpFieldOffsets[2]; #endif From 3389bcff0ac5c0a4d1b7d28f3f54d2420c60bb69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 14 Jun 2024 11:13:15 +0200 Subject: [PATCH 50/60] Fix copying structs returned by hardware floating-point calling convention to final destination in MethodDescCallSite::CallTargetWorker --- src/coreclr/vm/callhelpers.cpp | 51 +++++++++++++------------ src/coreclr/vm/callhelpers.h | 2 +- src/coreclr/vm/reflectioninvocation.cpp | 10 ++--- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index cfd940e81ed96c..66a07c0fb73b26 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -163,40 +163,29 @@ void DispatchCallDebuggerWrapper( } #if defined(TARGET_RISCV64) -void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStructInRegistersInfo info) +void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStructInRegistersInfo info, + bool handleGcRefs) { _ASSERTE(info.flags != FpStruct::UseIntCallConv); - unsigned reg1Offset, reg1Size; // offset and size of the field passed in the first saved register (fa0) - unsigned reg2Offset, reg2Size; // offset and size of the field passed in the second saved register (fa1 or a0) - if (info.flags & FpStruct::Float2nd) - { - reg1Offset = info.offset2nd; - reg2Offset = info.offset1st; - reg1Size = info.GetSize2nd(); - reg2Size = info.GetSize1st(); - } - else - { - reg1Offset = info.offset1st; - reg2Offset = info.offset2nd; - reg1Size = info.GetSize1st(); - reg2Size = info.GetSize2nd(); - } - memcpyNoGCRefs((char*)dest + reg1Offset, &returnRegs[0], reg1Size); + // Float2nd is the only case where returnRegs[0] (fa0) represents the second field, not the first. + UINT64* returnField1st = &returnRegs[(info.flags & FpStruct::Float2nd) ? 1 : 0]; + UINT64* returnField2nd = &returnRegs[(info.flags & FpStruct::Float2nd) ? 0 : 1]; + + memcpyNoGCRefs((char*)dest + info.offset1st, returnField1st, info.GetSize1st()); if ((info.flags & FpStruct::OnlyOne) == 0) { - char* pField = (char*)dest + reg2Offset; - if (info.flags & FpStruct::GcRef) + char* field2ndDest = (char*)dest + info.offset2nd; + if (handleGcRefs && (info.flags & FpStruct::GcRef)) { _ASSERTE(info.flags & (FpStruct::Float1st | FpStruct::Float2nd)); - _ASSERTE(reg2Size == TARGET_POINTER_SIZE); - memmoveGCRefs(pField, &returnRegs[1], TARGET_POINTER_SIZE); + _ASSERTE(info.GetSize2nd() == TARGET_POINTER_SIZE); + memmoveGCRefs(field2ndDest, returnField2nd, TARGET_POINTER_SIZE); } else { - memcpyNoGCRefs(pField, &returnRegs[1], reg2Size); + memcpyNoGCRefs(field2ndDest, returnField2nd, info.GetSize2nd()); } } } @@ -593,15 +582,27 @@ void MethodDescCallSite::CallTargetWorker(const ARG_SLOT *pArguments, ARG_SLOT * CallDescrWorkerWithHandler(&callDescrData); } +#ifdef FEATURE_HFA if (pvRetBuff != NULL) { memcpyNoGCRefs(pvRetBuff, &callDescrData.returnValue, sizeof(callDescrData.returnValue)); } +#endif // FEATURE_HFA if (pReturnValue != NULL) { - _ASSERTE((DWORD)cbReturnValue <= sizeof(callDescrData.returnValue)); - memcpyNoGCRefs(pReturnValue, &callDescrData.returnValue, cbReturnValue); + NOT_RISCV64(_ASSERTE((DWORD)cbReturnValue <= sizeof(callDescrData.returnValue));) +#if defined(TARGET_RISCV64) + if (callDescrData.fpReturnSize != FpStruct::UseIntCallConv) + { + FpStructInRegistersInfo info = m_argIt.GetReturnFpStructInRegistersInfo(); + CopyReturnedFpStructFromRegisters(pReturnValue, callDescrData.returnValue, info, false); + } + else +#endif // TARGET_RISCV64 + { + memcpyNoGCRefs(pReturnValue, &callDescrData.returnValue, cbReturnValue); + } #if !defined(HOST_64BIT) && BIGENDIAN { diff --git a/src/coreclr/vm/callhelpers.h b/src/coreclr/vm/callhelpers.h index 2291af9c80ff9d..36e85e12c3f95e 100644 --- a/src/coreclr/vm/callhelpers.h +++ b/src/coreclr/vm/callhelpers.h @@ -77,7 +77,7 @@ void * DispatchCallSimple( // On RISC-V structs returned according to floating-point calling convention may be larger than 16 bytes. // This routine copies such structs from 'returnRegs' containing fields (each in one register) as they are filled // by CallDescrWorkerInternal, to the final destination 'dest' respecting the struct's layout described in 'info'. -void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStructInRegistersInfo info); +void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStructInRegistersInfo info, bool handleGcRefs); #endif // TARGET_RISCV64 bool IsCerRootMethod(MethodDesc *pMD); diff --git a/src/coreclr/vm/reflectioninvocation.cpp b/src/coreclr/vm/reflectioninvocation.cpp index 8f33540a1091d1..a08f65aa0cde94 100644 --- a/src/coreclr/vm/reflectioninvocation.cpp +++ b/src/coreclr/vm/reflectioninvocation.cpp @@ -703,14 +703,14 @@ FCIMPL4(Object*, RuntimeMethodHandle::InvokeMethod, // we have allocated for this purpose. else if (!fHasRetBuffArg) { - #if defined(TARGET_RISCV64) - FpStructInRegistersInfo info = argit.GetReturnFpStructInRegistersInfo(); - if (info.flags != FpStruct::UseIntCallConv) +#if defined(TARGET_RISCV64) + if (callDescrData.fpReturnSize != FpStruct::UseIntCallConv) { - CopyReturnedFpStructFromRegisters(gc.retVal->GetData(), callDescrData.returnValue, info); + FpStructInRegistersInfo info = argit.GetReturnFpStructInRegistersInfo(); + CopyReturnedFpStructFromRegisters(gc.retVal->GetData(), callDescrData.returnValue, info, true); } else - #endif // TARGET_RISCV64 +#endif // TARGET_RISCV64 { CopyValueClass(gc.retVal->GetData(), &callDescrData.returnValue, gc.retVal->GetMethodTable()); } From 4d2e2cb2a246db71062a4b332ac4f7fd8011b29f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 09:05:15 +0200 Subject: [PATCH 51/60] Add signedness to integer field FpStructPassInRegistersInfo --- src/coreclr/inc/corinfo.h | 17 +++-- src/coreclr/jit/compiler.cpp | 5 +- .../tools/Common/JitInterface/CorInfoTypes.cs | 21 ++++-- .../RISCV64PassStructInRegister.cs | 52 +++++++++----- .../superpmi-shared/methodcontext.cpp | 5 +- src/coreclr/vm/callhelpers.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 71 +++++++++++-------- 7 files changed, 111 insertions(+), 62 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index bcf044a500a85d..e619da01d16b12 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -359,6 +359,11 @@ enum StructFloatFieldInfoFlags // Bitfields for FpStructInRegistersInfo::flags namespace FpStruct { + enum class IntKind + { + Signed, Unsigned, GcRef, GcByRef + }; + enum Flags { // Positions of bitfields @@ -368,8 +373,7 @@ namespace FpStruct PosSizeShift1st = 3, // 2 bits PosFloat2nd = 5, PosSizeShift2nd = 6, // 2 bits - PosGcRef = 8, - PosGcByRef = 9, + PosIntFieldKind = 8, // 2 bits UseIntCallConv = 0, // struct is passed according to integer calling convention @@ -380,10 +384,8 @@ namespace FpStruct SizeShift1st = 0b11 << PosSizeShift1st, // log2(size) of 1st field Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) SizeShift2nd = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field - GcRef = 1 << PosGcRef, // the integer field is a GC object reference - GcByRef = 1 << PosGcByRef, // the integer field is a GC interior pointer + IntFieldKind = 1 << PosIntFieldKind, // the kind of the integer field (FpStruct::IntKind) // Note: flags OnlyOne, BothFloat, Float1st, and Float2nd are mutually exclusive - // Note: flags GcRef, and ByRef are mutually exclusive and may only co-exist with either Float1st or Float2nd }; } @@ -426,6 +428,11 @@ struct FpStructInRegistersInfo return (flags & FpStruct::SizeShift2nd) == (3 << FpStruct::PosSizeShift2nd); } + FpStruct::IntKind GetIntFieldKind() const + { + return (FpStruct::IntKind)((flags >> FpStruct::PosIntFieldKind) & 0b11); + } + StructFloatFieldInfoFlags ToOldFlags() const { return StructFloatFieldInfoFlags( diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 4e56527f0a5ec8..7f1cf5cbd960e1 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8325,12 +8325,13 @@ void Compiler::GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, if ((info.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) { bool isInt1st = ((info.flags & FpStruct::Float1st) == 0); + FpStruct::IntKind kind = info.GetIntFieldKind(); var_types intType; - if ((info.flags & FpStruct::GcRef) != 0) + if (kind == FpStruct::IntKind::GcRef) { intType = TYP_REF; } - else if ((info.flags & FpStruct::GcByRef) != 0) + else if (kind == FpStruct::IntKind::GcByRef) { intType = TYP_BYREF; } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index b7a004b339a8f1..a2afff4b403e5f 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1263,6 +1263,11 @@ public enum StructFloatFieldInfoFlags }; + public enum FpStruct_IntKind + { + Signed, Unsigned, GcRef, GcByRef + } + // Bitfields for FpStructInRegistersInfo.flags [Flags] public enum FpStruct @@ -1274,8 +1279,7 @@ public enum FpStruct PosSizeShift1st = 3, // 2 bits PosFloat2nd = 5, PosSizeShift2nd = 6, // 2 bits - PosGcRef = 8, - PosGcByRef = 9, + PosIntFieldKind = 8, // 2 bits UseIntCallConv = 0, // struct is passed according to integer calling convention @@ -1286,11 +1290,9 @@ public enum FpStruct SizeShift1st = 0b11 << PosSizeShift1st, // log2(size) of 1st field Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) SizeShift2nd = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field - GcRef = 1 << PosGcRef, // the integer field is a GC object reference - GcByRef = 1 << PosGcByRef, // the integer field is a GC interior pointer + IntFieldKind = 1 << PosIntFieldKind, // the kind of the integer field (FpStruct::IntKind) // Note: flags OnlyOne, BothFloat, Float1st, and Float2nd are mutually exclusive - // Note: flags GcRef, and ByRef are mutually exclusive and may only co-exist with either Float1st or Float2nd - }; + } // On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating-point, // can be passed in registers according to hardware FP calling convention. FpStructInRegistersInfo represents @@ -1333,6 +1335,11 @@ public bool IsSize2nd8() return (flags & FpStruct.SizeShift2nd) == (FpStruct)(3 << (int)FpStruct.PosSizeShift2nd); } + public FpStruct_IntKind GetIntFieldKind() + { + return (FpStruct_IntKind)(((int)flags >> (int)FpStruct.PosIntFieldKind) & 0b11); + } + public StructFloatFieldInfoFlags ToOldFlags() { return @@ -1343,7 +1350,7 @@ public StructFloatFieldInfoFlags ToOldFlags() (IsSize1st8() ? StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | (IsSize2nd8() ? StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8 : 0); } - }; + } // DEBUGGER DATA public enum MappingTypes diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index 73d021d3ed9881..db15bc53328a08 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -130,10 +130,14 @@ private static uint GetRISCV64PassStructInRegisterFlags(TypeDesc td) private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo info, int index, - bool isFloating, bool isGcRef, bool isGcByRef, uint size, uint offset) + bool isFloating, FpStruct_IntKind intKind, uint size, uint offset) { Debug.Assert(index < 2); - Debug.Assert(!isFloating || size == sizeof(float) || size == sizeof(double)); + if (isFloating) + { + Debug.Assert(size == sizeof(float) || size == sizeof(double)); + Debug.Assert(intKind == FpStruct_IntKind.Signed); + } Debug.Assert(size >= 1 && size <= 8); Debug.Assert((size & (size - 1)) == 0, "size needs to be a power of 2"); @@ -145,10 +149,7 @@ private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo "1st flags need to be 2nd flags shifted by typeSize"); int type = (Convert.ToInt32(isFloating) << (int)PosFloat1st) | (sizeShift << (int)PosSizeShift1st); - info.flags |= (FpStruct)( - (type << (typeSize * index)) | - (Convert.ToInt32(isGcRef) << (int)PosGcRef) | - (Convert.ToInt32(isGcByRef) << (int)PosGcByRef)); + info.flags |= (FpStruct)((type << (typeSize * index)) | ((int)intKind << (int)PosIntFieldKind)); (index == 0 ? ref info.offset1st : ref info.offset2nd) = offset; } @@ -211,12 +212,26 @@ private static bool FlattenFields(TypeDesc td, uint offset, ref FpStructInRegist if (typeIndex >= 2) return false; // too many fields + bool isFloating = category is TypeFlags.Single or TypeFlags.Double; + bool isSignedInt = category is + TypeFlags.SByte or + TypeFlags.Int16 or + TypeFlags.Int32 or + TypeFlags.Int64 or + TypeFlags.IntPtr; + bool isGcRef = category is + TypeFlags.Class or + TypeFlags.Interface or + TypeFlags.Array or + TypeFlags.SzArray; + + FpStruct_IntKind intKind = + isGcRef ? FpStruct_IntKind.GcRef : + (category is TypeFlags.ByRef) ? FpStruct_IntKind.GcByRef : + (isSignedInt || isFloating) ? FpStruct_IntKind.Signed : FpStruct_IntKind.Unsigned; + SetFpStructInRegistersInfoField(ref info, typeIndex++, - (category is TypeFlags.Single or TypeFlags.Double), - (category is TypeFlags.Class or TypeFlags.Interface or TypeFlags.Array or TypeFlags.SzArray), - (category is TypeFlags.ByRef), - (uint)field.FieldType.GetElementSize().AsInt, - offset + (uint)field.Offset.AsInt); + isFloating, intKind, (uint)field.FieldType.GetElementSize().AsInt, offset + (uint)field.Offset.AsInt); } else { @@ -268,7 +283,7 @@ private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl Debug.Assert(info.offset2nd == 0); info.flags ^= (Float1st | OnlyOne); // replace Float1st with OnlyOne } - Debug.Assert(nFields == 1 + Convert.ToInt32((info.flags & OnlyOne) == 0)); + Debug.Assert(nFields == ((info.flags & OnlyOne) != 0 ? 1 : 2)); FpStruct floatFlags = info.flags & (OnlyOne | BothFloat | Float1st | Float2nd); Debug.Assert(floatFlags != 0); Debug.Assert(((uint)floatFlags & ((uint)floatFlags - 1)) == 0, @@ -281,13 +296,18 @@ private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl } Debug.Assert(info.offset1st + info.GetSize1st() <= td.GetElementSize().AsInt); Debug.Assert(info.offset2nd + info.GetSize2nd() <= td.GetElementSize().AsInt); - if ((info.flags & (GcRef | GcByRef)) != 0) + if (info.GetIntFieldKind() != FpStruct_IntKind.Signed) { - Debug.Assert((info.flags ^ (GcRef | GcByRef)) != 0, "either Ref or ByRef, not both"); Debug.Assert((info.flags & (Float1st | Float2nd)) != 0); - Debug.Assert((info.flags & Float1st) != 0 || (info.IsSize1st8() && IsAligned(info.offset1st, TARGET_POINTER_SIZE))); - Debug.Assert((info.flags & Float2nd) != 0 || (info.IsSize2nd8() && IsAligned(info.offset2nd, TARGET_POINTER_SIZE))); + if (info.GetIntFieldKind() >= FpStruct_IntKind.GcRef) + { + Debug.Assert((info.flags & Float2nd) != 0 + ? (info.IsSize1st8() && IsAligned(info.offset1st, TARGET_POINTER_SIZE)) + : (info.IsSize2nd8() && IsAligned(info.offset2nd, TARGET_POINTER_SIZE))); + } } + if ((info.flags & (OnlyOne | BothFloat)) != 0) + Debug.Assert(info.GetIntFieldKind() == FpStruct_IntKind.Signed); return info; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index f5ad480265ccd0..00ccf1345f0b9e 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6374,14 +6374,13 @@ void MethodContext::recGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDL void MethodContext::dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpStructInRegistersInfo value) { printf("GetRiscV64PassFpStructInRegistersInfo key %016" PRIX64 " value-%#02x-" - "{OnlyOne=%i, BothFloat=%i, Float1st=%i, Size1st=%u, Float2nd=%i, Size2nd=%u, GcRef=%i, GcByRef=%i, offset1st=%u, offset2nd=%u}", + "{OnlyOne=%i, BothFloat=%i, Float1st=%i, Size1st=%u, Float2nd=%i, Size2nd=%u, IntFieldKind=%i, offset1st=%u, offset2nd=%u}", key, value.flags, (value.flags & FpStruct::OnlyOne) != 0, (value.flags & FpStruct::BothFloat) != 0, (value.flags & FpStruct::Float1st) != 0, value.GetSize1st(), (value.flags & FpStruct::Float2nd) != 0, value.GetSize2nd(), - (value.flags & FpStruct::GcRef) != 0, - (value.flags & FpStruct::GcByRef) != 0, + (int)value.GetIntFieldKind(), value.offset1st, value.offset2nd); } diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index c984886676a1b5..69b33b5514f1f9 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -173,7 +173,7 @@ void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStruc if ((info.flags & FpStruct::OnlyOne) == 0) { char* field2ndDest = (char*)dest + info.offset2nd; - if (handleGcRefs && (info.flags & FpStruct::GcRef)) + if (handleGcRefs && info.GetIntFieldKind() == FpStruct::IntKind::GcRef) { _ASSERTE(info.flags & (FpStruct::Float1st | FpStruct::Float2nd)); _ASSERTE(info.GetSize2nd() == TARGET_POINTER_SIZE); diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index fb0ff235971341..7796cbbbe434d9 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2928,10 +2928,14 @@ static int GetRiscV64PassStructInRegisterFlags(TypeHandle th) #if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int index, - bool isFloating, bool isGcRef, bool isGcByRef, unsigned size, uint32_t offset) + bool isFloating, FpStruct::IntKind intKind, unsigned size, uint32_t offset) { assert(index < 2); - assert(!isFloating || size == sizeof(float) || size == sizeof(double)); + if (isFloating) + { + assert(size == sizeof(float) || size == sizeof(double)); + assert(intKind == FpStruct::IntKind::Signed); + } assert(size >= 1 && size <= 8); assert((size & (size - 1)) == 0); // size needs to be a power of 2 @@ -2944,10 +2948,7 @@ static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int i "1st flags need to be 2nd flags shifted by typeSize"); int type = (isFloating << PosFloat1st) | (sizeShift << PosSizeShift1st); - info.flags = FpStruct::Flags(info.flags | - (type << (typeSize * index)) | - (isGcRef << PosGcRef) | - (isGcByRef << PosGcByRef)); + info.flags = FpStruct::Flags(info.flags | (type << (typeSize * index)) | ((int)intKind << PosIntFieldKind)); (index == 0 ? info.offset1st : info.offset2nd) = offset; } @@ -3038,13 +3039,23 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf return false; } + bool isFloating = CorTypeInfo::IsFloat_NoThrow(type); + bool isSignedInt = ( + type == ELEMENT_TYPE_I1 || + type == ELEMENT_TYPE_I2 || + type == ELEMENT_TYPE_I4 || + type == ELEMENT_TYPE_I8 || + type == ELEMENT_TYPE_I); CorInfoGCType gcType = CorTypeInfo::GetGCType_NoThrow(type); + + FpStruct::IntKind intKind = + (gcType == TYPE_GC_REF) ? FpStruct::IntKind::GcRef : + (gcType == TYPE_GC_BYREF) ? FpStruct::IntKind::GcByRef : + (isSignedInt || isFloating) ? FpStruct::IntKind::Signed : FpStruct::IntKind::Unsigned; + SetFpStructInRegistersInfoField(info, typeIndex++, - CorTypeInfo::IsFloat_NoThrow(type), - (gcType == TYPE_GC_REF), - (gcType == TYPE_GC_BYREF), - CorTypeInfo::Size_NoThrow(type), - offset + fields[i].GetOffset()); + isFloating, intKind, CorTypeInfo::Size_NoThrow(type), offset + fields[i].GetOffset()); + LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", nestingLevel * 4, "", fields[i].GetDebugName(), fields[i].GetOffset(), fields[i].GetOffset() + fields[i].GetSize(), CorTypeInfo::GetName(type))); @@ -3119,8 +3130,7 @@ static bool FlattenFields(TypeHandle th, uint32_t offset, FpStructInRegistersInf SetFpStructInRegistersInfoField(info, typeIndex++, (category == NativeFieldCategory::FLOAT), - false, - false, + FpStruct::IntKind::Signed, // NativeFieldDescriptor doesn't save signedness, TODO: should it? fields[i].NativeSize(), offset + fields[i].GetExternalOffset()); LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * found field %s [%i..%i), type: %s\n", @@ -3172,10 +3182,10 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan assert(info.offset2nd == 0); info.flags = FpStruct::Flags(info.flags ^ (Float1st | OnlyOne)); // replace Float1st with OnlyOne } - assert(nFields == 1+ !(info.flags & OnlyOne)); + assert(nFields == ((info.flags & OnlyOne) != 0 ? 1 : 2)); int floatFlags = info.flags & (OnlyOne | BothFloat | Float1st | Float2nd); assert(floatFlags != 0); - assert((floatFlags & (floatFlags - 1)) == 0); // there can be only one of the above flags + assert((floatFlags & (floatFlags - 1)) == 0); // there can be only one of (OnlyOne | BothFloat | Float1st | Float2nd) if (nFields == 2) { unsigned end1st = info.offset1st + info.GetSize1st(); @@ -3184,27 +3194,32 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan } assert(info.offset1st + info.GetSize1st() <= th.GetSize()); assert(info.offset2nd + info.GetSize2nd() <= th.GetSize()); - if (info.flags & (GcRef | GcByRef)) + if (info.GetIntFieldKind() != FpStruct::IntKind::Signed) { - assert(info.flags ^ (GcRef | GcByRef)); // either Ref or ByRef, not both assert(info.flags & (Float1st | Float2nd)); - assert((info.flags & Float1st) || (info.IsSize1st8() && IS_ALIGNED(info.offset1st, TARGET_POINTER_SIZE))); - assert((info.flags & Float2nd) || (info.IsSize2nd8() && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); + if (info.GetIntFieldKind() >= FpStruct::IntKind::GcRef) + { + assert((info.flags & Float2nd) != 0 + ? (info.IsSize1st8() && IS_ALIGNED(info.offset1st, TARGET_POINTER_SIZE)) + : (info.IsSize2nd8() && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); + } } + if (info.flags & (OnlyOne | BothFloat)) + assert(info.GetIntFieldKind() == FpStruct::IntKind::Signed); + static const char* intKindNames[] = { "Signed", "Unsigned", "GcRef", "GcByRef" }; LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: " - "struct %s (%u bytes) can be passed with floating-point calling convention, flags=%#02x, " - "%s, sizes={%u, %u}, GcRef=%i, GcByRef=%i, offsets={%u, %u}\n", + "struct %s (%u bytes) can be passed with floating-point calling convention, flags=%#02x; " + "%s, sizes={%u, %u}, offsets={%u, %u}, IntFieldKind=%s\n", (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize(), info.flags, - ( (info.flags & FpStruct::OnlyOne) ? "OnlyFloat" - : (info.flags & FpStruct::BothFloat) ? "BothFloat" - : (info.flags & FpStruct::Float1st) ? "Float1st" + ( (info.flags & OnlyOne) ? "OnlyFloat" + : (info.flags & BothFloat) ? "BothFloat" + : (info.flags & Float1st) ? "Float1st" : "Float2nd" ), info.GetSize1st(), info.GetSize2nd(), - (info.flags & FpStruct::GcRef), - (info.flags & FpStruct::GcByRef), - info.offset1st, info.offset2nd)); - + info.offset1st, info.offset2nd, + intKindNames[(int)info.GetIntFieldKind()] + )); return info; } From 907ac953323d7965221e28b7e1b47d21359afaae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 09:05:15 +0200 Subject: [PATCH 52/60] Better flag names --- src/coreclr/inc/corinfo.h | 34 +++++++------- src/coreclr/jit/compiler.cpp | 8 ++-- src/coreclr/jit/lclvars.cpp | 2 +- src/coreclr/jit/morph.cpp | 10 ++--- src/coreclr/jit/targetriscv64.cpp | 6 +-- .../tools/Common/JitInterface/CorInfoTypes.cs | 30 ++++++------- .../RISCV64PassStructInRegister.cs | 36 ++++++++------- .../ReadyToRun/ArgIterator.cs | 4 +- .../superpmi-shared/methodcontext.cpp | 6 +-- src/coreclr/vm/argdestination.h | 12 ++--- src/coreclr/vm/callhelpers.cpp | 8 ++-- src/coreclr/vm/callingconvention.h | 4 +- src/coreclr/vm/comdelegate.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 44 ++++++++++--------- 14 files changed, 107 insertions(+), 99 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index e619da01d16b12..e583a9e4dc0edd 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -369,23 +369,23 @@ namespace FpStruct // Positions of bitfields PosOnlyOne = 0, PosBothFloat = 1, - PosFloat1st = 2, - PosSizeShift1st = 3, // 2 bits - PosFloat2nd = 5, + PosFloatInt = 2, + PosIntFloat = 3, + PosSizeShift1st = 4, // 2 bits PosSizeShift2nd = 6, // 2 bits PosIntFieldKind = 8, // 2 bits UseIntCallConv = 0, // struct is passed according to integer calling convention // The bitfields - OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point - BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point - Float1st = 1 << PosFloat1st, // has two fields, 1st is floating (and 2nd is integer) - SizeShift1st = 0b11 << PosSizeShift1st, // log2(size) of 1st field - Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) - SizeShift2nd = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field - IntFieldKind = 1 << PosIntFieldKind, // the kind of the integer field (FpStruct::IntKind) - // Note: flags OnlyOne, BothFloat, Float1st, and Float2nd are mutually exclusive + OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point + BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point + FloatInt = 1 << PosFloatInt, // has two fields, 1st is floating and 2nd is integer + IntFloat = 1 << PosIntFloat, // has two fields, 2nd is floating and 1st is integer + SizeShift1stMask = 0b11 << PosSizeShift1st, // log2(size) of 1st field + SizeShift2ndMask = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field + IntFieldKindMask = 0b11 << PosIntFieldKind, // the kind of the integer field (FpStruct::IntKind) + // Note: flags OnlyOne, BothFloat, FloatInt, and IntFloat are mutually exclusive }; } @@ -420,12 +420,12 @@ struct FpStructInRegistersInfo bool IsSize1st8() const { - return (flags & FpStruct::SizeShift1st) == (3 << FpStruct::PosSizeShift1st); + return (flags & FpStruct::SizeShift1stMask) == (3 << FpStruct::PosSizeShift1st); } bool IsSize2nd8() const { - return (flags & FpStruct::SizeShift2nd) == (3 << FpStruct::PosSizeShift2nd); + return (flags & FpStruct::SizeShift2ndMask) == (3 << FpStruct::PosSizeShift2nd); } FpStruct::IntKind GetIntFieldKind() const @@ -438,8 +438,8 @@ struct FpStructInRegistersInfo return StructFloatFieldInfoFlags( ((flags & FpStruct::OnlyOne) ? STRUCT_FLOAT_FIELD_ONLY_ONE : 0) | ((flags & FpStruct::BothFloat) ? STRUCT_FLOAT_FIELD_ONLY_TWO : 0) | - ((flags & FpStruct::Float1st) ? STRUCT_FLOAT_FIELD_FIRST : 0) | - ((flags & FpStruct::Float2nd) ? STRUCT_FLOAT_FIELD_SECOND : 0) | + ((flags & FpStruct::FloatInt) ? STRUCT_FLOAT_FIELD_FIRST : 0) | + ((flags & FpStruct::IntFloat) ? STRUCT_FLOAT_FIELD_SECOND : 0) | (IsSize1st8() ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | (IsSize2nd8() ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0)); } @@ -453,8 +453,8 @@ struct FpStructInRegistersInfo FpStruct::Flags( ((flags & STRUCT_FLOAT_FIELD_ONLY_ONE) ? FpStruct::OnlyOne : 0) | ((flags & STRUCT_FLOAT_FIELD_ONLY_TWO) ? FpStruct::BothFloat : 0) | - ((flags & STRUCT_FLOAT_FIELD_FIRST) ? FpStruct::Float1st : 0) | - ((flags & STRUCT_FLOAT_FIELD_SECOND) ? FpStruct::Float2nd : 0) | + ((flags & STRUCT_FLOAT_FIELD_FIRST) ? FpStruct::FloatInt : 0) | + ((flags & STRUCT_FLOAT_FIELD_SECOND) ? FpStruct::IntFloat : 0) | (sizeShift1st << FpStruct::PosSizeShift1st) | (hasTwo ? (sizeShift2nd << FpStruct::PosSizeShift2nd) : 0) // No GC ref info in old flags diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7f1cf5cbd960e1..7842957ffb59fd 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8316,15 +8316,15 @@ void Compiler::GetStructTypeOffset( // void Compiler::GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, var_types* type1st, var_types* type2nd) { - if ((info.flags & (FpStruct::BothFloat | FpStruct::Float1st | FpStruct::OnlyOne)) != 0) + if ((info.flags & (FpStruct::BothFloat | FpStruct::FloatInt | FpStruct::OnlyOne)) != 0) *type1st = info.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; - if ((info.flags & (FpStruct::BothFloat | FpStruct::Float2nd)) != 0) + if ((info.flags & (FpStruct::BothFloat | FpStruct::IntFloat)) != 0) *type2nd = info.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; - if ((info.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) + if ((info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) != 0) { - bool isInt1st = ((info.flags & FpStruct::Float1st) == 0); + bool isInt1st = ((info.flags & FpStruct::FloatInt) == 0); FpStruct::IntKind kind = info.GetIntFieldKind(); var_types intType; if (kind == FpStruct::IntKind::GcRef) diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index ff5a13abb6dea1..d16a4221078099 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -920,7 +920,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un int floatNum = ((fpInfo.flags & FpStruct::BothFloat) != 0) ? 2 : 1; canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, floatNum); - if (canPassArgInRegisters && ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0)) + if (canPassArgInRegisters && ((fpInfo.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) != 0)) canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, 1); Compiler::GetTypesFromFpStructInRegistersInfo(fpInfo, &argRegTypeInStruct1, &argRegTypeInStruct2); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index ecf69a83c6b200..39e2f27a2876f2 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2446,7 +2446,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call passUsingFloatRegs = comp->compFloatingPointUsed = true; if ((fpInfo.flags & FpStruct::OnlyOne) == 0) { - assert((fpInfo.flags & (FpStruct::BothFloat | FpStruct::Float1st | FpStruct::Float2nd)) != 0); + assert((fpInfo.flags & (FpStruct::BothFloat | FpStruct::FloatInt | FpStruct::IntFloat)) != 0); // On LoongArch64 and RISC-V64, "getPrimitiveTypeForStruct" will incorrectly return "TYP_LONG" // for "struct { float, float }", and retyping to a primitive here will cause the // multi-reg morphing to not kick in (the struct in question needs to be passed in @@ -2612,7 +2612,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call if (isStructArg) { - if ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0 && passUsingFloatRegs) + if ((fpInfo.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) != 0 && passUsingFloatRegs) { passUsingFloatRegs = isRegArg = intArgRegNum < maxRegArgs; } @@ -2629,14 +2629,14 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call { nextOtherRegNum = genMapFloatRegArgNumToRegNum(nextFltArgRegNum + 1); } - else if ((fpInfo.flags & FpStruct::Float2nd) != 0) + else if ((fpInfo.flags & FpStruct::IntFloat) != 0) { assert(size == 1); size = 2; passUsingFloatRegs = false; nextOtherRegNum = genMapFloatRegArgNumToRegNum(nextFltArgRegNum); } - else if ((fpInfo.flags & FpStruct::Float1st) != 0) + else if ((fpInfo.flags & FpStruct::FloatInt) != 0) { assert(size == 1); size = 2; @@ -2894,7 +2894,7 @@ void CallArgs::AddFinalArgsAndDetermineABIInfo(Compiler* comp, GenTreeCall* call structBaseType = arg.AbiInfo.StructFloatFieldType[0]; unsigned fltRegs = ((fpInfo.flags & FpStruct::BothFloat) != 0) ? 2 : 1; - unsigned intRegs = ((fpInfo.flags & (FpStruct::Float1st | FpStruct::Float2nd)) != 0) ? 1 : 0; + unsigned intRegs = ((fpInfo.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) != 0) ? 1 : 0; fltArgRegNum += fltRegs; intArgRegNum += intRegs; } diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index a1302e519ed1d9..c771611cb80120 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -80,7 +80,7 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, } else if (info.flags != UseIntCallConv) { - assert((info.flags & (Float1st | Float2nd)) != 0); + assert((info.flags & (FloatInt | IntFloat)) != 0); floatFields = 1; intFields = 1; } @@ -122,8 +122,8 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, assert(info.flags != UseIntCallConv); assert((info.flags & OnlyOne) == 0); - bool isFirstFloat = (info.flags & (BothFloat | Float1st)) != 0; - bool isSecondFloat = (info.flags & (BothFloat | Float2nd)) != 0; + bool isFirstFloat = (info.flags & (BothFloat | FloatInt)) != 0; + bool isSecondFloat = (info.flags & (BothFloat | IntFloat)) != 0; assert(isFirstFloat || isSecondFloat); regNumber firstReg = (isFirstFloat ? m_floatRegs : m_intRegs).Dequeue(); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index a2afff4b403e5f..5c497988e037a9 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1275,23 +1275,23 @@ public enum FpStruct // Positions of bitfields PosOnlyOne = 0, PosBothFloat = 1, - PosFloat1st = 2, - PosSizeShift1st = 3, // 2 bits - PosFloat2nd = 5, + PosFloatInt = 2, + PosIntFloat = 3, + PosSizeShift1st = 4, // 2 bits PosSizeShift2nd = 6, // 2 bits PosIntFieldKind = 8, // 2 bits UseIntCallConv = 0, // struct is passed according to integer calling convention // The bitfields - OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point - BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point - Float1st = 1 << PosFloat1st, // has two fields, 1st is floating (and 2nd is integer) - SizeShift1st = 0b11 << PosSizeShift1st, // log2(size) of 1st field - Float2nd = 1 << PosFloat2nd, // has two fields, 2nd is floating (and 1st is integer) - SizeShift2nd = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field - IntFieldKind = 1 << PosIntFieldKind, // the kind of the integer field (FpStruct::IntKind) - // Note: flags OnlyOne, BothFloat, Float1st, and Float2nd are mutually exclusive + OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point + BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point + FloatInt = 1 << PosFloatInt, // has two fields, 1st is floating and 2nd is integer + IntFloat = 1 << PosIntFloat, // has two fields, 2nd is floating and 1st is integer + SizeShift1stMask = 0b11 << PosSizeShift1st, // log2(size) of 1st field + SizeShift2ndMask = 0b11 << PosSizeShift2nd, // log2(size) of 2nd field + IntFieldKindMask = 0b11 << PosIntFieldKind, // the kind of the integer field (FpStruct::IntKind) + // Note: flags OnlyOne, BothFloat, FloatInt, and IntFloat are mutually exclusive } // On RISC-V and LoongArch a struct with up to two non-empty fields, at least one of them floating-point, @@ -1327,12 +1327,12 @@ public uint GetSize2nd() public bool IsSize1st8() { - return (flags & FpStruct.SizeShift1st) == (FpStruct)(3 << (int)FpStruct.PosSizeShift1st); + return (flags & FpStruct.SizeShift1stMask) == (FpStruct)(3 << (int)FpStruct.PosSizeShift1st); } public bool IsSize2nd8() { - return (flags & FpStruct.SizeShift2nd) == (FpStruct)(3 << (int)FpStruct.PosSizeShift2nd); + return (flags & FpStruct.SizeShift2ndMask) == (FpStruct)(3 << (int)FpStruct.PosSizeShift2nd); } public FpStruct_IntKind GetIntFieldKind() @@ -1345,8 +1345,8 @@ public StructFloatFieldInfoFlags ToOldFlags() return ((flags & FpStruct.OnlyOne) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_ONE : 0) | ((flags & FpStruct.BothFloat) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO : 0) | - ((flags & FpStruct.Float1st) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST : 0) | - ((flags & FpStruct.Float2nd) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND : 0) | + ((flags & FpStruct.FloatInt) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST : 0) | + ((flags & FpStruct.IntFloat) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND : 0) | (IsSize1st8() ? StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | (IsSize2nd8() ? StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8 : 0); } diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index db15bc53328a08..8e824eb3d3d322 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -137,6 +137,8 @@ private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo { Debug.Assert(size == sizeof(float) || size == sizeof(double)); Debug.Assert(intKind == FpStruct_IntKind.Signed); + Debug.Assert((int)FpStruct_IntKind.Signed == 0, + "IntKind for floating fields should not clobber IntKind for int fields"); } Debug.Assert(size >= 1 && size <= 8); @@ -144,12 +146,14 @@ private static void SetFpStructInRegistersInfoField(ref FpStructInRegistersInfo const int sizeShiftLUT = (0 << (1*2)) | (1 << (2*2)) | (2 << (4*2)) | (3 << (8*2)); int sizeShift = (sizeShiftLUT >> ((int)size * 2)) & 0b11; - const int typeSize = (int)PosFloat2nd - (int)PosFloat1st; - Debug.Assert((Float2nd | SizeShift2nd) == (FpStruct)((uint)(Float1st | SizeShift1st) << typeSize), - "1st flags need to be 2nd flags shifted by typeSize"); + // Use FloatInt and IntFloat as marker flags for 1st and 2nd field respectively being floating. + // Fix to real flags (with OnlyOne and BothFloat) after flattening is complete. + Debug.Assert((int)PosIntFloat == (int)PosFloatInt + 1, "FloatInt and IntFloat need to be adjacent"); + Debug.Assert((int)PosSizeShift2nd == (int)PosSizeShift1st + 2, "SizeShift1st and 2nd need to be adjacent"); + int floatFlag = Convert.ToInt32(isFloating) << ((int)PosFloatInt + index); + int sizeShiftMask = sizeShift << ((int)PosSizeShift1st + 2 * index); - int type = (Convert.ToInt32(isFloating) << (int)PosFloat1st) | (sizeShift << (int)PosSizeShift1st); - info.flags |= (FpStruct)((type << (typeSize * index)) | ((int)intKind << (int)PosIntFieldKind)); + info.flags |= (FpStruct)(floatFlag | sizeShiftMask | ((int)intKind << (int)PosIntFieldKind)); (index == 0 ? ref info.offset1st : ref info.offset2nd) = offset; } @@ -176,7 +180,7 @@ private static bool HandleInlineArray(int elementTypeIndex, int nElements, ref F Debug.Assert(typeIndex == 1); // duplicate the array element info - const int typeSize = (int)PosFloat2nd - (int)PosFloat1st; + const int typeSize = (int)PosIntFloat - (int)PosFloatInt; info.flags |= (FpStruct)((int)info.flags << typeSize); info.offset2nd = info.offset1st + info.GetSize1st(); } @@ -266,28 +270,28 @@ private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl if (!FlattenFields(td, 0, ref info, ref nFields)) return new FpStructInRegistersInfo{}; - if ((info.flags & (Float1st | Float2nd)) == 0) + if ((info.flags & (FloatInt | IntFloat)) == 0) return new FpStructInRegistersInfo{}; // struct has no floating fields Debug.Assert(nFields == 1 || nFields == 2); - if ((info.flags & (Float1st | Float2nd)) == (Float1st | Float2nd)) + if ((info.flags & (FloatInt | IntFloat)) == (FloatInt | IntFloat)) { Debug.Assert(nFields == 2); - info.flags ^= (Float1st | Float2nd | BothFloat); // replace Float(1st|2nd) with BothFloat + info.flags ^= (FloatInt | IntFloat | BothFloat); // replace (FloatInt | IntFloat) with BothFloat } else if (nFields == 1) { - Debug.Assert((info.flags & Float1st) != 0); - Debug.Assert((info.flags & (Float2nd | SizeShift2nd)) == 0); + Debug.Assert((info.flags & FloatInt) != 0); + Debug.Assert((info.flags & (IntFloat | SizeShift2ndMask)) == 0); Debug.Assert(info.offset2nd == 0); - info.flags ^= (Float1st | OnlyOne); // replace Float1st with OnlyOne + info.flags ^= (FloatInt | OnlyOne); // replace FloatInt with OnlyOne } Debug.Assert(nFields == ((info.flags & OnlyOne) != 0 ? 1 : 2)); - FpStruct floatFlags = info.flags & (OnlyOne | BothFloat | Float1st | Float2nd); + FpStruct floatFlags = info.flags & (OnlyOne | BothFloat | FloatInt | IntFloat); Debug.Assert(floatFlags != 0); Debug.Assert(((uint)floatFlags & ((uint)floatFlags - 1)) == 0, - "there can be only one of (OnlyOne | BothFloat | Float1st | Float2nd)"); + "there can be only one of (OnlyOne | BothFloat | FloatInt | IntFloat)"); if (nFields == 2) { uint end1st = info.offset1st + info.GetSize1st(); @@ -298,10 +302,10 @@ private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl Debug.Assert(info.offset2nd + info.GetSize2nd() <= td.GetElementSize().AsInt); if (info.GetIntFieldKind() != FpStruct_IntKind.Signed) { - Debug.Assert((info.flags & (Float1st | Float2nd)) != 0); + Debug.Assert((info.flags & (FloatInt | IntFloat)) != 0); if (info.GetIntFieldKind() >= FpStruct_IntKind.GcRef) { - Debug.Assert((info.flags & Float2nd) != 0 + Debug.Assert((info.flags & IntFloat) != 0 ? (info.IsSize1st8() && IsAligned(info.offset1st, TARGET_POINTER_SIZE)) : (info.IsSize2nd8() && IsAligned(info.offset2nd, TARGET_POINTER_SIZE))); } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs index c1552bc43dbf23..13e63729163089 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRun/ArgIterator.cs @@ -1510,7 +1510,7 @@ public int GetNextOffset() if (cFPRegs > 0) { // Pass according to hardware floating-point calling convention iff the argument can be fully enregistered - if ((info.flags & (FpStruct.Float1st | FpStruct.Float2nd)) != 0) + if ((info.flags & (FpStruct.FloatInt | FpStruct.IntFloat)) != 0) { Debug.Assert(cFPRegs == 1); @@ -1526,7 +1526,7 @@ public int GetNextOffset() _argLocDescForStructInRegs.m_structFields = info; _hasArgLocDescForStructInRegs = true; - int regOffset = ((info.flags & FpStruct.Float2nd) != 0) + int regOffset = ((info.flags & FpStruct.IntFloat) != 0) ? _transitionBlock.OffsetOfArgumentRegisters + _riscv64IdxGenReg * _transitionBlock.PointerSize : _transitionBlock.OffsetOfFloatArgumentRegisters + _riscv64IdxFPReg * _transitionBlock.FloatRegisterSize; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 00ccf1345f0b9e..4a3a9989ddf6ae 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6374,12 +6374,12 @@ void MethodContext::recGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDL void MethodContext::dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpStructInRegistersInfo value) { printf("GetRiscV64PassFpStructInRegistersInfo key %016" PRIX64 " value-%#02x-" - "{OnlyOne=%i, BothFloat=%i, Float1st=%i, Size1st=%u, Float2nd=%i, Size2nd=%u, IntFieldKind=%i, offset1st=%u, offset2nd=%u}", + "{OnlyOne=%i, BothFloat=%i, FloatInt=%i, Size1st=%u, IntFloat=%i, Size2nd=%u, IntFieldKindMask=%i, offset1st=%u, offset2nd=%u}", key, value.flags, (value.flags & FpStruct::OnlyOne) != 0, (value.flags & FpStruct::BothFloat) != 0, - (value.flags & FpStruct::Float1st) != 0, value.GetSize1st(), - (value.flags & FpStruct::Float2nd) != 0, value.GetSize2nd(), + (value.flags & FpStruct::FloatInt) != 0, value.GetSize1st(), + (value.flags & FpStruct::IntFloat) != 0, value.GetSize2nd(), (int)value.GetIntFieldKind(), value.offset1st, value.offset2nd); } diff --git a/src/coreclr/vm/argdestination.h b/src/coreclr/vm/argdestination.h index bb84fc41338f26..f399701decb7da 100644 --- a/src/coreclr/vm/argdestination.h +++ b/src/coreclr/vm/argdestination.h @@ -112,32 +112,32 @@ class ArgDestination using namespace FpStruct; FpStructInRegistersInfo info = m_argLocDescForStructInRegs->m_structFields; _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == ((info.flags & BothFloat) ? 2 : 1)); - _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == ((info.flags & (Float1st | Float2nd)) ? 1 : 0)); + _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == ((info.flags & (FloatInt | IntFloat)) ? 1 : 0)); int floatRegOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_argLocDescForStructInRegs->m_idxFloatReg * FLOAT_REGISTER_SIZE; INT64* floatReg = (INT64*)((char*)m_base + floatRegOffset); - if (info.flags & (OnlyOne | BothFloat | Float1st)) // copy first floating field + if (info.flags & (OnlyOne | BothFloat | FloatInt)) // copy first floating field { void* field = (char*)src + info.offset1st; *floatReg++ = info.IsSize1st8() ? *(INT64*)field : NanBox | *(INT32*)field; } - if (info.flags & (BothFloat | Float2nd)) // copy second floating field + if (info.flags & (BothFloat | IntFloat)) // copy second floating field { void* field = (char*)src + info.offset2nd; *floatReg = info.IsSize2nd8() ? *(INT64*)field : NanBox | *(INT32*)field; } - if (info.flags & (Float1st | Float2nd)) // copy integer field + if (info.flags & (FloatInt | IntFloat)) // copy integer field { int intRegOffset = TransitionBlock::GetOffsetOfArgumentRegisters() + m_argLocDescForStructInRegs->m_idxGenReg * TARGET_POINTER_SIZE; INT64* intReg = (INT64*)((char*)m_base + intRegOffset); - void* field = (char*)src + ((info.flags & Float2nd) ? info.offset1st : info.offset2nd); - switch ((info.flags & Float2nd) ? info.GetSizeShift1st() : info.GetSizeShift2nd()) + void* field = (char*)src + ((info.flags & IntFloat) ? info.offset1st : info.offset2nd); + switch ((info.flags & IntFloat) ? info.GetSizeShift1st() : info.GetSizeShift2nd()) { case 0: *intReg = *(INT8* )field; break; case 1: *intReg = *(INT16*)field; break; diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index 69b33b5514f1f9..c6c636f195e30c 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -164,9 +164,9 @@ void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStruc { _ASSERTE(info.flags != FpStruct::UseIntCallConv); - // Float2nd is the only case where returnRegs[0] (fa0) represents the second field, not the first. - UINT64* returnField1st = &returnRegs[(info.flags & FpStruct::Float2nd) ? 1 : 0]; - UINT64* returnField2nd = &returnRegs[(info.flags & FpStruct::Float2nd) ? 0 : 1]; + // IntFloat is the only case where returnRegs[0] (fa0) represents the second field, not the first. + UINT64* returnField1st = &returnRegs[(info.flags & FpStruct::IntFloat) ? 1 : 0]; + UINT64* returnField2nd = &returnRegs[(info.flags & FpStruct::IntFloat) ? 0 : 1]; memcpyNoGCRefs((char*)dest + info.offset1st, returnField1st, info.GetSize1st()); @@ -175,7 +175,7 @@ void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStruc char* field2ndDest = (char*)dest + info.offset2nd; if (handleGcRefs && info.GetIntFieldKind() == FpStruct::IntKind::GcRef) { - _ASSERTE(info.flags & (FpStruct::Float1st | FpStruct::Float2nd)); + _ASSERTE(info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)); _ASSERTE(info.GetSize2nd() == TARGET_POINTER_SIZE); memmoveGCRefs(field2ndDest, returnField2nd, TARGET_POINTER_SIZE); } diff --git a/src/coreclr/vm/callingconvention.h b/src/coreclr/vm/callingconvention.h index 0532f5c7e167da..4a3c28104ff33d 100644 --- a/src/coreclr/vm/callingconvention.h +++ b/src/coreclr/vm/callingconvention.h @@ -1859,7 +1859,7 @@ int ArgIteratorTemplate::GetNextOffset() if (cFPRegs > 0) { // Pass according to hardware floating-point calling convention iff the argument can be fully enregistered - if (info.flags & (FpStruct::Float1st | FpStruct::Float2nd)) + if (info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) { assert(cFPRegs == 1); @@ -1875,7 +1875,7 @@ int ArgIteratorTemplate::GetNextOffset() m_argLocDescForStructInRegs.m_structFields = info; m_hasArgLocDescForStructInRegs = true; - int regOffset = (info.flags & FpStruct::Float2nd) + int regOffset = (info.flags & FpStruct::IntFloat) ? TransitionBlock::GetOffsetOfArgumentRegisters() + m_idxGenReg * TARGET_POINTER_SIZE : TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_idxFPReg * FLOAT_REGISTER_SIZE; m_idxFPReg++; diff --git a/src/coreclr/vm/comdelegate.cpp b/src/coreclr/vm/comdelegate.cpp index c3c617e3da4d97..ed5ddb8b544c4b 100644 --- a/src/coreclr/vm/comdelegate.cpp +++ b/src/coreclr/vm/comdelegate.cpp @@ -172,7 +172,7 @@ class ShuffleIterator if (m_currentFloatRegIndex < m_argLocDesc->m_cFloatReg) { #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) - if ((m_argLocDesc->m_structFields.flags & FpStruct::Float2nd) && (m_currentGenRegIndex < m_argLocDesc->m_cGenReg)) + if ((m_argLocDesc->m_structFields.flags & FpStruct::IntFloat) && (m_currentGenRegIndex < m_argLocDesc->m_cGenReg)) { // the first field is integer so just skip this. } diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 7796cbbbe434d9..df25e20c371385 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2935,6 +2935,8 @@ static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int i { assert(size == sizeof(float) || size == sizeof(double)); assert(intKind == FpStruct::IntKind::Signed); + static_assert((int)FpStruct::IntKind::Signed == 0, + "IntKind for floating fields should not clobber IntKind for int fields"); } assert(size >= 1 && size <= 8); @@ -2943,12 +2945,14 @@ static void SetFpStructInRegistersInfoField(FpStructInRegistersInfo& info, int i int sizeShift = (sizeShiftLUT >> (size * 2)) & 0b11; using namespace FpStruct; - static const int typeSize = PosFloat2nd - PosFloat1st; - static_assert((Float2nd | SizeShift2nd) == (Float1st | SizeShift1st) << typeSize, - "1st flags need to be 2nd flags shifted by typeSize"); - - int type = (isFloating << PosFloat1st) | (sizeShift << PosSizeShift1st); - info.flags = FpStruct::Flags(info.flags | (type << (typeSize * index)) | ((int)intKind << PosIntFieldKind)); + // Use FloatInt and IntFloat as marker flags for 1st and 2nd field respectively being floating. + // Fix to real flags (with OnlyOne and BothFloat) after flattening is complete. + static_assert(PosIntFloat == PosFloatInt + 1, "FloatInt and IntFloat need to be adjacent"); + static_assert(PosSizeShift2nd == PosSizeShift1st + 2, "SizeShift1st and 2nd need to be adjacent"); + int floatFlag = isFloating << (PosFloatInt + index); + int sizeShiftMask = sizeShift << (PosSizeShift1st + 2 * index); + + info.flags = FpStruct::Flags(info.flags | floatFlag | sizeShiftMask | ((int)intKind << PosIntFieldKind)); (index == 0 ? info.offset1st : info.offset2nd) = offset; } @@ -2986,7 +2990,7 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg assert(typeIndex == 1); // duplicate the array element info - static const int typeSize = FpStruct::PosFloat2nd - FpStruct::PosFloat1st; + static const int typeSize = FpStruct::PosIntFloat - FpStruct::PosFloatInt; info.flags = FpStruct::Flags(info.flags | (info.flags << typeSize)); info.offset2nd = info.offset1st + info.GetSize1st(); LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * duplicated array element type\n", @@ -3161,7 +3165,7 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan return FpStructInRegistersInfo{}; using namespace FpStruct; - if ((info.flags & (Float1st | Float2nd)) == 0) + if ((info.flags & (FloatInt | IntFloat)) == 0) { LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: struct %s (%u bytes) has no floating fields\n", (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize())); @@ -3170,22 +3174,22 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan assert(nFields == 1 || nFields == 2); - if ((info.flags & (Float1st | Float2nd)) == (Float1st | Float2nd)) + if ((info.flags & (FloatInt | IntFloat)) == (FloatInt | IntFloat)) { assert(nFields == 2); - info.flags = FpStruct::Flags(info.flags ^ (Float1st | Float2nd | BothFloat)); // replace Float(1st|2nd) with BothFloat + info.flags = FpStruct::Flags(info.flags ^ (FloatInt | IntFloat | BothFloat)); // replace (FloatInt | IntFloat) with BothFloat } else if (nFields == 1) { - assert((info.flags & Float1st) != 0); - assert((info.flags & (Float2nd | SizeShift2nd)) == 0); + assert((info.flags & FloatInt) != 0); + assert((info.flags & (IntFloat | SizeShift2ndMask)) == 0); assert(info.offset2nd == 0); - info.flags = FpStruct::Flags(info.flags ^ (Float1st | OnlyOne)); // replace Float1st with OnlyOne + info.flags = FpStruct::Flags(info.flags ^ (FloatInt | OnlyOne)); // replace FloatInt with OnlyOne } assert(nFields == ((info.flags & OnlyOne) != 0 ? 1 : 2)); - int floatFlags = info.flags & (OnlyOne | BothFloat | Float1st | Float2nd); + int floatFlags = info.flags & (OnlyOne | BothFloat | FloatInt | IntFloat); assert(floatFlags != 0); - assert((floatFlags & (floatFlags - 1)) == 0); // there can be only one of (OnlyOne | BothFloat | Float1st | Float2nd) + assert((floatFlags & (floatFlags - 1)) == 0); // there can be only one of (OnlyOne | BothFloat | FloatInt | IntFloat) if (nFields == 2) { unsigned end1st = info.offset1st + info.GetSize1st(); @@ -3196,10 +3200,10 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan assert(info.offset2nd + info.GetSize2nd() <= th.GetSize()); if (info.GetIntFieldKind() != FpStruct::IntKind::Signed) { - assert(info.flags & (Float1st | Float2nd)); + assert(info.flags & (FloatInt | IntFloat)); if (info.GetIntFieldKind() >= FpStruct::IntKind::GcRef) { - assert((info.flags & Float2nd) != 0 + assert((info.flags & IntFloat) != 0 ? (info.IsSize1st8() && IS_ALIGNED(info.offset1st, TARGET_POINTER_SIZE)) : (info.IsSize2nd8() && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); } @@ -3210,12 +3214,12 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan static const char* intKindNames[] = { "Signed", "Unsigned", "GcRef", "GcByRef" }; LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: " "struct %s (%u bytes) can be passed with floating-point calling convention, flags=%#02x; " - "%s, sizes={%u, %u}, offsets={%u, %u}, IntFieldKind=%s\n", + "%s, sizes={%u, %u}, offsets={%u, %u}, IntFieldKindMask=%s\n", (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize(), info.flags, ( (info.flags & OnlyOne) ? "OnlyFloat" : (info.flags & BothFloat) ? "BothFloat" - : (info.flags & Float1st) ? "Float1st" - : "Float2nd" ), + : (info.flags & FloatInt) ? "FloatInt" + : "IntFloat" ), info.GetSize1st(), info.GetSize2nd(), info.offset1st, info.offset2nd, intKindNames[(int)info.GetIntFieldKind()] From 892ec22f111bf5d512b5c9ec42204a72a67b1985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 09:05:15 +0200 Subject: [PATCH 53/60] Improve explanation why RISC-V can't use genActualType in genParamStackType --- src/coreclr/jit/codegencommon.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index fddeebc6d5b484..3734332249cc2b 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -3171,8 +3171,11 @@ var_types CodeGen::genParamStackType(LclVarDsc* dsc, const ABIPassingSegment& se // use stp more often. return TYP_I_IMPL; #elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) - // On RISC-V/LoongArch struct { struct{} e1,e2,e3; byte b; float f; } is passed in 2 registers so the - // load/store instruction for 'b' needs to be exact in size or it will overlap 'f'. + // On RISC-V/LoongArch structs passed according to floating-point calling convention are enregistered one + // field per register regardless of the layout of the fields in memory, so the small int load/store + // instructions must not be upsized to 4 bytes, otherwise for example: + // * struct { struct{} e1,e2,e3; byte b; float f; } -- 4-byte store for 'b' would trash 'f' + // * struct { float f; struct{} e1,e2,e3; byte b; } -- 4-byte store for 'b' would trash adjacent stack slot return seg.GetRegisterType(); #else return genActualType(seg.GetRegisterType()); From e73450e8e44277e57e785766d7feb00078d7f28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 09:05:15 +0200 Subject: [PATCH 54/60] Take signedness into account in CopyStructToRegisters --- src/coreclr/jit/compiler.cpp | 41 ++++++++++++++++++--------------- src/coreclr/vm/argdestination.h | 17 ++++++++++---- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 7842957ffb59fd..25f999f3d50b3c 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8324,30 +8324,33 @@ void Compiler::GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, if ((info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) != 0) { - bool isInt1st = ((info.flags & FpStruct::FloatInt) == 0); - FpStruct::IntKind kind = info.GetIntFieldKind(); - var_types intType; - if (kind == FpStruct::IntKind::GcRef) + bool isInt1st = ((info.flags & FpStruct::IntFloat) != 0); + FpStruct::IntKind kind = info.GetIntFieldKind(); + var_types* intType = isInt1st ? type1st : type2nd; + if (kind < FpStruct::IntKind::GcRef) { - intType = TYP_REF; - } - else if (kind == FpStruct::IntKind::GcByRef) - { - intType = TYP_BYREF; + assert(kind == FpStruct::IntKind::Signed || kind == FpStruct::IntKind::Unsigned); + struct IntType + { + // Ignore signedness because Compiler::fgDebugCheckTypes doesn't allow unsigned types + static constexpr var_types Get(unsigned sizeShift) + { + return (var_types)(TYP_BYTE + (sizeShift * 2)); + } + }; + static_assert(IntType::Get(0) == TYP_BYTE, ""); + static_assert(IntType::Get(1) == TYP_SHORT, ""); + static_assert(IntType::Get(2) == TYP_INT, ""); + static_assert(IntType::Get(3) == TYP_LONG, ""); + + unsigned sizeShift = isInt1st ? info.GetSizeShift1st() : info.GetSizeShift2nd(); + *intType = IntType::Get(sizeShift); } else { - static const var_types intTypesLookUpTable[] = { - /*[0] =*/TYP_BYTE, - /*[1] =*/TYP_SHORT, - /*[2] =*/TYP_INT, - /*[3] =*/TYP_LONG, - }; - unsigned sizeShift = isInt1st ? info.GetSizeShift1st() : info.GetSizeShift2nd(); - intType = intTypesLookUpTable[sizeShift]; + assert(kind == FpStruct::IntKind::GcRef || kind == FpStruct::IntKind::GcByRef); + *intType = (kind == FpStruct::IntKind::GcRef) ? TYP_REF : TYP_BYREF; } - var_types* typePtr = isInt1st ? type1st : type2nd; - *typePtr = intType; } } diff --git a/src/coreclr/vm/argdestination.h b/src/coreclr/vm/argdestination.h index f399701decb7da..8584cad3fb000a 100644 --- a/src/coreclr/vm/argdestination.h +++ b/src/coreclr/vm/argdestination.h @@ -113,6 +113,7 @@ class ArgDestination FpStructInRegistersInfo info = m_argLocDescForStructInRegs->m_structFields; _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == ((info.flags & BothFloat) ? 2 : 1)); _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == ((info.flags & (FloatInt | IntFloat)) ? 1 : 0)); + _ASSERTE(info.offset2nd + info.GetSize2nd() <= fieldBytes); int floatRegOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_argLocDescForStructInRegs->m_idxFloatReg * FLOAT_REGISTER_SIZE; @@ -137,12 +138,18 @@ class ArgDestination INT64* intReg = (INT64*)((char*)m_base + intRegOffset); void* field = (char*)src + ((info.flags & IntFloat) ? info.offset1st : info.offset2nd); - switch ((info.flags & IntFloat) ? info.GetSizeShift1st() : info.GetSizeShift2nd()) + unsigned sizeShift = (info.flags & IntFloat) ? info.GetSizeShift1st() : info.GetSizeShift2nd(); + bool isSigned = (info.GetIntFieldKind() == FpStruct::IntKind::Signed); + switch ((sizeShift << 1) + isSigned) { - case 0: *intReg = *(INT8* )field; break; - case 1: *intReg = *(INT16*)field; break; - case 2: *intReg = *(INT32*)field; break; - case 3: *intReg = *(INT64*)field; break; + case (0 << 1) + 1: *intReg = *(INT8* )field; break; + case (1 << 1) + 1: *intReg = *(INT16*)field; break; + case (2 << 1) + 1: *intReg = *(INT32*)field; break; + case (3 << 1) + 1: *intReg = *(INT64*)field; break; + case (0 << 1) + 0: *intReg = *(UINT8* )field; break; + case (1 << 1) + 0: *intReg = *(UINT16*)field; break; + case (2 << 1) + 0: *intReg = *(UINT32*)field; break; + case (3 << 1) + 0: *intReg = *(UINT64*)field; break; default: _ASSERTE(false); } } From 717cd5968274191eea32c400b1ede4df3c79c7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 09:05:15 +0200 Subject: [PATCH 55/60] Remove IsSize(1st|2nd)8 because they weren't used much --- src/coreclr/inc/corinfo.h | 14 ++------------ src/coreclr/jit/compiler.cpp | 4 ++-- .../tools/Common/JitInterface/CorInfoTypes.cs | 14 ++------------ .../JitInterface/RISCV64PassStructInRegister.cs | 4 ++-- src/coreclr/vm/argdestination.h | 4 ++-- src/coreclr/vm/methodtable.cpp | 4 ++-- 6 files changed, 12 insertions(+), 32 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index e583a9e4dc0edd..d605a1aee63a93 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -418,16 +418,6 @@ struct FpStructInRegistersInfo return 1u << GetSizeShift2nd(); } - bool IsSize1st8() const - { - return (flags & FpStruct::SizeShift1stMask) == (3 << FpStruct::PosSizeShift1st); - } - - bool IsSize2nd8() const - { - return (flags & FpStruct::SizeShift2ndMask) == (3 << FpStruct::PosSizeShift2nd); - } - FpStruct::IntKind GetIntFieldKind() const { return (FpStruct::IntKind)((flags >> FpStruct::PosIntFieldKind) & 0b11); @@ -440,8 +430,8 @@ struct FpStructInRegistersInfo ((flags & FpStruct::BothFloat) ? STRUCT_FLOAT_FIELD_ONLY_TWO : 0) | ((flags & FpStruct::FloatInt) ? STRUCT_FLOAT_FIELD_FIRST : 0) | ((flags & FpStruct::IntFloat) ? STRUCT_FLOAT_FIELD_SECOND : 0) | - (IsSize1st8() ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | - (IsSize2nd8() ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0)); + ((GetSizeShift1st() == 3) ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | + ((GetSizeShift2nd() == 3) ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0)); } static FpStructInRegistersInfo FromOldFlags(StructFloatFieldInfoFlags flags) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 25f999f3d50b3c..af35dc6baa51cc 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8317,10 +8317,10 @@ void Compiler::GetStructTypeOffset( void Compiler::GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, var_types* type1st, var_types* type2nd) { if ((info.flags & (FpStruct::BothFloat | FpStruct::FloatInt | FpStruct::OnlyOne)) != 0) - *type1st = info.IsSize1st8() ? TYP_DOUBLE : TYP_FLOAT; + *type1st = (info.GetSizeShift1st() == 3) ? TYP_DOUBLE : TYP_FLOAT; if ((info.flags & (FpStruct::BothFloat | FpStruct::IntFloat)) != 0) - *type2nd = info.IsSize2nd8() ? TYP_DOUBLE : TYP_FLOAT; + *type2nd = (info.GetSizeShift2nd() == 3) ? TYP_DOUBLE : TYP_FLOAT; if ((info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) != 0) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 5c497988e037a9..f8883d9161be34 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1325,16 +1325,6 @@ public uint GetSize2nd() return 1u << shift; } - public bool IsSize1st8() - { - return (flags & FpStruct.SizeShift1stMask) == (FpStruct)(3 << (int)FpStruct.PosSizeShift1st); - } - - public bool IsSize2nd8() - { - return (flags & FpStruct.SizeShift2ndMask) == (FpStruct)(3 << (int)FpStruct.PosSizeShift2nd); - } - public FpStruct_IntKind GetIntFieldKind() { return (FpStruct_IntKind)(((int)flags >> (int)FpStruct.PosIntFieldKind) & 0b11); @@ -1347,8 +1337,8 @@ public StructFloatFieldInfoFlags ToOldFlags() ((flags & FpStruct.BothFloat) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO : 0) | ((flags & FpStruct.FloatInt) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST : 0) | ((flags & FpStruct.IntFloat) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND : 0) | - (IsSize1st8() ? StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | - (IsSize2nd8() ? StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8 : 0); + ((GetSizeShift1st() == 3) ? StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | + ((GetSizeShift2nd() == 3) ? StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8 : 0); } } diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index 8e824eb3d3d322..1f78c124c3285b 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -306,8 +306,8 @@ private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl if (info.GetIntFieldKind() >= FpStruct_IntKind.GcRef) { Debug.Assert((info.flags & IntFloat) != 0 - ? (info.IsSize1st8() && IsAligned(info.offset1st, TARGET_POINTER_SIZE)) - : (info.IsSize2nd8() && IsAligned(info.offset2nd, TARGET_POINTER_SIZE))); + ? ((info.GetSizeShift1st() == 3) && IsAligned(info.offset1st, TARGET_POINTER_SIZE)) + : ((info.GetSizeShift2nd() == 3) && IsAligned(info.offset2nd, TARGET_POINTER_SIZE))); } } if ((info.flags & (OnlyOne | BothFloat)) != 0) diff --git a/src/coreclr/vm/argdestination.h b/src/coreclr/vm/argdestination.h index 8584cad3fb000a..25791371ec693c 100644 --- a/src/coreclr/vm/argdestination.h +++ b/src/coreclr/vm/argdestination.h @@ -122,13 +122,13 @@ class ArgDestination if (info.flags & (OnlyOne | BothFloat | FloatInt)) // copy first floating field { void* field = (char*)src + info.offset1st; - *floatReg++ = info.IsSize1st8() ? *(INT64*)field : NanBox | *(INT32*)field; + *floatReg++ = (info.GetSizeShift1st() == 3) ? *(INT64*)field : NanBox | *(INT32*)field; } if (info.flags & (BothFloat | IntFloat)) // copy second floating field { void* field = (char*)src + info.offset2nd; - *floatReg = info.IsSize2nd8() ? *(INT64*)field : NanBox | *(INT32*)field; + *floatReg = (info.GetSizeShift2nd() == 3) ? *(INT64*)field : NanBox | *(INT32*)field; } if (info.flags & (FloatInt | IntFloat)) // copy integer field diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index df25e20c371385..161235793845ab 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3204,8 +3204,8 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan if (info.GetIntFieldKind() >= FpStruct::IntKind::GcRef) { assert((info.flags & IntFloat) != 0 - ? (info.IsSize1st8() && IS_ALIGNED(info.offset1st, TARGET_POINTER_SIZE)) - : (info.IsSize2nd8() && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); + ? ((info.GetSizeShift1st() == 3) && IS_ALIGNED(info.offset1st, TARGET_POINTER_SIZE)) + : ((info.GetSizeShift2nd() == 3) && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); } } if (info.flags & (OnlyOne | BothFloat)) From 64de193659e01ffae1080507f8c788a07022919f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 09:05:15 +0200 Subject: [PATCH 56/60] Improve getter names in FpStructInRegistersInfo --- src/coreclr/inc/corinfo.h | 28 +++++------------- src/coreclr/jit/compiler.cpp | 8 ++--- src/coreclr/jit/targetriscv64.cpp | 6 ++-- .../tools/Common/JitInterface/CorInfoTypes.cs | 29 +++++-------------- .../RISCV64PassStructInRegister.cs | 20 ++++++------- .../superpmi-shared/methodcontext.cpp | 6 ++-- src/coreclr/vm/argdestination.h | 10 +++---- src/coreclr/vm/callhelpers.cpp | 8 ++--- src/coreclr/vm/methodtable.cpp | 24 +++++++-------- 9 files changed, 55 insertions(+), 84 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index d605a1aee63a93..7dc58237c620ea 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -398,27 +398,13 @@ struct FpStructInRegistersInfo uint32_t offset1st; uint32_t offset2nd; - unsigned GetSizeShift1st() const - { - return (flags >> FpStruct::PosSizeShift1st) & 0b11; - } - - unsigned GetSizeShift2nd() const - { - return (flags >> FpStruct::PosSizeShift2nd) & 0b11; - } + unsigned SizeShift1st() const { return (flags >> FpStruct::PosSizeShift1st) & 0b11; } + unsigned SizeShift2nd() const { return (flags >> FpStruct::PosSizeShift2nd) & 0b11; } - unsigned GetSize1st() const - { - return 1u << GetSizeShift1st(); - } - - unsigned GetSize2nd() const - { - return 1u << GetSizeShift2nd(); - } + unsigned Size1st() const { return 1u << SizeShift1st(); } + unsigned Size2nd() const { return 1u << SizeShift2nd(); } - FpStruct::IntKind GetIntFieldKind() const + FpStruct::IntKind IntFieldKind() const { return (FpStruct::IntKind)((flags >> FpStruct::PosIntFieldKind) & 0b11); } @@ -430,8 +416,8 @@ struct FpStructInRegistersInfo ((flags & FpStruct::BothFloat) ? STRUCT_FLOAT_FIELD_ONLY_TWO : 0) | ((flags & FpStruct::FloatInt) ? STRUCT_FLOAT_FIELD_FIRST : 0) | ((flags & FpStruct::IntFloat) ? STRUCT_FLOAT_FIELD_SECOND : 0) | - ((GetSizeShift1st() == 3) ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | - ((GetSizeShift2nd() == 3) ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0)); + ((SizeShift1st() == 3) ? STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | + ((SizeShift2nd() == 3) ? STRUCT_SECOND_FIELD_SIZE_IS8 : 0)); } static FpStructInRegistersInfo FromOldFlags(StructFloatFieldInfoFlags flags) diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index af35dc6baa51cc..92d127d0aba7fd 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -8317,15 +8317,15 @@ void Compiler::GetStructTypeOffset( void Compiler::GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, var_types* type1st, var_types* type2nd) { if ((info.flags & (FpStruct::BothFloat | FpStruct::FloatInt | FpStruct::OnlyOne)) != 0) - *type1st = (info.GetSizeShift1st() == 3) ? TYP_DOUBLE : TYP_FLOAT; + *type1st = (info.SizeShift1st() == 3) ? TYP_DOUBLE : TYP_FLOAT; if ((info.flags & (FpStruct::BothFloat | FpStruct::IntFloat)) != 0) - *type2nd = (info.GetSizeShift2nd() == 3) ? TYP_DOUBLE : TYP_FLOAT; + *type2nd = (info.SizeShift2nd() == 3) ? TYP_DOUBLE : TYP_FLOAT; if ((info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)) != 0) { bool isInt1st = ((info.flags & FpStruct::IntFloat) != 0); - FpStruct::IntKind kind = info.GetIntFieldKind(); + FpStruct::IntKind kind = info.IntFieldKind(); var_types* intType = isInt1st ? type1st : type2nd; if (kind < FpStruct::IntKind::GcRef) { @@ -8343,7 +8343,7 @@ void Compiler::GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, static_assert(IntType::Get(2) == TYP_INT, ""); static_assert(IntType::Get(3) == TYP_LONG, ""); - unsigned sizeShift = isInt1st ? info.GetSizeShift1st() : info.GetSizeShift2nd(); + unsigned sizeShift = isInt1st ? info.SizeShift1st() : info.SizeShift2nd(); *intType = IntType::Get(sizeShift); } else diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index c771611cb80120..d3f2ccf614009d 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -108,7 +108,7 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, else { assert((info.flags & OnlyOne) != 0); // struct containing just one FP real - passedSize = info.GetSize1st(); + passedSize = info.Size1st(); offset = info.offset1st; } @@ -129,8 +129,8 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, regNumber firstReg = (isFirstFloat ? m_floatRegs : m_intRegs).Dequeue(); regNumber secondReg = (isSecondFloat ? m_floatRegs : m_intRegs).Dequeue(); - ABIPassingSegment seg1st = ABIPassingSegment::InRegister(firstReg, info.offset1st, info.GetSize1st()); - ABIPassingSegment seg2nd = ABIPassingSegment::InRegister(secondReg, info.offset2nd, info.GetSize2nd()); + ABIPassingSegment seg1st = ABIPassingSegment::InRegister(firstReg, info.offset1st, info.Size1st()); + ABIPassingSegment seg2nd = ABIPassingSegment::InRegister(secondReg, info.offset2nd, info.Size2nd()); return {2, new (comp, CMK_ABI) ABIPassingSegment[]{seg1st, seg2nd}}; } } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index f8883d9161be34..c71c099acf189a 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1303,29 +1303,14 @@ public struct FpStructInRegistersInfo public uint offset1st; public uint offset2nd; - public uint GetSizeShift1st() - { - return (uint)((int)flags >> (int)FpStruct.PosSizeShift1st) & 0b11; - } + public uint SizeShift1st() { return (uint)((int)flags >> (int)FpStruct.PosSizeShift1st) & 0b11; } - public uint GetSizeShift2nd() - { - return (uint)((int)flags >> (int)FpStruct.PosSizeShift2nd) & 0b11; - } + public uint SizeShift2nd() { return (uint)((int)flags >> (int)FpStruct.PosSizeShift2nd) & 0b11; } - public uint GetSize1st() - { - int shift = ((int)flags >> (int)FpStruct.PosSizeShift1st) & 0b11; - return 1u << shift; - } - - public uint GetSize2nd() - { - int shift = ((int)flags >> (int)FpStruct.PosSizeShift2nd) & 0b11; - return 1u << shift; - } + public uint Size1st() { return 1u << (int)SizeShift1st(); } + public uint Size2nd() { return 1u << (int)SizeShift2nd(); } - public FpStruct_IntKind GetIntFieldKind() + public FpStruct_IntKind IntFieldKind() { return (FpStruct_IntKind)(((int)flags >> (int)FpStruct.PosIntFieldKind) & 0b11); } @@ -1337,8 +1322,8 @@ public StructFloatFieldInfoFlags ToOldFlags() ((flags & FpStruct.BothFloat) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_ONLY_TWO : 0) | ((flags & FpStruct.FloatInt) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_FIRST : 0) | ((flags & FpStruct.IntFloat) != 0 ? StructFloatFieldInfoFlags.STRUCT_FLOAT_FIELD_SECOND : 0) | - ((GetSizeShift1st() == 3) ? StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | - ((GetSizeShift2nd() == 3) ? StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8 : 0); + ((SizeShift1st() == 3) ? StructFloatFieldInfoFlags.STRUCT_FIRST_FIELD_SIZE_IS8 : 0) | + ((SizeShift2nd() == 3) ? StructFloatFieldInfoFlags.STRUCT_SECOND_FIELD_SIZE_IS8 : 0); } } diff --git a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs index 1f78c124c3285b..8aa2664f47e525 100644 --- a/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs +++ b/src/coreclr/tools/Common/JitInterface/RISCV64PassStructInRegister.cs @@ -182,7 +182,7 @@ private static bool HandleInlineArray(int elementTypeIndex, int nElements, ref F // duplicate the array element info const int typeSize = (int)PosIntFloat - (int)PosFloatInt; info.flags |= (FpStruct)((int)info.flags << typeSize); - info.offset2nd = info.offset1st + info.GetSize1st(); + info.offset2nd = info.offset1st + info.Size1st(); } return true; } @@ -294,24 +294,24 @@ private static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl "there can be only one of (OnlyOne | BothFloat | FloatInt | IntFloat)"); if (nFields == 2) { - uint end1st = info.offset1st + info.GetSize1st(); - uint end2nd = info.offset2nd + info.GetSize2nd(); + uint end1st = info.offset1st + info.Size1st(); + uint end2nd = info.offset2nd + info.Size2nd(); Debug.Assert(end1st <= info.offset2nd || end2nd <= info.offset1st, "fields must not overlap"); } - Debug.Assert(info.offset1st + info.GetSize1st() <= td.GetElementSize().AsInt); - Debug.Assert(info.offset2nd + info.GetSize2nd() <= td.GetElementSize().AsInt); - if (info.GetIntFieldKind() != FpStruct_IntKind.Signed) + Debug.Assert(info.offset1st + info.Size1st() <= td.GetElementSize().AsInt); + Debug.Assert(info.offset2nd + info.Size2nd() <= td.GetElementSize().AsInt); + if (info.IntFieldKind() != FpStruct_IntKind.Signed) { Debug.Assert((info.flags & (FloatInt | IntFloat)) != 0); - if (info.GetIntFieldKind() >= FpStruct_IntKind.GcRef) + if (info.IntFieldKind() >= FpStruct_IntKind.GcRef) { Debug.Assert((info.flags & IntFloat) != 0 - ? ((info.GetSizeShift1st() == 3) && IsAligned(info.offset1st, TARGET_POINTER_SIZE)) - : ((info.GetSizeShift2nd() == 3) && IsAligned(info.offset2nd, TARGET_POINTER_SIZE))); + ? ((info.SizeShift1st() == 3) && IsAligned(info.offset1st, TARGET_POINTER_SIZE)) + : ((info.SizeShift2nd() == 3) && IsAligned(info.offset2nd, TARGET_POINTER_SIZE))); } } if ((info.flags & (OnlyOne | BothFloat)) != 0) - Debug.Assert(info.GetIntFieldKind() == FpStruct_IntKind.Signed); + Debug.Assert(info.IntFieldKind() == FpStruct_IntKind.Signed); return info; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 4a3a9989ddf6ae..5eb543f64f08f0 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6378,9 +6378,9 @@ void MethodContext::dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpSt key, value.flags, (value.flags & FpStruct::OnlyOne) != 0, (value.flags & FpStruct::BothFloat) != 0, - (value.flags & FpStruct::FloatInt) != 0, value.GetSize1st(), - (value.flags & FpStruct::IntFloat) != 0, value.GetSize2nd(), - (int)value.GetIntFieldKind(), + (value.flags & FpStruct::FloatInt) != 0, value.Size1st(), + (value.flags & FpStruct::IntFloat) != 0, value.Size2nd(), + (int)value.IntFieldKind(), value.offset1st, value.offset2nd); } diff --git a/src/coreclr/vm/argdestination.h b/src/coreclr/vm/argdestination.h index 25791371ec693c..4aeca6eb4c9870 100644 --- a/src/coreclr/vm/argdestination.h +++ b/src/coreclr/vm/argdestination.h @@ -113,7 +113,7 @@ class ArgDestination FpStructInRegistersInfo info = m_argLocDescForStructInRegs->m_structFields; _ASSERTE(m_argLocDescForStructInRegs->m_cFloatReg == ((info.flags & BothFloat) ? 2 : 1)); _ASSERTE(m_argLocDescForStructInRegs->m_cGenReg == ((info.flags & (FloatInt | IntFloat)) ? 1 : 0)); - _ASSERTE(info.offset2nd + info.GetSize2nd() <= fieldBytes); + _ASSERTE(info.offset2nd + info.Size2nd() <= fieldBytes); int floatRegOffset = TransitionBlock::GetOffsetOfFloatArgumentRegisters() + m_argLocDescForStructInRegs->m_idxFloatReg * FLOAT_REGISTER_SIZE; @@ -122,13 +122,13 @@ class ArgDestination if (info.flags & (OnlyOne | BothFloat | FloatInt)) // copy first floating field { void* field = (char*)src + info.offset1st; - *floatReg++ = (info.GetSizeShift1st() == 3) ? *(INT64*)field : NanBox | *(INT32*)field; + *floatReg++ = (info.SizeShift1st() == 3) ? *(INT64*)field : NanBox | *(INT32*)field; } if (info.flags & (BothFloat | IntFloat)) // copy second floating field { void* field = (char*)src + info.offset2nd; - *floatReg = (info.GetSizeShift2nd() == 3) ? *(INT64*)field : NanBox | *(INT32*)field; + *floatReg = (info.SizeShift2nd() == 3) ? *(INT64*)field : NanBox | *(INT32*)field; } if (info.flags & (FloatInt | IntFloat)) // copy integer field @@ -138,8 +138,8 @@ class ArgDestination INT64* intReg = (INT64*)((char*)m_base + intRegOffset); void* field = (char*)src + ((info.flags & IntFloat) ? info.offset1st : info.offset2nd); - unsigned sizeShift = (info.flags & IntFloat) ? info.GetSizeShift1st() : info.GetSizeShift2nd(); - bool isSigned = (info.GetIntFieldKind() == FpStruct::IntKind::Signed); + unsigned sizeShift = (info.flags & IntFloat) ? info.SizeShift1st() : info.SizeShift2nd(); + bool isSigned = (info.IntFieldKind() == FpStruct::IntKind::Signed); switch ((sizeShift << 1) + isSigned) { case (0 << 1) + 1: *intReg = *(INT8* )field; break; diff --git a/src/coreclr/vm/callhelpers.cpp b/src/coreclr/vm/callhelpers.cpp index c6c636f195e30c..c4d38aa79c4364 100644 --- a/src/coreclr/vm/callhelpers.cpp +++ b/src/coreclr/vm/callhelpers.cpp @@ -168,20 +168,20 @@ void CopyReturnedFpStructFromRegisters(void* dest, UINT64 returnRegs[2], FpStruc UINT64* returnField1st = &returnRegs[(info.flags & FpStruct::IntFloat) ? 1 : 0]; UINT64* returnField2nd = &returnRegs[(info.flags & FpStruct::IntFloat) ? 0 : 1]; - memcpyNoGCRefs((char*)dest + info.offset1st, returnField1st, info.GetSize1st()); + memcpyNoGCRefs((char*)dest + info.offset1st, returnField1st, info.Size1st()); if ((info.flags & FpStruct::OnlyOne) == 0) { char* field2ndDest = (char*)dest + info.offset2nd; - if (handleGcRefs && info.GetIntFieldKind() == FpStruct::IntKind::GcRef) + if (handleGcRefs && info.IntFieldKind() == FpStruct::IntKind::GcRef) { _ASSERTE(info.flags & (FpStruct::FloatInt | FpStruct::IntFloat)); - _ASSERTE(info.GetSize2nd() == TARGET_POINTER_SIZE); + _ASSERTE(info.Size2nd() == TARGET_POINTER_SIZE); memmoveGCRefs(field2ndDest, returnField2nd, TARGET_POINTER_SIZE); } else { - memcpyNoGCRefs(field2ndDest, returnField2nd, info.GetSize2nd()); + memcpyNoGCRefs(field2ndDest, returnField2nd, info.Size2nd()); } } } diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 161235793845ab..40680cffdce139 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2992,7 +2992,7 @@ static bool HandleInlineArray(int elementTypeIndex, int nElements, FpStructInReg // duplicate the array element info static const int typeSize = FpStruct::PosIntFloat - FpStruct::PosFloatInt; info.flags = FpStruct::Flags(info.flags | (info.flags << typeSize)); - info.offset2nd = info.offset1st + info.GetSize1st(); + info.offset2nd = info.offset1st + info.Size1st(); LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo:%*s * duplicated array element type\n", nestingLevel * 4, "")); } @@ -3192,24 +3192,24 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan assert((floatFlags & (floatFlags - 1)) == 0); // there can be only one of (OnlyOne | BothFloat | FloatInt | IntFloat) if (nFields == 2) { - unsigned end1st = info.offset1st + info.GetSize1st(); - unsigned end2nd = info.offset2nd + info.GetSize2nd(); + unsigned end1st = info.offset1st + info.Size1st(); + unsigned end2nd = info.offset2nd + info.Size2nd(); assert(end1st <= info.offset2nd || end2nd <= info.offset1st); // fields must not overlap } - assert(info.offset1st + info.GetSize1st() <= th.GetSize()); - assert(info.offset2nd + info.GetSize2nd() <= th.GetSize()); - if (info.GetIntFieldKind() != FpStruct::IntKind::Signed) + assert(info.offset1st + info.Size1st() <= th.GetSize()); + assert(info.offset2nd + info.Size2nd() <= th.GetSize()); + if (info.IntFieldKind() != FpStruct::IntKind::Signed) { assert(info.flags & (FloatInt | IntFloat)); - if (info.GetIntFieldKind() >= FpStruct::IntKind::GcRef) + if (info.IntFieldKind() >= FpStruct::IntKind::GcRef) { assert((info.flags & IntFloat) != 0 - ? ((info.GetSizeShift1st() == 3) && IS_ALIGNED(info.offset1st, TARGET_POINTER_SIZE)) - : ((info.GetSizeShift2nd() == 3) && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); + ? ((info.SizeShift1st() == 3) && IS_ALIGNED(info.offset1st, TARGET_POINTER_SIZE)) + : ((info.SizeShift2nd() == 3) && IS_ALIGNED(info.offset2nd, TARGET_POINTER_SIZE))); } } if (info.flags & (OnlyOne | BothFloat)) - assert(info.GetIntFieldKind() == FpStruct::IntKind::Signed); + assert(info.IntFieldKind() == FpStruct::IntKind::Signed); static const char* intKindNames[] = { "Signed", "Unsigned", "GcRef", "GcByRef" }; LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: " @@ -3220,9 +3220,9 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan : (info.flags & BothFloat) ? "BothFloat" : (info.flags & FloatInt) ? "FloatInt" : "IntFloat" ), - info.GetSize1st(), info.GetSize2nd(), + info.Size1st(), info.Size2nd(), info.offset1st, info.offset2nd, - intKindNames[(int)info.GetIntFieldKind()] + intKindNames[(int)info.IntFieldKind()] )); return info; } From d2d1841eb803186c6aa2aea8acbf8859e479e235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 09:19:09 +0200 Subject: [PATCH 57/60] Add comment to fix JIT to AssignClassifiedEightByteTypes --- src/coreclr/vm/methodtable.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 40680cffdce139..5a441220ea6bec 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -2685,9 +2685,11 @@ void MethodTable::AssignClassifiedEightByteTypes(SystemVStructRegisterPassingHe { if (!foundFieldInEightByte) { - // If we didn't find a field in an eight-byte (i.e. there are no explicit offsets that start a field in this eightbyte) + // If we didn't find a field in an eightbyte (i.e. there are no explicit offsets that start a field in this eightbyte) // then the classification of this eightbyte might be NoClass. We can't hand a classification of NoClass to the JIT // so set the class to Integer (as though the struct has a char[8] padding) if the class is NoClass. + // + // TODO: Fix JIT, NoClass eightbytes are valid and passing them is now broken because of this. if (helperPtr->eightByteClassifications[offset / SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES] == SystemVClassificationTypeNoClass) { helperPtr->eightByteClassifications[offset / SYSTEMV_EIGHT_BYTE_SIZE_IN_BYTES] = SystemVClassificationTypeInteger; From 47f9d87b16fa862c7c5593e5a2a979ab5094ce47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 09:48:23 +0200 Subject: [PATCH 58/60] Add helpers for IntKind and flag names to FpStructInRegistersInfo --- src/coreclr/inc/corinfo.h | 22 +++++++++++++++++-- .../tools/Common/JitInterface/CorInfoTypes.cs | 4 ++-- .../superpmi-shared/methodcontext.cpp | 9 ++------ src/coreclr/vm/methodtable.cpp | 9 +------- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 7dc58237c620ea..f94562aa3104f9 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -366,7 +366,7 @@ namespace FpStruct enum Flags { - // Positions of bitfields + // Positions of flags and bitfields PosOnlyOne = 0, PosBothFloat = 1, PosFloatInt = 2, @@ -377,7 +377,7 @@ namespace FpStruct UseIntCallConv = 0, // struct is passed according to integer calling convention - // The bitfields + // The flags and bitfields OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point FloatInt = 1 << PosFloatInt, // has two fields, 1st is floating and 2nd is integer @@ -409,6 +409,24 @@ struct FpStructInRegistersInfo return (FpStruct::IntKind)((flags >> FpStruct::PosIntFieldKind) & 0b11); } + const char* IntFieldKindName() const + { + static const char* intKindNames[] = { "Signed", "Unsigned", "GcRef", "GcByRef" }; + return intKindNames[(int)IntFieldKind()]; + } + + const char* FlagName() const + { + switch (flags & (FpStruct::OnlyOne | FpStruct::BothFloat | FpStruct::FloatInt | FpStruct::IntFloat)) + { + case FpStruct::OnlyOne: return "OnlyOne"; + case FpStruct::BothFloat: return "BothFloat"; + case FpStruct::FloatInt: return "FloatInt"; + case FpStruct::IntFloat: return "IntFloat"; + default: return "?"; + } + } + StructFloatFieldInfoFlags ToOldFlags() const { return StructFloatFieldInfoFlags( diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index c71c099acf189a..1027e3cf2dcf80 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1272,7 +1272,7 @@ public enum FpStruct_IntKind [Flags] public enum FpStruct { - // Positions of bitfields + // Positions of flags and bitfields PosOnlyOne = 0, PosBothFloat = 1, PosFloatInt = 2, @@ -1283,7 +1283,7 @@ public enum FpStruct UseIntCallConv = 0, // struct is passed according to integer calling convention - // The bitfields + // The flags and bitfields OnlyOne = 1 << PosOnlyOne, // has only one field, which is floating-point BothFloat = 1 << PosBothFloat, // has two fields, both are floating-point FloatInt = 1 << PosFloatInt, // has two fields, 1st is floating and 2nd is integer diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 5eb543f64f08f0..547a8aa0d11196 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6374,14 +6374,9 @@ void MethodContext::recGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDL void MethodContext::dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpStructInRegistersInfo value) { printf("GetRiscV64PassFpStructInRegistersInfo key %016" PRIX64 " value-%#02x-" - "{OnlyOne=%i, BothFloat=%i, FloatInt=%i, Size1st=%u, IntFloat=%i, Size2nd=%u, IntFieldKindMask=%i, offset1st=%u, offset2nd=%u}", + "{%s, sizes={%u, %u}, offsets={%u, %u}, IntFieldKind=%s}\n", key, value.flags, - (value.flags & FpStruct::OnlyOne) != 0, - (value.flags & FpStruct::BothFloat) != 0, - (value.flags & FpStruct::FloatInt) != 0, value.Size1st(), - (value.flags & FpStruct::IntFloat) != 0, value.Size2nd(), - (int)value.IntFieldKind(), - value.offset1st, value.offset2nd); + value.FlagName(), value.Size1st(), value.Size2nd(), value.offset1st, value.offset2nd, value.IntFieldKindName()); } FpStructInRegistersInfo MethodContext::repGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHnd) diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 5a441220ea6bec..609c322d150992 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3213,18 +3213,11 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan if (info.flags & (OnlyOne | BothFloat)) assert(info.IntFieldKind() == FpStruct::IntKind::Signed); - static const char* intKindNames[] = { "Signed", "Unsigned", "GcRef", "GcByRef" }; LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: " "struct %s (%u bytes) can be passed with floating-point calling convention, flags=%#02x; " "%s, sizes={%u, %u}, offsets={%u, %u}, IntFieldKindMask=%s\n", (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize(), info.flags, - ( (info.flags & OnlyOne) ? "OnlyFloat" - : (info.flags & BothFloat) ? "BothFloat" - : (info.flags & FloatInt) ? "FloatInt" - : "IntFloat" ), - info.Size1st(), info.Size2nd(), - info.offset1st, info.offset2nd, - intKindNames[(int)info.IntFieldKind()] + info.FlagName(), info.Size1st(), info.Size2nd(), info.offset1st, info.offset2nd, info.IntFieldKindName() )); return info; } From 8d6a1021f39a6c68cbbc7cf264b84ccabcc12e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 11:52:36 +0200 Subject: [PATCH 59/60] Fix C# build: Enum values should be on separate lines --- src/coreclr/inc/corinfo.h | 5 ++++- src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index f94562aa3104f9..4ec8b9585c6544 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -361,7 +361,10 @@ namespace FpStruct { enum class IntKind { - Signed, Unsigned, GcRef, GcByRef + Signed, + Unsigned, + GcRef, + GcByRef, }; enum Flags diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index 1027e3cf2dcf80..58c987db0a4174 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -1265,7 +1265,10 @@ public enum StructFloatFieldInfoFlags public enum FpStruct_IntKind { - Signed, Unsigned, GcRef, GcByRef + Signed, + Unsigned, + GcRef, + GcByRef, } // Bitfields for FpStructInRegistersInfo.flags From 7036dc0c64b731a748a936ba6068d0fb92638138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 21 Jun 2024 15:29:57 +0200 Subject: [PATCH 60/60] Make a logging wrapper Compiler::GetPassFpStructInRegistersInfo, similar to eeGetSystemVAmd64PassStructInRegisterDescriptor --- src/coreclr/inc/corinfo.h | 4 +- src/coreclr/jit/buildstring.cpp | 12 ++-- src/coreclr/jit/compiler.cpp | 56 ++++++++++++++++--- src/coreclr/jit/compiler.h | 5 +- src/coreclr/jit/ee_il_dll.cpp | 4 +- src/coreclr/jit/gentree.cpp | 19 +------ src/coreclr/jit/lclvars.cpp | 9 +-- src/coreclr/jit/targetriscv64.cpp | 2 +- src/coreclr/jit/utils.h | 3 + .../superpmi-shared/methodcontext.cpp | 2 +- src/coreclr/vm/methodtable.cpp | 2 +- 11 files changed, 74 insertions(+), 44 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 4ec8b9585c6544..4142cd85cc4802 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -415,7 +415,9 @@ struct FpStructInRegistersInfo const char* IntFieldKindName() const { static const char* intKindNames[] = { "Signed", "Unsigned", "GcRef", "GcByRef" }; - return intKindNames[(int)IntFieldKind()]; + return (flags & (FpStruct::FloatInt | FpStruct::IntFloat)) + ? intKindNames[(int)IntFieldKind()] + : "None"; } const char* FlagName() const diff --git a/src/coreclr/jit/buildstring.cpp b/src/coreclr/jit/buildstring.cpp index 3f0222ad2649ac..f51e262c517ef8 100644 --- a/src/coreclr/jit/buildstring.cpp +++ b/src/coreclr/jit/buildstring.cpp @@ -1,17 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#define STRINGIFY(L) #L -#define MAKESTRING(M, L) M(L) -#define STRINGIZE(X) MAKESTRING(STRINGIFY, X) - #if defined(__clang__) #define BUILD_COMPILER \ - "Clang " STRINGIZE(__clang_major__) "." STRINGIZE(__clang_minor__) "." STRINGIZE(__clang_patchlevel__) + "Clang " STRINGIFY(__clang_major__) "." STRINGIFY(__clang_minor__) "." STRINGIFY(__clang_patchlevel__) #elif defined(_MSC_VER) -#define BUILD_COMPILER "MSVC " STRINGIZE(_MSC_FULL_VER) +#define BUILD_COMPILER "MSVC " STRINGIFY(_MSC_FULL_VER) #elif defined(__GNUC__) -#define BUILD_COMPILER "GCC " STRINGIZE(__GNUC__) "." STRINGIZE(__GNUC_MINOR__) "." STRINGIZE(__GNUC_PATCHLEVEL__) +#define BUILD_COMPILER "GCC " STRINGIFY(__GNUC__) "." STRINGIFY(__GNUC_MINOR__) "." STRINGIFY(__GNUC_PATCHLEVEL__) #else #define BUILD_COMPILER "Unknown" #endif @@ -26,6 +22,8 @@ #define TARGET_ARCH_STRING "arm64" #elif defined(TARGET_LOONGARCH64) #define TARGET_ARCH_STRING "loongarch64" +#elif defined(TARGET_RISCV64) +#define TARGET_ARCH_STRING "riscv64" #else #define TARGET_ARCH_STRING "Unknown" #endif diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index c7db41764d1611..576debc9e841c9 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -769,8 +769,8 @@ var_types Compiler::getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, #ifdef TARGET_RISCV64 // Struct larger than 16 can still be passed in registers according to FP call conv if it has empty fields // or more padding - uint32_t flags = info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(clsHnd).ToOldFlags(); - if (flags != STRUCT_NO_FLOAT_FIELD) + FpStructInRegistersInfo info = GetPassFpStructInRegistersInfo(clsHnd); + if (info.flags != FpStruct::UseIntCallConv) { howToPassStruct = SPK_ByValue; useType = TYP_STRUCT; @@ -956,15 +956,15 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, } #elif defined(TARGET_RISCV64) - uint32_t floatFieldFlags = (uint32_t)info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(clsHnd).ToOldFlags(); - if ((floatFieldFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0) + FpStructInRegistersInfo info = GetPassFpStructInRegistersInfo(clsHnd); + if ((info.flags & FpStruct::OnlyOne) != 0) { howToReturnStruct = SPK_PrimitiveType; - useType = ((floatFieldFlags & STRUCT_FIRST_FIELD_SIZE_IS8) != 0) ? TYP_DOUBLE : TYP_FLOAT; + useType = (info.SizeShift1st() == 3) ? TYP_DOUBLE : TYP_FLOAT; } - else if (floatFieldFlags != STRUCT_NO_FLOAT_FIELD) + else if (info.flags != FpStruct::UseIntCallConv) { - assert((floatFieldFlags & (STRUCT_HAS_FLOAT_FIELDS_MASK ^ STRUCT_FLOAT_FIELD_ONLY_ONE)) != 0); + assert((info.flags & (FpStruct::BothFloat | FpStruct::FloatInt | FpStruct::IntFloat)) != 0); howToReturnStruct = SPK_ByValue; useType = TYP_STRUCT; } @@ -8310,6 +8310,48 @@ void Compiler::GetStructTypeOffset( } #elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) +//------------------------------------------------------------------------ +// GetPassFpStructInRegistersInfo: Gets the information on passing of a struct according to hardware floating-point +// calling convention. +// +// Arguments: +// structHandle - type handle +// +// Return value: +// The passing info +FpStructInRegistersInfo Compiler::GetPassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHandle) +{ +#ifdef TARGET_RISCV64 +#define getInfoFunc getRiscV64PassFpStructInRegistersInfo +#else +#define getInfoFunc getLoongArchPassFpStructInRegistersInfo +#endif + + FpStructInRegistersInfo ret = info.compCompHnd->getInfoFunc(structHandle); +#ifdef DEBUG + if (VERBOSE) + { + logf("**** " STRINGIFY(getInfoFunc) "(0x%x (%s, %u bytes)) =>\n", dspPtr(structHandle), + eeGetClassName(structHandle), info.compCompHnd->getClassSize(structHandle)); +#undef getInfoFunc + if (ret.flags == FpStruct::UseIntCallConv) + { + logf(" pass by integer calling convention\n"); + } + else + { + bool hasOne = ((ret.flags & FpStruct::OnlyOne) != 0); + long size2nd = hasOne ? -1l : ret.Size2nd(); + long offset2nd = hasOne ? -1l : ret.offset2nd; + logf(" may be passed by floating-point calling convention:\n" + " flags=%#03x; %s, field sizes={%u, %li}, field offsets={%u, %li}, IntFieldKind=%s\n", + ret.flags, ret.FlagName(), ret.Size1st(), size2nd, ret.offset1st, offset2nd, ret.IntFieldKindName()); + } + } +#endif // DEBUG + return ret; +} + //------------------------------------------------------------------------ // GetTypesFromFpStructInRegistersInfo: Gets the field types of a struct passed in registers according to hardware // floating-point calling convention. diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index ac1011dd403be6..05fb9b6e566776 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -8481,9 +8481,10 @@ class Compiler { return eeRunWithSPMIErrorTrap( [](Functor* pf) { - (*pf)(); + (*pf)(); }, &f); + } bool eeRunWithSPMIErrorTrapImp(void (*function)(void*), void* param); @@ -11370,6 +11371,8 @@ class Compiler CORINFO_CLASS_HANDLE typeHnd, var_types* type0, var_types* type1, uint8_t* offset0, uint8_t* offset1); #elif defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) + FpStructInRegistersInfo GetPassFpStructInRegistersInfo(CORINFO_CLASS_HANDLE structHandle); + static void GetTypesFromFpStructInRegistersInfo(FpStructInRegistersInfo info, var_types* type1st, var_types* type2nd); diff --git a/src/coreclr/jit/ee_il_dll.cpp b/src/coreclr/jit/ee_il_dll.cpp index 8bd738c10d2fee..f8831f39af3c97 100644 --- a/src/coreclr/jit/ee_il_dll.cpp +++ b/src/coreclr/jit/ee_il_dll.cpp @@ -431,7 +431,7 @@ unsigned Compiler::eeGetArgSize(CorInfoType corInfoType, CORINFO_CLASS_HANDLE ty #ifdef TARGET_RISCV64 // ... unless on RISC-V they are still eligible for passing according to hardware floating-point calling // convention, e.g. struct {long; struct{}; double; }. - FpStructInRegistersInfo fpInfo = info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(typeHnd); + FpStructInRegistersInfo fpInfo = GetPassFpStructInRegistersInfo(typeHnd); if (fpInfo.flags == FpStruct::UseIntCallConv) #endif { @@ -1383,7 +1383,7 @@ void Compiler::eeGetSystemVAmd64PassStructInRegisterDescriptor( printf(" eightByte #%d -- classification: ", i); dumpSystemVClassificationType(structPassInRegDescPtr->eightByteClassifications[i]); printf(", byteSize: %d, byteOffset: %d\n", structPassInRegDescPtr->eightByteSizes[i], - structPassInRegDescPtr->eightByteOffsets[i]); + structPassInRegDescPtr->eightByteOffsetn[i]); } } } diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index a425c730d2ccc7..1cba1343d2e655 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -28625,13 +28625,7 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, m_regType[0] = returnType; #if defined(TARGET_RISCV64) || defined(TARGET_LOONGARCH64) -#ifdef TARGET_LOONGARCH64 - uint32_t floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(retClsHnd); - FpStructInRegistersInfo fpInfo = - FpStructInRegistersInfo::FromOldFlags((StructFloatFieldInfoFlags)floatFieldFlags); -#else // TARGET_RISCV64 - FpStructInRegistersInfo fpInfo = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(retClsHnd); -#endif + FpStructInRegistersInfo fpInfo = comp->GetPassFpStructInRegistersInfo(retClsHnd); if (fpInfo.flags != FpStruct::UseIntCallConv) { assert((fpInfo.flags & FpStruct::OnlyOne) != 0); @@ -28704,15 +28698,8 @@ void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) assert(structSize >= TARGET_POINTER_SIZE); - -#ifdef TARGET_LOONGARCH64 - assert(structSize <= (2 * TARGET_POINTER_SIZE)); - uint32_t floatFieldFlags = comp->info.compCompHnd->getLoongArch64PassStructInRegisterFlags(retClsHnd); - FpStructInRegistersInfo fpInfo = - FpStructInRegistersInfo::FromOldFlags((StructFloatFieldInfoFlags)floatFieldFlags); -#else // TARGET_RISCV64 - FpStructInRegistersInfo fpInfo = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(retClsHnd); -#endif + LOONGARCH64_ONLY(assert(structSize <= (2 * TARGET_POINTER_SIZE));) + FpStructInRegistersInfo fpInfo = comp->GetPassFpStructInRegistersInfo(retClsHnd); if (fpInfo.flags != FpStruct::UseIntCallConv) { comp->compFloatingPointUsed = true; diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index a4a0e8fe615eb1..57260631c705a4 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -905,12 +905,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un if ((strip(corInfoType) == CORINFO_TYPE_VALUECLASS) LOONGARCH64_ONLY(&&(argSize <= MAX_PASS_MULTIREG_BYTES))) { -#if defined(TARGET_LOONGARCH64) - uint32_t floatFlagFields = info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd); - fpInfo = FpStructInRegistersInfo::FromOldFlags((StructFloatFieldInfoFlags)floatFieldFlags); -#else - fpInfo = info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(typeHnd); -#endif + fpInfo = GetPassFpStructInRegistersInfo(typeHnd); } if (fpInfo.flags != FpStruct::UseIntCallConv) @@ -1263,7 +1258,7 @@ void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, un } printf("\n"); } -#endif // DEBUG +#endif // DEBUG } // end if (canPassArgInRegisters) else { diff --git a/src/coreclr/jit/targetriscv64.cpp b/src/coreclr/jit/targetriscv64.cpp index e6e1447dcde048..567fc141b02e9a 100644 --- a/src/coreclr/jit/targetriscv64.cpp +++ b/src/coreclr/jit/targetriscv64.cpp @@ -68,7 +68,7 @@ ABIPassingInformation RiscV64Classifier::Classify(Compiler* comp, passedSize = structLayout->GetSize(); if (!structLayout->IsBlockLayout()) { - info = comp->info.compCompHnd->getRiscV64PassFpStructInRegistersInfo(structLayout->GetClassHandle()); + info = comp->GetPassFpStructInRegistersInfo(structLayout->GetClassHandle()); if ((info.flags & OnlyOne) != 0) { diff --git a/src/coreclr/jit/utils.h b/src/coreclr/jit/utils.h index f665a4e813eff6..1a28ba7b8dee57 100644 --- a/src/coreclr/jit/utils.h +++ b/src/coreclr/jit/utils.h @@ -1174,4 +1174,7 @@ bool CastFromFloatOverflows(float fromValue, var_types toType); bool CastFromDoubleOverflows(double fromValue, var_types toType); } // namespace CheckedOps +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_(x) + #endif // _UTILS_H_ diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 547a8aa0d11196..d0e8ac4d35d522 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -6373,7 +6373,7 @@ void MethodContext::recGetRiscV64PassFpStructInRegistersInfo(CORINFO_CLASS_HANDL void MethodContext::dmpGetRiscV64PassFpStructInRegistersInfo(DWORDLONG key, FpStructInRegistersInfo value) { - printf("GetRiscV64PassFpStructInRegistersInfo key %016" PRIX64 " value-%#02x-" + printf("GetRiscV64PassFpStructInRegistersInfo key %016" PRIX64 " value-%#03x-" "{%s, sizes={%u, %u}, offsets={%u, %u}, IntFieldKind=%s}\n", key, value.flags, value.FlagName(), value.Size1st(), value.Size2nd(), value.offset1st, value.offset2nd, value.IntFieldKindName()); diff --git a/src/coreclr/vm/methodtable.cpp b/src/coreclr/vm/methodtable.cpp index 609c322d150992..07b24bc1022173 100644 --- a/src/coreclr/vm/methodtable.cpp +++ b/src/coreclr/vm/methodtable.cpp @@ -3214,7 +3214,7 @@ static FpStructInRegistersInfo GetRiscV64PassFpStructInRegistersInfoImpl(TypeHan assert(info.IntFieldKind() == FpStruct::IntKind::Signed); LOG((LF_JIT, LL_EVERYTHING, "FpStructInRegistersInfo: " - "struct %s (%u bytes) can be passed with floating-point calling convention, flags=%#02x; " + "struct %s (%u bytes) can be passed with floating-point calling convention, flags=%#03x; " "%s, sizes={%u, %u}, offsets={%u, %u}, IntFieldKindMask=%s\n", (!th.IsTypeDesc() ? th.AsMethodTable() : th.AsNativeValueType())->GetDebugClassName(), th.GetSize(), info.flags, info.FlagName(), info.Size1st(), info.Size2nd(), info.offset1st, info.offset2nd, info.IntFieldKindName()