diff --git a/.gitignore b/.gitignore index 84c6635..0c4f53c 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,5 @@ compile_commands.json cmake_install.cmake /CMakeCache.txt /CMakeFiles +.venv/ +*.sif diff --git a/CMakeLists.txt b/CMakeLists.txt index acfdb88..a8b2ea6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,8 +174,8 @@ endif() if(QIREE_BUILD_TESTS AND NOT GTest_FOUND) find_package(GTest) if(NOT GTest_FOUND) - message(SEND_ERROR - "Googletest (GTest) is required for testing but was not found" + message(FATAL_ERROR + "Googletest (GTest) is required for testing but was not found. Please install from source. Optional: If you are in a user environment, you can run `cmake -DCMAKE_INSTALL_PREFIX=../install -DBUILD_SHARED_LIBS=ON ..` in the googletest/build folder, which allows you to build without sudo. Then you can set `export CMAKE_PREFIX_PATH=$HOME/googletest/install so that QIR-EE can find the installation." ) endif() endif() diff --git a/src/qiree/Module.cc b/src/qiree/Module.cc index d771eb0..3c4775c 100644 --- a/src/qiree/Module.cc +++ b/src/qiree/Module.cc @@ -7,6 +7,7 @@ //---------------------------------------------------------------------------// #include "Module.hh" +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #include "Assert.hh" @@ -116,6 +118,7 @@ Module::Module(UPModule&& module) : module_{std::move(module)} //---------------------------------------------------------------------------// /*! * Construct with an LLVM module and an entry point. + * * Useful when there are multiple entry points. */ Module::Module(UPModule&& module, std::string const& entrypoint) @@ -155,7 +158,36 @@ Module::Module(std::string const& filename, std::string const& entrypoint) } //---------------------------------------------------------------------------// +/*! + * Read a module by parsing an in-memory LLVM IR string. + */ +std::unique_ptr Module::from_bytes(std::string const& content) +{ + llvm::SMDiagnostic err; + + // Create memory buffer from the in-memory IR content + std::unique_ptr buffer + = llvm::MemoryBuffer::getMemBuffer(content, "", false); + + // Parse the IR using LLVM context + auto llvm_module = llvm::parseIR(buffer->getMemBufferRef(), err, context()); + + if (!llvm_module) + { + err.print("qiree", llvm::errs()); + QIREE_VALIDATE(llvm_module, + << "Failed to parse QIR from in-memory content '" + << content << "'"); + } + + // Construct and return Module from parsed llvm::Module + return std::make_unique(std::move(llvm_module)); +} + +//! Construct in an empty state Module::Module() = default; + +// Default destructor and move Module::~Module() = default; Module::Module(Module&&) = default; Module& Module::operator=(Module&&) = default; diff --git a/src/qiree/Module.hh b/src/qiree/Module.hh index 21ed2df..8ce38c6 100644 --- a/src/qiree/Module.hh +++ b/src/qiree/Module.hh @@ -8,6 +8,7 @@ #pragma once #include +#include #include "Types.hh" @@ -32,15 +33,8 @@ class Module //!@} public: - // Default empty constructor - Module(); - // Externally defined defaults - ~Module(); - Module(Module&&); - Module& operator=(Module&&); - // Prevent copying - Module(Module const&) = delete; - Module& operator=(Module const&) = delete; + // Reading a module by parsing an in-memory LLVM IR string. + static std::unique_ptr from_bytes(std::string const & content); // Construct from an externally created LLVM module explicit Module(UPModule&& module); @@ -54,6 +48,20 @@ class Module // Construct with an LLVM IR file (bitcode or disassembled) and entry point Module(std::string const& filename, std::string const& entrypoint); + // Construct in an empty state + Module(); + + // Externally defined defaults + ~Module(); + Module(Module&&); + Module& operator=(Module&&); + + // Prevent copying + Module(Module const&) = delete; + Module& operator=(Module const&) = delete; + + //// ACCESSORS //// + // Process entry point attributes EntryPointAttrs load_entry_point_attrs() const; diff --git a/test/qiree/Module.test.cc b/test/qiree/Module.test.cc index 145661c..edb93f9 100644 --- a/test/qiree/Module.test.cc +++ b/test/qiree/Module.test.cc @@ -7,6 +7,11 @@ //---------------------------------------------------------------------------// #include "qiree/Module.hh" +#include +#include +#include +#include + #include "qiree_test.hh" namespace qiree @@ -109,6 +114,31 @@ TEST_F(ModuleTest, several_gates) EXPECT_FALSE(flags.dynamic_result_management); } +//---------------------------------------------------------------------------// +TEST_F(ModuleTest, parse_ir_from_file) { + + // Helper function to read a file and return its contents as a string to be fed into from_bytes + auto read_ll_file = [](const std::string& path) -> std::string { + std::ifstream file(path); + if (!file) throw std::runtime_error("Cannot open file: " + path); + std::ostringstream buf; + buf << file.rdbuf(); + return buf.str(); + }; + + // Read the LLVM IR from a file and parse it + std::string ir = read_ll_file(this->test_data_path("bell.ll")); + + std::unique_ptr m; + // Expect no exceptions during parsing + EXPECT_NO_THROW(m = Module::from_bytes(ir)); + ASSERT_TRUE(m); + + // Check flags + ASSERT_NE(ir.find("!llvm.module.flags"), std::string::npos) + << "QIR should contain a !llvm.module.flags metadata node"; +} + //---------------------------------------------------------------------------// } // namespace test } // namespace qiree