From 5dffc010f0746a5d9e0ee95ee9504fbbb1b6cee1 Mon Sep 17 00:00:00 2001 From: Jeremi Kurdek <59935235+jkurdek@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:12:51 +0200 Subject: [PATCH 1/2] Fix lowering support for inline arrays (#107744) --- src/mono/mono/metadata/marshal.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mono/mono/metadata/marshal.c b/src/mono/mono/metadata/marshal.c index d0e0b2327907c..0ad51eae802a9 100644 --- a/src/mono/mono/metadata/marshal.c +++ b/src/mono/mono/metadata/marshal.c @@ -6798,11 +6798,15 @@ static void record_struct_field_physical_lowering (guint8* lowered_bytes, MonoTy static void record_inlinearray_struct_physical_lowering (guint8* lowered_bytes, MonoClass* klass, guint32 offset) { + int align; + int type_offset = MONO_ABI_SIZEOF (MonoObject); + // Get the first field and record its physical lowering N times - MonoClassField* field = mono_class_get_fields_internal (klass, NULL); + gpointer iter = NULL; + MonoClassField* field = mono_class_get_fields_internal (klass, &iter); MonoType* fieldType = field->type; for (int i = 0; i < m_class_inlinearray_value(klass); ++i) { - record_struct_field_physical_lowering(lowered_bytes, fieldType, offset + m_field_get_offset(field) + i * mono_type_size(fieldType, NULL)); + record_struct_field_physical_lowering(lowered_bytes, fieldType, offset + m_field_get_offset(field) + i * mono_type_size(fieldType, &align) - type_offset); } } From 5d4c664b63017845757db513486ec670245d3ad3 Mon Sep 17 00:00:00 2001 From: Jeremi Kurdek <59935235+jkurdek@users.noreply.github.com> Date: Tue, 1 Oct 2024 17:55:59 +0200 Subject: [PATCH 2/2] [Swift interop] Add inline array struct lowering tests (#107859) * [swift interop] Add inline array tests * Rename test suite --- src/tests/Interop/CMakeLists.txt | 1 + .../Swift/SwiftInlineArray/CMakeLists.txt | 24 +++ .../SwiftInlineArray/SwiftInlineArray.cs | 153 ++++++++++++++++++ .../SwiftInlineArray/SwiftInlineArray.csproj | 16 ++ .../SwiftInlineArray/SwiftInlineArray.swift | 102 ++++++++++++ 5 files changed, 296 insertions(+) create mode 100644 src/tests/Interop/Swift/SwiftInlineArray/CMakeLists.txt create mode 100644 src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.cs create mode 100644 src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.csproj create mode 100644 src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.swift diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index 4a6bca7b8fad8..4137b5ba16df6 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -110,4 +110,5 @@ if(CLR_CMAKE_TARGET_APPLE) add_subdirectory(Swift/SwiftInvalidCallConv) add_subdirectory(Swift/SwiftRetAbiStress) add_subdirectory(Swift/SwiftSelfContext) + add_subdirectory(Swift/SwiftInlineArray) endif() diff --git a/src/tests/Interop/Swift/SwiftInlineArray/CMakeLists.txt b/src/tests/Interop/Swift/SwiftInlineArray/CMakeLists.txt new file mode 100644 index 0000000000000..c8ad8b4654981 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftInlineArray/CMakeLists.txt @@ -0,0 +1,24 @@ +project(SwiftInlineArray) +include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake") + +set(SOURCE SwiftInlineArray) + +if (NOT SWIFT_COMPILER_TARGET AND CLR_CMAKE_TARGET_APPLE) + set(SWIFT_PLATFORM "macosx") + set(SWIFT_PLATFORM_SUFFIX "") + if (NOT CMAKE_OSX_DEPLOYMENT_TARGET) + set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0") + endif() + set(SWIFT_DEPLOYMENT_TARGET ${CMAKE_OSX_DEPLOYMENT_TARGET}) + set(SWIFT_COMPILER_TARGET "${CMAKE_OSX_ARCHITECTURES}-apple-${SWIFT_PLATFORM}${SWIFT_DEPLOYMENT_TARGET}${SWIFT_PLATFORM_SUFFIX}") +endif() + +add_custom_target(${SOURCE} ALL + COMMAND xcrun swiftc -target ${SWIFT_COMPILER_TARGET} -emit-library ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift -o ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.swift + COMMENT "Generating ${SOURCE} library" +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib${SOURCE}.dylib + DESTINATION bin +) diff --git a/src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.cs b/src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.cs new file mode 100644 index 0000000000000..c3937290d6839 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.cs @@ -0,0 +1,153 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Swift; +using Xunit; + +public class SwiftInlineArray +{ + private const string SwiftLib = "libSwiftInlineArray.dylib"; + + [InlineArray(32)] + struct F0 + { + private byte _element0; + } + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftInlineArray10swiftFunc02a0SiAA2F0V_tF")] + private static extern nint SwiftFunc0(F0 a0); + + [Fact] + public static unsafe void TestFuncWithByteInlineArray() + { + F0 a0 = default; + byte* ptr = (byte*)&a0; + + ptr[0] = (byte)122; + ptr[1] = (byte)223; + ptr[2] = (byte)66; + ptr[3] = (byte)1; + ptr[4] = (byte)135; + ptr[5] = (byte)209; + ptr[6] = (byte)54; + ptr[7] = (byte)221; + ptr[8] = (byte)24; + ptr[9] = (byte)104; + ptr[10] = (byte)21; + ptr[11] = (byte)222; + ptr[12] = (byte)156; + ptr[13] = (byte)241; + ptr[14] = (byte)97; + ptr[15] = (byte)141; + ptr[16] = (byte)239; + ptr[17] = (byte)184; + ptr[18] = (byte)69; + ptr[19] = (byte)247; + ptr[20] = (byte)134; + ptr[21] = (byte)121; + ptr[22] = (byte)204; + ptr[23] = (byte)45; + ptr[24] = (byte)112; + ptr[25] = (byte)166; + ptr[26] = (byte)220; + ptr[27] = (byte)221; + ptr[28] = (byte)86; + ptr[29] = (byte)197; + ptr[30] = (byte)178; + ptr[31] = (byte)29; + + + long result = SwiftFunc0(a0); + Assert.Equal(8091295595945034296, result); + Console.WriteLine("OK"); + } + + + [InlineArray(8)] + struct F1 + { + private int _element0; + } + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftInlineArray10swiftFunc12a0SiAA2F1V_tF")] + private static extern nint SwiftFunc1(F1 a0); + + [Fact] + public static unsafe void TestFuncWithIntInlineArray() + { + F1 a0 = default; + int* ptr = (int*)&a0; + + ptr[0] = (int)-1172606642; + ptr[1] = (int)2004011304; + ptr[2] = (int)-1751053775; + ptr[3] = (int)-1361536584; + ptr[4] = (int)1578364919; + ptr[5] = (int)1205365715; + ptr[6] = (int)-883274792; + ptr[7] = (int)-550660826; + + + long result = SwiftFunc1(a0); + Assert.Equal(8444261314257660732, result); + Console.WriteLine("OK"); + } + + [InlineArray(6)] + struct F2 + { + private ulong _element0; + } + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftInlineArray10swiftFunc22a0SiAA2F2V_tF")] + private static extern nint SwiftFunc2(F2 a0); + + [Fact] + public static unsafe void TestFuncWithLargeInlineArray() + { + F2 a0 = default; + ulong* ptr = (ulong*)&a0; + + ptr[0] = (ulong)163054281557578879; + ptr[1] = (ulong)3715665182263428629; + ptr[2] = (ulong)15352099497683712058; + ptr[3] = (ulong)9456667702469177637; + ptr[4] = (ulong)5768234261922277852; + ptr[5] = (ulong)17154681812528174574; + + + long result = SwiftFunc2(a0); + Assert.Equal(-627554439188077294, result); + Console.WriteLine("OK"); + } + + [InlineArray(1)] + struct F3 + { + private byte _element0; + } + + [UnmanagedCallConv(CallConvs = new Type[] { typeof(CallConvSwift) })] + [DllImport(SwiftLib, EntryPoint = "$s16SwiftInlineArray10swiftFunc32a0SiAA2F3V_tF")] + private static extern nint SwiftFunc3(F3 a0); + + [Fact] + public static unsafe void TestFuncWithSingleElementInlineArray() + { + F3 a0 = default; + byte* ptr = (byte*)&a0; + + ptr[0] = (byte)177; + + + long result = SwiftFunc3(a0); + Assert.Equal(-5808468912223652740, result); + Console.WriteLine("OK"); + } +} diff --git a/src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.csproj b/src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.csproj new file mode 100644 index 0000000000000..89eda99352fd2 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.csproj @@ -0,0 +1,16 @@ + + + + true + true + + true + + + + + + + + + diff --git a/src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.swift b/src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.swift new file mode 100644 index 0000000000000..e5ddefd2e1038 --- /dev/null +++ b/src/tests/Interop/Swift/SwiftInlineArray/SwiftInlineArray.swift @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +struct HasherFNV1a { + + private var hash: UInt = 14_695_981_039_346_656_037 + private let prime: UInt = 1_099_511_628_211 + + mutating func combine(_ val: T) { + for byte in withUnsafeBytes(of: val, Array.init) { + hash ^= UInt(byte) + hash = hash &* prime + } + } + + func finalize() -> Int { + Int(truncatingIfNeeded: hash) + } +} + +@frozen public struct F0 { + public var elements: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) +} + +public func swiftFunc0(a0: F0) -> Int { + var hasher = HasherFNV1a() + hasher.combine(a0.elements.0); + hasher.combine(a0.elements.1); + hasher.combine(a0.elements.2); + hasher.combine(a0.elements.3); + hasher.combine(a0.elements.4); + hasher.combine(a0.elements.5); + hasher.combine(a0.elements.6); + hasher.combine(a0.elements.7); + hasher.combine(a0.elements.8); + hasher.combine(a0.elements.9); + hasher.combine(a0.elements.10); + hasher.combine(a0.elements.11); + hasher.combine(a0.elements.12); + hasher.combine(a0.elements.13); + hasher.combine(a0.elements.14); + hasher.combine(a0.elements.15); + hasher.combine(a0.elements.16); + hasher.combine(a0.elements.17); + hasher.combine(a0.elements.18); + hasher.combine(a0.elements.19); + hasher.combine(a0.elements.20); + hasher.combine(a0.elements.21); + hasher.combine(a0.elements.22); + hasher.combine(a0.elements.23); + hasher.combine(a0.elements.24); + hasher.combine(a0.elements.25); + hasher.combine(a0.elements.26); + hasher.combine(a0.elements.27); + hasher.combine(a0.elements.28); + hasher.combine(a0.elements.29); + hasher.combine(a0.elements.30); + hasher.combine(a0.elements.31); + return hasher.finalize() +} + +@frozen public struct F1 { + public var elements: (Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32) +} + +public func swiftFunc1(a0: F1) -> Int { + var hasher = HasherFNV1a() + hasher.combine(a0.elements.0); + hasher.combine(a0.elements.1); + hasher.combine(a0.elements.2); + hasher.combine(a0.elements.3); + hasher.combine(a0.elements.4); + hasher.combine(a0.elements.5); + hasher.combine(a0.elements.6); + hasher.combine(a0.elements.7); + return hasher.finalize() +} + +@frozen public struct F2 { + public var elements: (UInt64, UInt64, UInt64, UInt64, UInt64, UInt64) +} + +public func swiftFunc2(a0: F2) -> Int { + var hasher = HasherFNV1a() + hasher.combine(a0.elements.0); + hasher.combine(a0.elements.1); + hasher.combine(a0.elements.2); + hasher.combine(a0.elements.3); + hasher.combine(a0.elements.4); + hasher.combine(a0.elements.5); + return hasher.finalize() +} + +@frozen public struct F3 { + public var element: UInt8 +} + +public func swiftFunc3(a0: F3) -> Int { + var hasher = HasherFNV1a() + hasher.combine(a0.element); + return hasher.finalize() +}