Skip to content

Commit

Permalink
unittests/ThunkLibs: Add various tests for structs repacking and for …
Browse files Browse the repository at this point in the history
…void parameters
  • Loading branch information
neobrain committed Oct 2, 2023
1 parent dad7b39 commit 9ebbb17
Showing 1 changed file with 88 additions and 6 deletions.
94 changes: 88 additions & 6 deletions unittests/ThunkLibs/generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ struct Fixture {
* It will be prepended to "code" before processing and also to the generator output.
*/
SourceWithAST run_thunkgen_guest(std::string_view prelude, std::string_view code, bool silent = false);
SourceWithAST run_thunkgen_host(std::string_view prelude, std::string_view code, bool silent = false);
SourceWithAST run_thunkgen_host(std::string_view prelude, std::string_view code, GuestABI = GuestABI::X86_64, bool silent = false);
GenOutput run_thunkgen(std::string_view prelude, std::string_view code, bool silent = false);

const std::string libname = "libtest";
Expand Down Expand Up @@ -236,13 +236,13 @@ SourceWithAST Fixture::run_thunkgen_guest(std::string_view prelude, std::string_
/**
* Generates host thunk library code from the given input
*/
SourceWithAST Fixture::run_thunkgen_host(std::string_view prelude, std::string_view code, bool silent) {
SourceWithAST Fixture::run_thunkgen_host(std::string_view prelude, std::string_view code, GuestABI guest_abi, bool silent) {
const std::string full_code = std::string { prelude } + std::string { code };

// These tests don't deal with data layout differences, so just run data
// layout analysis with host configuration
auto data_layout_analysis_factory = std::make_unique<AnalyzeDataLayoutActionFactory>();
run_tool(*data_layout_analysis_factory, full_code, silent);
run_tool(*data_layout_analysis_factory, full_code, silent, guest_abi);
auto& data_layout = data_layout_analysis_factory->GetDataLayout();

run_tool(std::make_unique<GenerateThunkLibsActionFactory>(libname, output_filenames, data_layout), full_code, silent);
Expand Down Expand Up @@ -282,17 +282,27 @@ SourceWithAST Fixture::run_thunkgen_host(std::string_view prelude, std::string_v
auto& filename = output_filenames.host;
{
std::ifstream file(filename);
const auto current_size = result.size();
const auto prelude_size = result.size();
const auto new_data_size = std::filesystem::file_size(filename);
result.resize(result.size() + new_data_size);
file.read(result.data() + current_size, result.size());
file.read(result.data() + prelude_size, result.size());

// Force all functions to be non-static, since having to define them
// would add a lot of noise to simple tests.
while (true) {
auto pos = result.find("static ", prelude_size);
if (pos == std::string::npos) {
break;
}
result.replace(pos, 6, " "); // Replace "static" with 6 spaces (avoiding reallocation)
}
}
return SourceWithAST { std::string { prelude } + result };
}

Fixture::GenOutput Fixture::run_thunkgen(std::string_view prelude, std::string_view code, bool silent) {
return { run_thunkgen_guest(prelude, code, silent),
run_thunkgen_host(prelude, code, silent) };
run_thunkgen_host(prelude, code, GuestABI::X86_64, silent) };
}

TEST_CASE_METHOD(Fixture, "Trivial") {
Expand Down Expand Up @@ -525,3 +535,75 @@ TEST_CASE_METHOD(Fixture, "VariadicFunctionsWithoutAnnotation") {
"template<auto> struct fex_gen_config {};\n"
"template<> struct fex_gen_config<func> {};\n", true));
}

TEST_CASE_METHOD(Fixture, "StructRepacking") {
auto guest_abi = GENERATE(GuestABI::X86_32, GuestABI::X86_64);
INFO(guest_abi);

// All tests use the same function, but the prelude defining its parameter type "A" varies
const std::string code =
"#include <thunks_common.h>\n"
"void func(A*);\n"
"template<auto> struct fex_gen_config {};\n"
"template<> struct fex_gen_config<func> : fexgen::custom_host_impl {};\n";

SECTION("Pointer to struct with consistent data layout") {
CHECK_NOTHROW(run_thunkgen_host("struct A { int a; };\n", code, guest_abi));
}

SECTION("Pointer to struct with unannotated pointer member with inconsistent data layout") {
const auto prelude =
"#ifdef HOST\n"
"struct B { int a; };\n"
"#else\n"
"struct B { int b; };\n"
"#endif\n"
"struct A { B* a; };\n";

SECTION("Parameter unannotated") {
CHECK_THROWS(run_thunkgen_host(prelude, code, guest_abi, true));
}
}

SECTION("Pointer to struct with pointer member of opaque type") {
const auto prelude =
"struct B;\n"
"struct A { B* a; };\n";

// Unannotated
REQUIRE_THROWS_WITH(run_thunkgen_host(prelude, code, guest_abi), Catch::Contains("incomplete type"));
}
}

TEST_CASE_METHOD(Fixture, "VoidPointerParameter") {
auto guest_abi = GENERATE(GuestABI::X86_32, GuestABI::X86_64);
INFO(guest_abi);

SECTION("Unannotated") {
const char* code =
"#include <thunks_common.h>\n"
"void func(void*);\n"
"template<> struct fex_gen_config<func> {};\n";
if (guest_abi == GuestABI::X86_32) {
// TODO: Currently not considered an error
// CHECK_THROWS_WITH(run_thunkgen_host("", code, guest_abi, true), Catch::Contains("unsupported parameter type", Catch::CaseSensitive::No));
} else {
// Pointee data is assumed to be compatible on 64-bit
CHECK_NOTHROW(run_thunkgen_host("", code, guest_abi));
}
}

SECTION("Unannotated in struct") {
const char* prelude =
"struct A { void* a; };\n";
const char* code =
"#include <thunks_common.h>\n"
"void func(A*);\n"
"template<> struct fex_gen_config<func> {};\n";
if (guest_abi == GuestABI::X86_32) {
CHECK_THROWS_WITH(run_thunkgen_host(prelude, code, guest_abi, true), Catch::Contains("unsupported parameter type", Catch::CaseSensitive::No));
} else {
CHECK_NOTHROW(run_thunkgen_host(prelude, code, guest_abi));
}
}
}

0 comments on commit 9ebbb17

Please sign in to comment.