From 585320093ab48599b474b1779d0e525d45d20f44 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 5 Sep 2024 17:05:33 +0200 Subject: [PATCH] LibraryForwarding: Add custom repacking tests for Vulkan-like scenarios --- ThunkLibs/libfex_thunk_test/Host.cpp | 85 ++++++++++++++++++- ThunkLibs/libfex_thunk_test/api.h | 33 +++++++ ThunkLibs/libfex_thunk_test/lib.cpp | 24 ++++++ .../libfex_thunk_test_interface.cpp | 10 +++ .../tests/thunks/thunk_testlib.cpp | 23 +++++ 5 files changed, 174 insertions(+), 1 deletion(-) diff --git a/ThunkLibs/libfex_thunk_test/Host.cpp b/ThunkLibs/libfex_thunk_test/Host.cpp index 42043bd0b..92441ffe3 100644 --- a/ThunkLibs/libfex_thunk_test/Host.cpp +++ b/ThunkLibs/libfex_thunk_test/Host.cpp @@ -4,9 +4,10 @@ tags: thunklibs|fex_thunk_test $end_info$ */ -#include #include +#include + #include "common/Host.h" #include "api.h" @@ -29,4 +30,86 @@ bool fex_custom_repack_exit(guest_layout& to, const host_lay return false; } +template +static const TestBaseStruct* convert(const TestBaseStruct* source) { + // Using malloc here since no easily available type information is available at the time of destruction. + auto guest_next = reinterpret_cast*>((void*)source); + auto child_mem = (char*)aligned_alloc(alignof(host_layout), sizeof(host_layout)); + auto child = new (child_mem) host_layout {*guest_next}; + + fex_custom_repack_entry(*child, *reinterpret_cast*>((void*)(source))); + + return (const TestBaseStruct*)child; +} + +template +static void convert_to_guest(void* into, const TestBaseStruct* from) { + auto typed_into = (guest_layout*)into; + auto oldNext = typed_into->data.Next; + *typed_into = to_guest(to_host_layout(*(Type*)from)); + typed_into->data.Next = oldNext; + + fex_custom_repack_exit(*typed_into, to_host_layout(*(Type*)from)); +} + +template +inline constexpr std::pair> converters = { + TypeIndex, + {convert, convert_to_guest}}; + +static std::unordered_map> next_handlers { + converters, + converters, +}; + +static void default_fex_custom_repack_entry(TestBaseStruct& into, const guest_layout* from) { + if (!from->data.Next.get_pointer()) { + into.Next = nullptr; + return; + } + auto typed_source = reinterpret_cast*>(from->data.Next.get_pointer()); + + auto next_handler = next_handlers.at(StructType {typed_source->data.Type.data}); + + into.Next = (TestBaseStruct*)next_handler.first((const TestBaseStruct*)typed_source); +} + +static void default_fex_custom_repack_reverse(guest_layout& into, const TestBaseStruct* from) { + auto NextHost = from->Next; + if (!NextHost) { + return; + } + + auto next_handler = next_handlers.at(static_cast(into.data.Next.get_pointer()->data.Type.data)); + next_handler.second((void*)into.data.Next.get_pointer(), from->Next); + + free((void*)NextHost); +} + +#define CREATE_INFO_DEFAULT_CUSTOM_REPACK(name) \ + void fex_custom_repack_entry(host_layout& into, const guest_layout& from) { \ + default_fex_custom_repack_entry(*(TestBaseStruct*)&into.data, reinterpret_cast*>(&from)); \ + } \ + \ + bool fex_custom_repack_exit(guest_layout& into, const host_layout& from) { \ + auto prev_next = into.data.Next; \ + default_fex_custom_repack_reverse(*reinterpret_cast*>(&into), \ + &reinterpret_cast(from.data)); \ + into = to_guest(from); \ + into.data.Next = prev_next; \ + return true; \ + } + +CREATE_INFO_DEFAULT_CUSTOM_REPACK(TestStruct1) +CREATE_INFO_DEFAULT_CUSTOM_REPACK(TestStruct2) + +void fex_custom_repack_entry(host_layout&, const guest_layout&) { + std::abort(); +} + +bool fex_custom_repack_exit(guest_layout&, const host_layout&) { + std::abort(); + return false; +} + EXPORTS(libfex_thunk_test) diff --git a/ThunkLibs/libfex_thunk_test/api.h b/ThunkLibs/libfex_thunk_test/api.h index 25f940d2d..759bde3ae 100644 --- a/ThunkLibs/libfex_thunk_test/api.h +++ b/ThunkLibs/libfex_thunk_test/api.h @@ -5,6 +5,7 @@ #pragma once #include +#include extern "C" { @@ -73,4 +74,36 @@ enum DivType : uint8_t {}; enum DivType : uint32_t {}; #endif int FunctionWithDivergentSignature(DivType, DivType, DivType, DivType); + + +/// Interfaces used to test Vulkan-like APIs + +// Equivalent of VkStructureType +enum class StructType { + Struct1, + Struct2, +}; + +// Equivalent of VkBaseInStructure +struct TestBaseStruct { + TestBaseStruct* Next; + StructType Type; +}; + +// Equivalent of e.g. VkImageCreateInfo +struct TestStruct1 { + const void* Next; + StructType Type; // StructType::Struct1 + uint8_t Data2; + uint8_t pad0[3]; + int Data1; +}; + +struct TestStruct2 { + const void* Next; + StructType Type; // StructType::Struct2 + int Data1; +}; + +int ReadData1(TestStruct1*, int depth); } diff --git a/ThunkLibs/libfex_thunk_test/lib.cpp b/ThunkLibs/libfex_thunk_test/lib.cpp index 1058bbc3a..648144172 100644 --- a/ThunkLibs/libfex_thunk_test/lib.cpp +++ b/ThunkLibs/libfex_thunk_test/lib.cpp @@ -1,5 +1,8 @@ #include "api.h" +#include +#include + extern "C" { uint32_t GetDoubledValue(uint32_t input) { @@ -59,4 +62,25 @@ int FunctionWithDivergentSignature(DivType a, DivType b, DivType c, DivType d) { return ((uint8_t)a << 24) | ((uint8_t)b << 16) | ((uint8_t)c << 8) | (uint8_t)d; } +int ReadData1(TestStruct1* data, int depth) { + auto* base = (TestBaseStruct*)data; + for (int i = 0; i != depth; ++i) { + if (!base) { + return -1; + } + base = base->Next; + } + if (!base) { + return -1; + } + + switch (base->Type) { + case StructType::Struct1: return ((TestStruct1*)base)->Data1; + + case StructType::Struct2: return ((TestStruct2*)base)->Data1; + + default: return -2; + } +} + } // extern "C" diff --git a/ThunkLibs/libfex_thunk_test/libfex_thunk_test_interface.cpp b/ThunkLibs/libfex_thunk_test/libfex_thunk_test_interface.cpp index dc01cbfc0..bcf301921 100644 --- a/ThunkLibs/libfex_thunk_test/libfex_thunk_test_interface.cpp +++ b/ThunkLibs/libfex_thunk_test/libfex_thunk_test_interface.cpp @@ -53,3 +53,13 @@ struct fex_gen_config {}; template<> struct fex_gen_config {}; + +template<> +struct fex_gen_config<&TestBaseStruct::Next> : fexgen::custom_repack {}; +template<> +struct fex_gen_config<&TestStruct1::Next> : fexgen::custom_repack {}; +template<> +struct fex_gen_config<&TestStruct2::Next> : fexgen::custom_repack {}; + +template<> +struct fex_gen_config {}; diff --git a/unittests/FEXLinuxTests/tests/thunks/thunk_testlib.cpp b/unittests/FEXLinuxTests/tests/thunks/thunk_testlib.cpp index 8ed1bcd91..a2991a817 100644 --- a/unittests/FEXLinuxTests/tests/thunks/thunk_testlib.cpp +++ b/unittests/FEXLinuxTests/tests/thunks/thunk_testlib.cpp @@ -36,6 +36,8 @@ struct Fixture { GET_SYMBOL(RanCustomRepack); GET_SYMBOL(FunctionWithDivergentSignature); + + GET_SYMBOL(ReadData1); }; TEST_CASE_METHOD(Fixture, "Trivial") { @@ -89,3 +91,24 @@ TEST_CASE_METHOD(Fixture, "Assisted struct repacking") { TEST_CASE_METHOD(Fixture, "Function signature with differing parameter sizes") { CHECK(FunctionWithDivergentSignature(DivType {1}, DivType {2}, DivType {3}, DivType {4}) == 0x01020304); } + +// Test Vulkan-like linked lists +TEST_CASE_METHOD(Fixture, "Assisted repacking of linked lists") { + const int s2_data = 0xcddeeff; + TestStruct2 s2 { + .Next = nullptr, + .Type = StructType::Struct2, + .Data1 = s2_data, + }; + + const int s1_data = 0x1234567; + TestStruct1 s1 { + .Next = &s2, + .Type = StructType::Struct1, + .Data2 = 0xab, + .Data1 = s1_data, + }; + + CHECK(ReadData1(&s1, 0) == s1_data); + CHECK(ReadData1(&s1, 1) == s2_data); +}