From a6138170cbca9e5f13964fd969b48f396dda5a6b Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 11 Aug 2023 12:48:42 -0700 Subject: [PATCH 1/2] Add LLVM support for JIT compiling (#174) * add llvm to cmake scripts and add Dockerfile * add JIT compiler * add scalar JIT type tests * add pointer type JIT tests * add loops to jit function class * add JIT function tests * fix JIT loop bug * Update include/micm/jit/jit_function.hpp Co-authored-by: Kyle Shores * Update cmake/test_util.cmake Co-authored-by: Kyle Shores * add assert on regenerating JIT function --------- Co-authored-by: Kyle Shores --- .github/workflows/test.yml | 3 +- CMakeLists.txt | 1 + Dockerfile.coverage | 3 + Dockerfile.llvm | 29 ++ cmake/dependencies.cmake | 24 ++ cmake/test_util.cmake | 5 +- include/micm/jit/jit_compiler.hpp | 149 +++++++++ include/micm/jit/jit_function.hpp | 312 +++++++++++++++++++ include/micm/process/jit_process_set.hpp | 63 ++++ test/unit/CMakeLists.txt | 3 + test/unit/jit/CMakeLists.txt | 9 + test/unit/jit/test_jit_function.cpp | 381 +++++++++++++++++++++++ test/valgrind.supp | 13 + 13 files changed, 993 insertions(+), 2 deletions(-) create mode 100644 Dockerfile.llvm create mode 100644 include/micm/jit/jit_compiler.hpp create mode 100644 include/micm/jit/jit_function.hpp create mode 100644 include/micm/process/jit_process_set.hpp create mode 100644 test/unit/jit/CMakeLists.txt create mode 100644 test/unit/jit/test_jit_function.cpp diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2965ee42f..9da8262f7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,10 +14,11 @@ jobs: dockerfile: - Dockerfile - Dockerfile.coverage + - Dockerfile.llvm - Dockerfile.memcheck - Dockerfile.no_json - Dockerfile.nvhpc - - Dockerfile.intel + # - Dockerfile.intel # intel image is too large for GH action # - Dockerfile.openmp # - Dockerfile.mpi steps: diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ffabbb80..88ed31ba9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,7 @@ option(ENABLE_JSON "Enable json configureation file reading" ON) option(BUILD_DOCS "Build the documentation" OFF) option(ENABLE_CUDA "Build with Cuda support" OFF) option(ENABLE_OPENACC "Build with OpenACC Support" OFF) +option(ENABLE_LLVM "Build with LLVM support for JIT-compiling" OFF) include(CMakeDependentOption) # Option to collect custom OpenACC flags diff --git a/Dockerfile.coverage b/Dockerfile.coverage index 9f9145f7d..fdd8e78bc 100644 --- a/Dockerfile.coverage +++ b/Dockerfile.coverage @@ -7,6 +7,8 @@ RUN dnf -y update \ git \ lcov \ make \ + zlib-devel \ + llvm-devel \ openmpi-devel \ && dnf clean all @@ -24,6 +26,7 @@ RUN mkdir /build \ -D CMAKE_BUILD_TYPE=debug \ -D ENABLE_CLANG_TIDY:BOOL=FALSE \ -D ENABLE_COVERAGE:BOOL=TRUE \ + -D ENABLE_LLVM:BOOL=TRUE \ # -D ENABLE_MPI:BOOL=TRUE \ # -D ENABLE_OPENMP:BOOL=TRUE \ ../micm \ diff --git a/Dockerfile.llvm b/Dockerfile.llvm new file mode 100644 index 000000000..78767b7a8 --- /dev/null +++ b/Dockerfile.llvm @@ -0,0 +1,29 @@ +FROM fedora:37 + +RUN dnf -y update \ + && dnf -y install \ + cmake \ + gcc-c++ \ + gdb \ + git \ + make \ + zlib-devel \ + llvm-devel \ + valgrind \ + && dnf clean all + +# copy the MICM code +COPY . /micm/ + +# build the library and run the tests +RUN mkdir /build \ + && cd /build \ + && cmake \ + -D CMAKE_BUILD_TYPE=debug \ + -D ENABLE_CLANG_TIDY:BOOL=FALSE \ + -D ENABLE_LLVM:BOOL=TRUE \ + -D ENABLE_MEMCHECK:BOOL=TRUE \ + ../micm \ + && make install -j 8 + +WORKDIR /build \ No newline at end of file diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index bb84c438c..b5fe2d8c5 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -92,3 +92,27 @@ endif() if(ENABLE_OPENACC) find_package(OpenACC REQUIRED) endif() + +################################################################################ +# LLVM Support +# +# TODO: Try to use fetch content for LLVM libraries + +if(ENABLE_LLVM) + find_package(LLVM REQUIRED CONFIG) + if(LLVM_FOUND) + message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") + message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + + include_directories(${LLVM_INCLUDE_DIRS}) + separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) + add_definitions(${LLVM_DEFINITIONS_LIST}) + + llvm_map_components_to_libnames(llvm_libs support core orcjit native irreader) + else() + set(LLVM_CMD "llvm-config --cxxflags --ldflags --system-libs --libs support core orcjit native irreader | tr '\\n' ' '") + execute_process(COMMAND bash "-c" ${LLVM_CMD} + OUTPUT_VARIABLE llvm_libs) + separate_arguments(llvm_libs) + endif() +endif() \ No newline at end of file diff --git a/cmake/test_util.cmake b/cmake/test_util.cmake index 0374dd5b9..eb843efc6 100644 --- a/cmake/test_util.cmake +++ b/cmake/test_util.cmake @@ -27,7 +27,10 @@ function(create_standard_test) if(ENABLE_JSON) target_link_libraries(test_${TEST_NAME} PRIVATE nlohmann_json::nlohmann_json) - target_compile_definitions(test_${TEST_NAME} PUBLIC USE_JSON) + endif() + + if(ENABLE_LLVM) + target_link_libraries(test_${TEST_NAME} PRIVATE ${llvm_libs}) endif() if(NOT DEFINED TEST_WORKING_DIRECTORY) diff --git a/include/micm/jit/jit_compiler.hpp b/include/micm/jit/jit_compiler.hpp new file mode 100644 index 000000000..356f0fbf8 --- /dev/null +++ b/include/micm/jit/jit_compiler.hpp @@ -0,0 +1,149 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +// +// Based on examples from the LLVM Project, +// under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#pragma once + +#include + +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/CompileUtils.h" +#include "llvm/ExecutionEngine/Orc/Core.h" +#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" +#include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h" +#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" +#include "llvm/ExecutionEngine/Orc/IRTransformLayer.h" +#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" +#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Transforms/InstCombine/InstCombine.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/Transforms/Scalar/GVN.h" + +namespace micm +{ + + class JitCompiler + { + private: + std::unique_ptr execution_session_; + + llvm::DataLayout data_layout_; + llvm::orc::MangleAndInterner mangle_; + + llvm::orc::RTDyldObjectLinkingLayer object_layer_; + llvm::orc::IRCompileLayer compile_layer_; + llvm::orc::IRTransformLayer optimize_layer_; + + llvm::orc::JITDylib &main_lib_; + + public: + JitCompiler( + std::unique_ptr execution_session, + llvm::orc::JITTargetMachineBuilder machine_builder, + llvm::DataLayout data_layout) + : execution_session_(std::move(execution_session)), + data_layout_(std::move(data_layout)), + mangle_(*this->execution_session_, this->data_layout_), + object_layer_(*this->execution_session_, []() { return std::make_unique(); }), + compile_layer_( + *this->execution_session_, + object_layer_, + std::make_unique(std::move(machine_builder))), + optimize_layer_(*this->execution_session_, compile_layer_, OptimizeModule), + main_lib_(this->execution_session_->createBareJITDylib("
")) + { + main_lib_.addGenerator( + llvm::cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(data_layout_.getGlobalPrefix()))); + } + + ~JitCompiler() + { + if (auto Err = execution_session_->endSession()) + execution_session_->reportError(std::move(Err)); + } + + static llvm::Expected> create() + { + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); + + auto EPC = llvm::orc::SelfExecutorProcessControl::Create(); + if (!EPC) + return EPC.takeError(); + + auto execution_session = std::make_unique(std::move(*EPC)); + + llvm::orc::JITTargetMachineBuilder machine_builder(execution_session->getExecutorProcessControl().getTargetTriple()); + + auto data_layout = machine_builder.getDefaultDataLayoutForTarget(); + if (!data_layout) + return data_layout.takeError(); + + return std::make_shared( + std::move(execution_session), std::move(machine_builder), std::move(*data_layout)); + } + + const llvm::DataLayout &GetDataLayout() const + { + return data_layout_; + } + + llvm::orc::JITDylib &GetMainJITDylib() + { + return main_lib_; + } + + llvm::Error AddModule( + llvm::orc::ThreadSafeModule threadsafe_module, + llvm::orc::ResourceTrackerSP resource_tracker = nullptr) + { + if (!resource_tracker) + resource_tracker = main_lib_.getDefaultResourceTracker(); + return optimize_layer_.add(resource_tracker, std::move(threadsafe_module)); + } + + llvm::Expected Lookup(llvm::StringRef name) + { + return execution_session_->lookup({ &main_lib_ }, mangle_(name.str())); + } + + private: + static llvm::Expected OptimizeModule( + llvm::orc::ThreadSafeModule threadsafe_module, + const llvm::orc::MaterializationResponsibility &responsibility) + { + threadsafe_module.withModuleDo( + [](llvm::Module &module) + { + // Create a function pass manager. + auto pass_manager = std::make_unique(&module); + + // Add some optimizations. + pass_manager->add(llvm::createInstructionCombiningPass()); + pass_manager->add(llvm::createReassociatePass()); + pass_manager->add(llvm::createGVNPass()); + pass_manager->add(llvm::createCFGSimplificationPass()); + pass_manager->doInitialization(); + + // Run the optimizations over all functions in the module being added to + // the JIT. + for (auto &function : module) + pass_manager->run(function); + }); + + return std::move(threadsafe_module); + } + }; + +} // end namespace micm \ No newline at end of file diff --git a/include/micm/jit/jit_function.hpp b/include/micm/jit/jit_function.hpp new file mode 100644 index 000000000..b7ba12fee --- /dev/null +++ b/include/micm/jit/jit_function.hpp @@ -0,0 +1,312 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +#include +#include +#include + +#include "jit_compiler.hpp" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/Target/TargetMachine.h" + +namespace micm +{ + + /// @brief Types used in JIT functions + enum class JitType + { + Int32, + Int32Ptr, + Int64, + Int64Ptr, + Float, + FloatPtr, + Double, + DoublePtr, + Bool, + Void + }; + + /// @brief JIT function argument + struct JitArgument + { + std::string name_; + llvm::Type* type_; + llvm::Value* arg_; + llvm::AllocaInst* alloca_; + llvm::Value* ptr_; + }; + + /// @brief JIT function loop + struct JitLoop + { + std::string name_; + llvm::BasicBlock* block_; + llvm::PHINode* index_; + llvm::Value* step_; + llvm::Value* end_; + llvm::BasicBlock* prior_block_; + llvm::BasicBlock* after_block_; + }; + + class JitFunctionBuilder; + + /// @brief A JIT-compiled function generator + /// + /// An instance of this class can be used to build a single JIT function and includes + /// some convenience functions for creating loops and operating on array elements + class JitFunction + { + bool generated_ = false; + std::string name_; + std::shared_ptr compiler_; + + public: + std::unique_ptr context_; + std::unique_ptr module_; + std::unique_ptr> builder_; + llvm::ExitOnError exit_on_error_; + std::vector arguments_; + llvm::Function* function_; + llvm::BasicBlock* entry_block_; + + JitFunction() = delete; + + friend class JitFunctionBuilder; + static JitFunctionBuilder create(std::shared_ptr compiler); + JitFunction(JitFunctionBuilder& function_builder); + + /// @brief Generates the function + /// @return Resource tracker and function pointer + /// + /// This can only be called once. + std::pair Generate(); + + /// @brief Get an LLVM type variable + /// @param type Type to get + /// @return LLVM type + llvm::Type* GetType(JitType type); + + /// @brief Get a value from an array + /// @param array_ptr Array pointer + /// @param index Index in array to return value for + /// @param type Data type of element + /// @return Value of array element + llvm::Value* GetArrayElement(JitArgument array_ptr, llvm::ArrayRef index, JitType type); + + /// @brief Set the value in an array + /// @param array_ptr Array pointer + /// @param index Index in array to return value for + /// @param type Data type of element + /// @param value Value to set array element to + void SetArrayElement(JitArgument array_ptr, llvm::ArrayRef index, JitType type, llvm::Value* value); + + /// @brief Start a for loop + /// @param name Label for the loop + /// @param start Starting index + /// @param end Ending index + /// @param step Step size + /// @return Loop reference + JitLoop StartLoop(std::string name, int start, int end, int step); + JitLoop StartLoop(std::string name, llvm::Value* start, llvm::Value* end, llvm::Value* step); + + /// @brief End a loop block + /// @param loop Loop reference + void EndLoop(JitLoop& loop); + + private: + llvm::AllocaInst* CreateEntryBlockAlloca(llvm::Type* type, const std::string& var_name); + }; + + class JitFunctionBuilder + { + std::shared_ptr compiler_; + std::string name_; + std::vector> arguments_; + JitType return_type_{ JitType::Void }; + friend class JitFunction; + + public: + JitFunctionBuilder() = delete; + JitFunctionBuilder(std::shared_ptr compiler); + JitFunctionBuilder& name(std::string name); + JitFunctionBuilder& arguments(const std::vector>& arguments); + JitFunctionBuilder& return_type(JitType type); + }; + + inline JitFunctionBuilder JitFunction::create(std::shared_ptr compiler) + { + return JitFunctionBuilder{ compiler }; + } + + JitFunction::JitFunction(JitFunctionBuilder& function_builder) + : generated_(false), + name_(function_builder.name_), + compiler_(function_builder.compiler_), + context_(std::make_unique()), + module_(std::make_unique(name_ + " module", *context_)), + builder_(std::make_unique>(*context_)), + exit_on_error_(), + arguments_(), + function_(), + entry_block_() + { + module_->setDataLayout(compiler_->GetDataLayout()); + + // Prototype function + std::vector arg_types; + for (const auto& pair : function_builder.arguments_) + { + llvm::Type* type = GetType(pair.second); + arg_types.push_back(type); + arguments_.push_back({ .name_ = pair.first, .type_ = type }); + } + llvm::FunctionType* function_type = llvm::FunctionType::get(GetType(function_builder.return_type_), arg_types, false); + function_ = llvm::Function::Create(function_type, llvm::Function::ExternalLinkage, name_, module_.get()); + llvm::Function::arg_iterator arg_iter = function_->arg_begin(); + for (auto& arg : arguments_) + { + arg.arg_ = arg_iter++; + arg.arg_->setName(arg.name_); + } + + // function body + + // set up entry block + entry_block_ = llvm::BasicBlock::Create(*context_, "entry", function_); + builder_->SetInsertPoint(entry_block_); + + // set up function argument variables + for (auto& arg : arguments_) + arg.alloca_ = CreateEntryBlockAlloca(arg.type_, arg.name_); + for (auto& arg : arguments_) + builder_->CreateStore(arg.arg_, arg.alloca_); + for (auto& arg : arguments_) + arg.ptr_ = builder_->CreateLoad(arg.type_, arg.alloca_); + } + + std::pair JitFunction::Generate() + { + assert((!generated_) && "JIT Function already generated"); + std::pair ret_val; + verifyFunction(*function_); + ret_val.first = compiler_->GetMainJITDylib().createResourceTracker(); + + // Add the module to the JIT + auto threadsafe_module = llvm::orc::ThreadSafeModule(std::move(module_), std::move(context_)); + exit_on_error_(compiler_->AddModule(std::move(threadsafe_module), ret_val.first)); + + // Find the function + auto expr_symbol = exit_on_error_(compiler_->Lookup(name_)); + ret_val.second = expr_symbol.getAddress(); + + generated_ = true; + return ret_val; + } + + inline llvm::Type* JitFunction::GetType(JitType type) + { + switch (type) + { + case JitType::Int32: return llvm::Type::getInt32Ty(*context_); + case JitType::Int32Ptr: return llvm::Type::getInt32Ty(*context_)->getPointerTo(); + case JitType::Int64: return llvm::Type::getInt64Ty(*context_); + case JitType::Int64Ptr: return llvm::Type::getInt64Ty(*context_)->getPointerTo(); + case JitType::Float: return llvm::Type::getFloatTy(*context_); + case JitType::FloatPtr: return llvm::Type::getFloatTy(*context_)->getPointerTo(); + case JitType::Double: return llvm::Type::getDoubleTy(*context_); + case JitType::DoublePtr: return llvm::Type::getDoubleTy(*context_)->getPointerTo(); + case JitType::Bool: return llvm::Type::getInt1Ty(*context_); + case JitType::Void: return llvm::Type::getVoidTy(*context_); + } + return llvm::Type::getVoidTy(*context_); + } + + llvm::Value* JitFunction::GetArrayElement(JitArgument array_ptr, llvm::ArrayRef index, JitType type) + { + llvm::Value* elem = builder_->CreateGEP(GetType(type), array_ptr.ptr_, index, array_ptr.name_ + " get elem"); + return builder_->CreateLoad(GetType(type), elem, array_ptr.name_ + " load elem"); + } + + void + JitFunction::SetArrayElement(JitArgument array_ptr, llvm::ArrayRef index, JitType type, llvm::Value* value) + { + llvm::Value* elem = builder_->CreateGEP(GetType(type), array_ptr.ptr_, index, array_ptr.name_ + " set elem"); + builder_->CreateStore(value, elem); + } + + JitLoop JitFunction::StartLoop(std::string name, int start, int end, int step = 1) + { + llvm::Value* start_val = llvm::ConstantInt::get(*context_, llvm::APInt(64, start)); + llvm::Value* step_val = llvm::ConstantInt::get(*context_, llvm::APInt(64, step)); + llvm::Value* end_val = llvm::ConstantInt::get(*context_, llvm::APInt(64, end)); + return StartLoop(name, start_val, end_val, step_val); + } + + JitLoop JitFunction::StartLoop(std::string name, llvm::Value* start, llvm::Value* end, llvm::Value* step) + { + JitLoop loop; + loop.name_ = name; + loop.prior_block_ = builder_->GetInsertBlock(); + loop.block_ = llvm::BasicBlock::Create(*context_, name, function_); + builder_->CreateBr(loop.block_); + builder_->SetInsertPoint(loop.block_); + loop.index_ = builder_->CreatePHI(GetType(JitType::Int64), 2, "i_" + name); + loop.index_->addIncoming(start, loop.prior_block_); + loop.step_ = step; + loop.end_ = end; + return loop; + } + + void JitFunction::EndLoop(JitLoop& loop) + { + llvm::Value* nextIter = builder_->CreateNSWAdd(loop.index_, loop.step_, "next " + loop.name_); + llvm::Value* atEnd = builder_->CreateICmpSGE(nextIter, loop.end_, "at end " + loop.name_); + loop.after_block_ = llvm::BasicBlock::Create(*context_, "after " + loop.name_, function_); + builder_->CreateCondBr(atEnd, loop.after_block_, loop.block_); + builder_->SetInsertPoint(loop.after_block_); + loop.index_->addIncoming(nextIter, loop.block_); + } + + inline llvm::AllocaInst* JitFunction::CreateEntryBlockAlloca(llvm::Type* type, const std::string& var_name) + { + llvm::IRBuilder<> TmpB(&function_->getEntryBlock(), function_->getEntryBlock().begin()); + return TmpB.CreateAlloca(type, 0, var_name.c_str()); + } + + inline JitFunctionBuilder::JitFunctionBuilder(std::shared_ptr compiler) + : compiler_(compiler){}; + + inline JitFunctionBuilder& JitFunctionBuilder::name(std::string name) + { + name_ = name; + return *this; + } + + inline JitFunctionBuilder& JitFunctionBuilder::arguments(const std::vector>& arguments) + { + arguments_ = arguments; + return *this; + } + + inline JitFunctionBuilder& JitFunctionBuilder::return_type(JitType type) + { + return_type_ = type; + return *this; + } +} // namespace micm \ No newline at end of file diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp new file mode 100644 index 000000000..4723729a0 --- /dev/null +++ b/include/micm/process/jit_process_set.hpp @@ -0,0 +1,63 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include + +namespace micm +{ + + /// @brief JIT-compiled solver function calculators for a collection of processes + /// The template parameter is the number of grid cells to solve simultaneously + template + class JitProcessSet : public ProcessSet + { + std::shared_ptr compiler_; + llvm::orc::ResourceTrackerSP resource_tracker_; + void (*forcing_function_)(double*, double*, double*); + + public: + /// @brief Create a JITed process set calculator for a given set of processes + /// @param compiler JIT compiler + /// @param processes Processes to create calculator for + /// @param state Solver state + JitProcessSet( + std::shared_ptr compiler, + const std::vector& processes, + const State& state); + + /// @brief Add forcing terms for the set of processes for the current conditions + /// @param rate_constants Current values for the process rate constants (grid cell, process) + /// @param state_variables Current state variable values (grid cell, state variable) + /// @param forcing Forcing terms for each state variable (grid cell, state variable) + void AddForcingTerms( + const VectorMatrix& rate_constants, + const VectorMatrix& state_variables, + VectorMatrix& forcing) const; + + }; + + inline JitProcessSet::JitProcessSet( + std::shared_ptr compiler, + const std::vector& processes, + const State& state) + : ProcessSet(processes, state), + compiler_(compiler) + { + JitFunction func = JitFunction::create(compiler.get()) + .name("add_forcing_terms") + .arguments({ { "rate constants", JitType::Double }, + { "state variables", JitType::Double }, + { "forcing", JitType::Double }}), + .return_type(JitType::Void); + for (std::size_t i_rxn = 0; i_rxn < number_of_reactants_.size(); ++i_rxn) + { + llvm::Value *rc_start = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, i_rxn * L)); + llvm::Value *rc_end = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, i_rxn * L + L)); + llvm::ArrayType *rate_arr = llvm::ArrayType::get(func.GetType(JitType::Int32), ) + auto loop = func.StartLoop("rate constant loop", rc_start, rc_end); + llvm::Value *rate = func.GetArrayElement(func.arguments_[0], index_list, micm::JitType::Double); + } + } +} // namespace micm \ No newline at end of file diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 2f61327bd..dc0abc627 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -1,6 +1,9 @@ if(ENABLE_JSON) add_subdirectory(configure) endif() +if(ENABLE_LLVM) + add_subdirectory(jit) +endif() add_subdirectory(process) add_subdirectory(solver) add_subdirectory(system) diff --git a/test/unit/jit/CMakeLists.txt b/test/unit/jit/CMakeLists.txt new file mode 100644 index 000000000..d4f3c1797 --- /dev/null +++ b/test/unit/jit/CMakeLists.txt @@ -0,0 +1,9 @@ +################################################################################ +# Test utilities + +include(test_util) + +################################################################################ +# Tests + +create_standard_test(NAME jit_function SOURCES test_jit_function.cpp) \ No newline at end of file diff --git a/test/unit/jit/test_jit_function.cpp b/test/unit/jit/test_jit_function.cpp new file mode 100644 index 000000000..9877c1b7d --- /dev/null +++ b/test/unit/jit/test_jit_function.cpp @@ -0,0 +1,381 @@ +#include + +#include +#include + +// This test creates a function that adds two integers and returns the sum +TEST(JitFunction, SimpleInt32Function) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo_int32") + .arguments({ { "foo", micm::JitType::Int32 }, { "bar", micm::JitType::Int32 } }) + .return_type(micm::JitType::Int32); + llvm::Value *ret_val = func.builder_->CreateNSWAdd(func.arguments_[0].ptr_, func.arguments_[1].ptr_, "add args"); + func.builder_->CreateRet(ret_val); + auto func_target = func.Generate(); + int32_t (*func_ptr)(int32_t, int32_t) = (int32_t(*)(int32_t, int32_t))(intptr_t)func_target.second; + EXPECT_EQ(12, func_ptr(8, 4)); + EXPECT_EQ(-4, func_ptr(-8, 4)); + EXPECT_EQ(92, func_ptr(80, 12)); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates a function that adds two integers and returns the sum +TEST(JitFunction, SimpleInt64Function) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo_int64") + .arguments({ { "foo", micm::JitType::Int64 }, { "bar", micm::JitType::Int64 } }) + .return_type(micm::JitType::Int64); + llvm::Value *ret_val = func.builder_->CreateNSWAdd(func.arguments_[0].ptr_, func.arguments_[1].ptr_, "add args"); + func.builder_->CreateRet(ret_val); + auto func_target = func.Generate(); + int64_t (*func_ptr)(int64_t, int64_t) = (int64_t(*)(int64_t, int64_t))(intptr_t)func_target.second; + EXPECT_EQ(12l, func_ptr(8l, 4l)); + EXPECT_EQ(-4l, func_ptr(-8l, 4l)); + EXPECT_EQ(92l, func_ptr(80l, 12l)); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates a function that adds two floats and returns the sum +TEST(JitFunction, SimpleFloatFunction) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo_float") + .arguments({ { "foo", micm::JitType::Float }, { "bar", micm::JitType::Float } }) + .return_type(micm::JitType::Float); + llvm::Value *ret_val = func.builder_->CreateFAdd(func.arguments_[0].ptr_, func.arguments_[1].ptr_, "add args"); + func.builder_->CreateRet(ret_val); + auto func_target = func.Generate(); + float (*func_ptr)(float, float) = (float (*)(float, float))(intptr_t)func_target.second; + EXPECT_EQ(8.32f + 4.23f, func_ptr(8.32f, 4.23f)); + EXPECT_EQ(-8.93f + 4.01f, func_ptr(-8.93f, 4.01f)); + EXPECT_EQ(80.12f + 12.42f, func_ptr(80.12f, 12.42f)); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates a function that adds two doubles and returns the sum +TEST(JitFunction, SimpleDoubleFunction) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo_double") + .arguments({ { "foo", micm::JitType::Double }, { "bar", micm::JitType::Double } }) + .return_type(micm::JitType::Double); + llvm::Value *ret_val = func.builder_->CreateFAdd(func.arguments_[0].ptr_, func.arguments_[1].ptr_, "add args"); + func.builder_->CreateRet(ret_val); + auto func_target = func.Generate(); + double (*func_ptr)(double, double) = (double (*)(double, double))(intptr_t)func_target.second; + EXPECT_EQ(8.32 + 4.23, func_ptr(8.32, 4.23)); + EXPECT_EQ(-8.93 + 4.01, func_ptr(-8.93, 4.01)); + EXPECT_EQ(80.12 + 12.42, func_ptr(80.12, 12.42)); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates a function that returns an OR of two booleans +TEST(JitFunction, SimpleBoolFunction) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo_bool") + .arguments({ { "foo", micm::JitType::Bool }, { "bar", micm::JitType::Bool } }) + .return_type(micm::JitType::Bool); + llvm::Value *ret_val = func.builder_->CreateOr(func.arguments_[0].ptr_, func.arguments_[1].ptr_, "add args"); + func.builder_->CreateRet(ret_val); + auto func_target = func.Generate(); + bool (*func_ptr)(bool, bool) = (bool (*)(bool, bool))(intptr_t)func_target.second; + EXPECT_EQ(true, func_ptr(true, false)); + EXPECT_EQ(true, func_ptr(true, true)); + EXPECT_EQ(false, func_ptr(false, false)); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates a function that adds the third elements in two integer arrays, sets the +// second element of the second array as the sum, and also returns the sum +TEST(JitFunction, SimpleInt32PtrFunction) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo_int32_ptr") + .arguments({ { "foo", micm::JitType::Int32Ptr }, { "bar", micm::JitType::Int32Ptr } }) + .return_type(micm::JitType::Int32); + llvm::Value *index_list[1]; + index_list[0] = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 2)); + llvm::Value *foo_val = func.GetArrayElement(func.arguments_[0], index_list, micm::JitType::Int32); + llvm::Value *bar_val = func.GetArrayElement(func.arguments_[1], index_list, micm::JitType::Int32); + llvm::Value *sum = func.builder_->CreateNSWAdd(foo_val, bar_val, "sum foo bar"); + index_list[0] = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 1)); + func.SetArrayElement(func.arguments_[1], index_list, micm::JitType::Int32, sum); + func.builder_->CreateRet(sum); + auto func_target = func.Generate(); + int32_t (*func_ptr)(int32_t *, int32_t *) = (int32_t(*)(int32_t *, int32_t *))(intptr_t)func_target.second; + int32_t a[] = { 9, 4, 33 }; + int32_t b[] = { 4, 21, 2, 42 }; + EXPECT_EQ(35, func_ptr(a, b)); + EXPECT_EQ(9, a[0]); + EXPECT_EQ(4, a[1]); + EXPECT_EQ(33, a[2]); + EXPECT_EQ(4, b[0]); + EXPECT_EQ(35, b[1]); + EXPECT_EQ(2, b[2]); + EXPECT_EQ(42, b[3]); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates a function that adds the third elements in two integer arrays, sets the +// second element of the second array as the sum, and also returns the sum +TEST(JitFunction, SimpleInt64PtrFunction) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo_int64_ptr") + .arguments({ { "foo", micm::JitType::Int64Ptr }, { "bar", micm::JitType::Int64Ptr } }) + .return_type(micm::JitType::Int64); + llvm::Value *index_list[1]; + index_list[0] = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 2)); + llvm::Value *foo_val = func.GetArrayElement(func.arguments_[0], index_list, micm::JitType::Int64); + llvm::Value *bar_val = func.GetArrayElement(func.arguments_[1], index_list, micm::JitType::Int64); + llvm::Value *sum = func.builder_->CreateNSWAdd(foo_val, bar_val, "sum foo bar"); + index_list[0] = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 1)); + func.SetArrayElement(func.arguments_[1], index_list, micm::JitType::Int64, sum); + func.builder_->CreateRet(sum); + auto func_target = func.Generate(); + int64_t (*func_ptr)(int64_t *, int64_t *) = (int64_t(*)(int64_t *, int64_t *))(intptr_t)func_target.second; + int64_t a[] = { 9l, 4l, 33l }; + int64_t b[] = { 4l, 21l, 2l, 42l }; + EXPECT_EQ(35l, func_ptr(a, b)); + EXPECT_EQ(9l, a[0]); + EXPECT_EQ(4l, a[1]); + EXPECT_EQ(33l, a[2]); + EXPECT_EQ(4l, b[0]); + EXPECT_EQ(35l, b[1]); + EXPECT_EQ(2l, b[2]); + EXPECT_EQ(42l, b[3]); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates a function that adds the third elements in two float arrays, sets the +// second element of the second array as the sum, and also returns the sum +TEST(JitFunction, SimpleFloatPtrFunction) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo_float_ptr") + .arguments({ { "foo", micm::JitType::FloatPtr }, { "bar", micm::JitType::FloatPtr } }) + .return_type(micm::JitType::Float); + llvm::Value *index_list[1]; + index_list[0] = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 2)); + llvm::Value *foo_val = func.GetArrayElement(func.arguments_[0], index_list, micm::JitType::Float); + llvm::Value *bar_val = func.GetArrayElement(func.arguments_[1], index_list, micm::JitType::Float); + llvm::Value *sum = func.builder_->CreateFAdd(foo_val, bar_val, "sum foo bar"); + index_list[0] = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 1)); + func.SetArrayElement(func.arguments_[1], index_list, micm::JitType::Float, sum); + func.builder_->CreateRet(sum); + auto func_target = func.Generate(); + float (*func_ptr)(float *, float *) = (float (*)(float *, float *))(intptr_t)func_target.second; + float a[] = { 9.3f, 4.4f, 33.3f }; + float b[] = { 4.53f, 21.02f, 2.0f, 42.23f }; + EXPECT_EQ(35.3f, func_ptr(a, b)); + EXPECT_EQ(9.3f, a[0]); + EXPECT_EQ(4.4f, a[1]); + EXPECT_EQ(33.3f, a[2]); + EXPECT_EQ(4.53f, b[0]); + EXPECT_EQ(35.3f, b[1]); + EXPECT_EQ(2.0f, b[2]); + EXPECT_EQ(42.23f, b[3]); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates a function that adds the third elements in two double arrays, sets the +// second element of the second array as the sum, and also returns the sum +TEST(JitFunction, SimpleDoublePtrFunction) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo_double_ptr") + .arguments({ { "foo", micm::JitType::DoublePtr }, { "bar", micm::JitType::DoublePtr } }) + .return_type(micm::JitType::Double); + llvm::Value *index_list[1]; + index_list[0] = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 2)); + llvm::Value *foo_val = func.GetArrayElement(func.arguments_[0], index_list, micm::JitType::Double); + llvm::Value *bar_val = func.GetArrayElement(func.arguments_[1], index_list, micm::JitType::Double); + llvm::Value *sum = func.builder_->CreateFAdd(foo_val, bar_val, "sum foo bar"); + index_list[0] = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 1)); + func.SetArrayElement(func.arguments_[1], index_list, micm::JitType::Double, sum); + func.builder_->CreateRet(sum); + auto func_target = func.Generate(); + double (*func_ptr)(double *, double *) = (double (*)(double *, double *))(intptr_t)func_target.second; + double a[] = { 9.3, 4.4, 33.3 }; + double b[] = { 4.53, 21.02, 2.0, 42.23 }; + EXPECT_EQ(35.3, func_ptr(a, b)); + EXPECT_EQ(9.3, a[0]); + EXPECT_EQ(4.4, a[1]); + EXPECT_EQ(33.3, a[2]); + EXPECT_EQ(4.53, b[0]); + EXPECT_EQ(35.3, b[1]); + EXPECT_EQ(2.0, b[2]); + EXPECT_EQ(42.23, b[3]); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates a function that includes a loop that adds 1 to a variable that starts at 0 +// and iterates 10 times and returns the summed value +TEST(JitFunction, SimpleLoop) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = + micm::JitFunction::create(jit.get()).name("foo_loop").arguments({}).return_type(micm::JitType::Int32); + auto loop = func.StartLoop("foo loop", 0, 10); + llvm::PHINode *ret_val = func.builder_->CreatePHI(func.GetType(micm::JitType::Int32), 2, "ret val"); + ret_val->addIncoming(llvm::ConstantInt::get(*(func.context_), llvm::APInt(32, 1)), func.entry_block_); + llvm::Value *incr = llvm::ConstantInt::get(*(func.context_), llvm::APInt(32, 1)); + llvm::Value *next_val = func.builder_->CreateNSWAdd(ret_val, incr, "add incr"); + func.EndLoop(loop); + ret_val->addIncoming(next_val, loop.block_); + func.builder_->CreateRet(ret_val); + auto func_target = func.Generate(); + int32_t (*func_ptr)() = (int32_t(*)())(intptr_t)func_target.second; + EXPECT_EQ(10, func_ptr()); + func.exit_on_error_(func_target.first->remove()); +} + +// This test creates two functions, foo and bar, that return the sum of their single integer +// arguments and 10 and 100, respectively +TEST(JitFunction, MultipleFunctions) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction foo_func = micm::JitFunction::create(jit.get()) + .name("foo") + .arguments({ { "arg", micm::JitType::Int32 } }) + .return_type(micm::JitType::Int32); + llvm::Value *foo_ret_val = foo_func.builder_->CreateNSWAdd( + foo_func.arguments_[0].ptr_, llvm::ConstantInt::get(*(foo_func.context_), llvm::APInt(32, 10)), "add args"); + foo_func.builder_->CreateRet(foo_ret_val); + auto foo_target = foo_func.Generate(); + int32_t (*foo_func_ptr)(int) = (int32_t(*)(int))(intptr_t)foo_target.second; + micm::JitFunction bar_func = micm::JitFunction::create(jit.get()) + .name("bar") + .arguments({ { "arg", micm::JitType::Int32 } }) + .return_type(micm::JitType::Int32); + llvm::Value *bar_ret_val = bar_func.builder_->CreateNSWAdd( + bar_func.arguments_[0].ptr_, llvm::ConstantInt::get(*(bar_func.context_), llvm::APInt(32, 100)), "add args"); + bar_func.builder_->CreateRet(bar_ret_val); + auto bar_target = bar_func.Generate(); + int32_t (*bar_func_ptr)(int) = (int32_t(*)(int))(intptr_t)bar_target.second; + EXPECT_EQ(32, foo_func_ptr(22)); + EXPECT_EQ(102, bar_func_ptr(2)); + EXPECT_EQ(254, bar_func_ptr(foo_func_ptr(144))); + foo_func.exit_on_error_(foo_target.first->remove()); + bar_func.exit_on_error_(bar_target.first->remove()); +} + +// This test creates a local array of ints, populates it with a specified value, +// and returns the sum +TEST(JitFunction, LocalArray) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func = micm::JitFunction::create(jit.get()) + .name("foo") + .arguments({ { "arg", micm::JitType::Int64 } }) + .return_type(micm::JitType::Int64); + + auto int_type = func.GetType(micm::JitType::Int64); + llvm::Value *zero = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 0)); + llvm::Type *foo_array_type = llvm::ArrayType::get(int_type, 10); + llvm::AllocaInst *foo_array = + func.builder_->CreateAlloca(foo_array_type, llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, 1)), "foo_array"); + + // loop to set array elements + auto loop = func.StartLoop("set_loop", 0, 10); + llvm::Value *index_list[2]; + index_list[0] = zero; + index_list[1] = loop.index_; + llvm::Value *set_elem = func.builder_->CreateInBoundsGEP(foo_array_type, foo_array, index_list, "set_elem_ptr"); + func.builder_->CreateStore(func.arguments_[0].ptr_, set_elem); + func.EndLoop(loop); + + // loop to sum array elements + index_list[1] = zero; + llvm::Value *get_elem = func.builder_->CreateInBoundsGEP(foo_array_type, foo_array, index_list, "get_first_elem_ptr"); + llvm::Value *first_elem = func.builder_->CreateLoad(int_type, get_elem, "load_first_elem"); + loop = func.StartLoop("sum_loop", 0, 10, 2); + llvm::PHINode *ret_val = func.builder_->CreatePHI(int_type, 2, "ret_val"); + ret_val->addIncoming(first_elem, loop.prior_block_); + index_list[1] = loop.index_; + get_elem = func.builder_->CreateInBoundsGEP(foo_array_type, foo_array, index_list, "get_curr_elem_ptr"); + llvm::Value *curr_elem = func.builder_->CreateLoad(int_type, get_elem, "load_curr_elem"); + llvm::Value *next_val = func.builder_->CreateNSWAdd(ret_val, curr_elem, "add_curr_elem"); + func.EndLoop(loop); + ret_val->addIncoming(next_val, loop.block_); + + func.builder_->CreateRet(ret_val); + + auto foo_target = func.Generate(); + int64_t (*func_ptr)(int) = (int64_t(*)(int))(intptr_t)foo_target.second; + EXPECT_EQ(20, func_ptr(4)); + EXPECT_EQ(5, func_ptr(1)); + func.exit_on_error_(foo_target.first->remove()); +} \ No newline at end of file diff --git a/test/valgrind.supp b/test/valgrind.supp index e69de29bb..e45889e52 100644 --- a/test/valgrind.supp +++ b/test/valgrind.supp @@ -0,0 +1,13 @@ +############################################################## +# +# LLVM suppressions +# +############################################################## + +{ + + Memcheck:Cond + fun:_ZN4llvm10AsmPrinter28emitPatchableFunctionEntriesEv + fun:_ZN4llvm10AsmPrinter16emitFunctionBodyEv + ... +} \ No newline at end of file From 3a1011b190ec6024b496fbab2463798b4af03d9a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Aug 2023 15:38:34 -0700 Subject: [PATCH 2/2] Auto-format code changes (#181) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/process/jit_process_set.hpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index 4723729a0..46d7b1de6 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -35,7 +35,6 @@ namespace micm const VectorMatrix& rate_constants, const VectorMatrix& state_variables, VectorMatrix& forcing) const; - }; inline JitProcessSet::JitProcessSet( @@ -46,18 +45,18 @@ namespace micm compiler_(compiler) { JitFunction func = JitFunction::create(compiler.get()) - .name("add_forcing_terms") - .arguments({ { "rate constants", JitType::Double }, - { "state variables", JitType::Double }, - { "forcing", JitType::Double }}), - .return_type(JitType::Void); + .name("add_forcing_terms") + .arguments({ { "rate constants", JitType::Double }, + { "state variables", JitType::Double }, + { "forcing", JitType::Double } }), + .return_type(JitType::Void); for (std::size_t i_rxn = 0; i_rxn < number_of_reactants_.size(); ++i_rxn) { - llvm::Value *rc_start = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, i_rxn * L)); - llvm::Value *rc_end = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, i_rxn * L + L)); - llvm::ArrayType *rate_arr = llvm::ArrayType::get(func.GetType(JitType::Int32), ) - auto loop = func.StartLoop("rate constant loop", rc_start, rc_end); - llvm::Value *rate = func.GetArrayElement(func.arguments_[0], index_list, micm::JitType::Double); + llvm::Value* rc_start = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, i_rxn * L)); + llvm::Value* rc_end = llvm::ConstantInt::get(*(func.context_), llvm::APInt(64, i_rxn * L + L)); + llvm::ArrayType* rate_arr = llvm::ArrayType::get(func.GetType(JitType::Int32), ) auto loop = + func.StartLoop("rate constant loop", rc_start, rc_end); + llvm::Value* rate = func.GetArrayElement(func.arguments_[0], index_list, micm::JitType::Double); } } } // namespace micm \ No newline at end of file