mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-02-10 00:55:55 +00:00
![Tony Wasserka](/assets/img/avatar_default.png)
This can be used to allow automatically handling structures that require special behavior for one member but are automatically repackable otherwise. The feature is enabled using the new custom_repack annotation and requires additional repacking functions to be defined in the host file for each customized member.
136 lines
4.5 KiB
C++
136 lines
4.5 KiB
C++
#pragma once
|
|
|
|
#include <clang/Frontend/TextDiagnosticPrinter.h>
|
|
#include <clang/Tooling/Tooling.h>
|
|
|
|
#include <llvm/Support/raw_os_ostream.h>
|
|
|
|
#include <optional>
|
|
|
|
/**
|
|
* Prints diagnostics to console like clang::TextDiagnosticPrinter.
|
|
* A copy of the first error message is stored so that it can be queried
|
|
* after compiling.
|
|
*/
|
|
class TestDiagnosticConsumer : public clang::TextDiagnosticPrinter {
|
|
bool silent;
|
|
|
|
std::optional<std::string> first_error;
|
|
|
|
public:
|
|
TestDiagnosticConsumer(bool silent_) : clang::TextDiagnosticPrinter(llvm::errs(), new clang::DiagnosticOptions), silent(silent_) {
|
|
|
|
}
|
|
|
|
void HandleDiagnostic(clang::DiagnosticsEngine::Level level,
|
|
const clang::Diagnostic& diag) override {
|
|
if (level >= clang::DiagnosticsEngine::Error && !first_error) {
|
|
llvm::SmallVector<char, 64> message;
|
|
diag.FormatDiagnostic(message);
|
|
first_error = std::string(message.begin(), message.end());
|
|
}
|
|
|
|
if (silent && level != clang::DiagnosticsEngine::Fatal) {
|
|
return;
|
|
}
|
|
|
|
clang::TextDiagnosticPrinter::HandleDiagnostic(level, diag);
|
|
}
|
|
|
|
std::optional<std::string> GetFirstError() const {
|
|
return first_error;
|
|
}
|
|
};
|
|
|
|
enum class GuestABI {
|
|
X86_32,
|
|
X86_64,
|
|
};
|
|
|
|
inline std::ostream& operator<<(std::ostream& os, GuestABI abi) {
|
|
if (abi == GuestABI::X86_32) {
|
|
os << "X86_32";
|
|
} else if (abi == GuestABI::X86_64) {
|
|
os << "X86_64";
|
|
}
|
|
return os;
|
|
}
|
|
|
|
/**
|
|
* Run the given ToolAction on the input code.
|
|
*
|
|
* The "silent" parameter is used to suppress non-fatal diagnostics in tests that expect failure
|
|
*/
|
|
inline void run_tool(clang::tooling::ToolAction& action, std::string_view code, bool silent = false, std::optional<GuestABI> guest_abi = std::nullopt) {
|
|
const char* memory_filename = "gen_input.cpp";
|
|
auto adjuster = clang::tooling::getClangStripDependencyFileAdjuster();
|
|
std::vector<std::string> args = { "clang-tool", "-fsyntax-only", "-std=c++20", "-Werror", "-I.", memory_filename };
|
|
if (CLANG_RESOURCE_DIR[0] != 0) {
|
|
args.push_back("-resource-dir");
|
|
args.push_back(CLANG_RESOURCE_DIR);
|
|
}
|
|
if (guest_abi == GuestABI::X86_64) {
|
|
args.push_back("-target");
|
|
args.push_back("x86_64-linux-gnu");
|
|
args.push_back("-isystem");
|
|
args.push_back("/usr/x86_64-linux-gnu/include/");
|
|
} else if (guest_abi == GuestABI::X86_32) {
|
|
args.push_back("-target");
|
|
args.push_back("i686-linux-gnu");
|
|
args.push_back("-isystem");
|
|
args.push_back("/usr/i686-linux-gnu/include/");
|
|
} else {
|
|
args.push_back("-DHOST");
|
|
}
|
|
|
|
// Corresponds to the content of GeneratorInterface.h
|
|
const char* common_header_code = R"(namespace fexgen {
|
|
struct returns_guest_pointer {};
|
|
struct custom_host_impl {};
|
|
struct callback_annotation_base { bool prevent_multiple; };
|
|
struct callback_stub : callback_annotation_base {};
|
|
|
|
struct custom_repack {};
|
|
struct emit_layout_wrappers {};
|
|
|
|
struct opaque_type {};
|
|
struct assume_compatible_data_layout {};
|
|
|
|
struct ptr_passthrough {};
|
|
|
|
} // namespace fexgen
|
|
|
|
template<auto, int, typename = void> struct fex_gen_param {};
|
|
|
|
template<typename>
|
|
struct fex_gen_type;
|
|
template<auto>
|
|
struct fex_gen_config;
|
|
|
|
)";
|
|
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> overlay_fs(new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()));
|
|
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> memory_fs(new llvm::vfs::InMemoryFileSystem);
|
|
overlay_fs->pushOverlay(memory_fs);
|
|
memory_fs->addFile(memory_filename, 0, llvm::MemoryBuffer::getMemBufferCopy(code));
|
|
memory_fs->addFile("thunks_common.h", 0, llvm::MemoryBuffer::getMemBufferCopy(common_header_code));
|
|
llvm::IntrusiveRefCntPtr<clang::FileManager> files(new clang::FileManager(clang::FileSystemOptions(), overlay_fs));
|
|
|
|
auto invocation = clang::tooling::ToolInvocation(args, &action, files.get(), std::make_shared<clang::PCHContainerOperations>());
|
|
|
|
TestDiagnosticConsumer consumer(silent);
|
|
invocation.setDiagnosticConsumer(&consumer);
|
|
|
|
// Process the actual ToolAction.
|
|
// NOTE: If the ToolAction throws an exception, clang will leak memory here.
|
|
invocation.run();
|
|
|
|
if (auto error = consumer.GetFirstError()) {
|
|
throw std::runtime_error(*error);
|
|
}
|
|
}
|
|
|
|
inline void run_tool(std::unique_ptr<clang::tooling::ToolAction> action, std::string_view code, bool silent = false, std::optional<GuestABI> guest_abi = std::nullopt) {
|
|
return run_tool(*action, code, silent, guest_abi);
|
|
}
|