From 846a21923633c6c8ea5c1d7163e4433a5373a9da Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Wed, 13 Dec 2023 20:53:26 +0530 Subject: [PATCH 1/4] DEV: Use minimal version of evaluator.h for LC --- src/bin/lc.cpp | 2 +- src/libasr/CMakeLists.txt | 4 +- src/libasr/codegen/asr_to_llvm.h | 2 +- src/libasr/codegen/c_evaluator.cpp | 266 +++++++++++++++++++++++++++++ src/libasr/codegen/c_evaluator.h | 56 ++++++ 5 files changed, 326 insertions(+), 4 deletions(-) create mode 100644 src/libasr/codegen/c_evaluator.cpp create mode 100644 src/libasr/codegen/c_evaluator.h diff --git a/src/bin/lc.cpp b/src/bin/lc.cpp index cc3be13..19ff5be 100644 --- a/src/bin/lc.cpp +++ b/src/bin/lc.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/libasr/CMakeLists.txt b/src/libasr/CMakeLists.txt index 689f6ea..af8fdba 100644 --- a/src/libasr/CMakeLists.txt +++ b/src/libasr/CMakeLists.txt @@ -79,14 +79,14 @@ set(SRC ) if (WITH_LLVM) set(SRC ${SRC} - codegen/evaluator.cpp + codegen/c_evaluator.cpp codegen/asr_to_llvm.cpp codegen/llvm_array_utils.cpp codegen/llvm_utils.cpp ) # We use deprecated API in LLVM, so we disable the warning until we upgrade if (NOT MSVC) - set_source_files_properties(codegen/evaluator.cpp PROPERTIES + set_source_files_properties(codegen/c_evaluator.cpp PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations) set_source_files_properties(codegen/asr_to_llvm.cpp PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations) diff --git a/src/libasr/codegen/asr_to_llvm.h b/src/libasr/codegen/asr_to_llvm.h index a1e911e..a099f54 100644 --- a/src/libasr/codegen/asr_to_llvm.h +++ b/src/libasr/codegen/asr_to_llvm.h @@ -2,7 +2,7 @@ #define LFORTRAN_ASR_TO_LLVM_H #include -#include +#include #include namespace LCompilers { diff --git a/src/libasr/codegen/c_evaluator.cpp b/src/libasr/codegen/c_evaluator.cpp new file mode 100644 index 0000000..695e459 --- /dev/null +++ b/src/libasr/codegen/c_evaluator.cpp @@ -0,0 +1,266 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LLVM_VERSION_MAJOR >= 14 +# include +#else +# include +#endif +#include + +#include +#include +#include +#include +#include +#include + + +namespace LCompilers { + +// Extracts the integer from APInt. +// APInt does not seem to have this functionality, so we implement it here. +uint64_t APInt_getint(const llvm::APInt &i) { + // The APInt::isSingleWord() is private, but we can emulate it: + bool isSingleWord = !i.needsCleanup(); + if (isSingleWord) { + return *i.getRawData(); + } else { + throw std::runtime_error("APInt too large to fit uint64_t"); + } +} + +LLVMModule::LLVMModule(std::unique_ptr m) +{ + m_m = std::move(m); +} + +LLVMModule::~LLVMModule() = default; + +std::string LLVMModule::str() +{ + return LLVMEvaluator::module_to_string(*m_m); +} + +std::string LLVMModule::get_return_type(const std::string &fn_name) +{ + llvm::Module *m = m_m.get(); + llvm::Function *fn = m->getFunction(fn_name); + if (!fn) { + return "none"; + } + llvm::Type *type = fn->getReturnType(); + if (type->isFloatTy()) { + return "real4"; + } else if (type->isDoubleTy()) { + return "real8"; + } else if (type->isIntegerTy(32)) { + return "integer4"; + } else if (type->isIntegerTy(64)) { + return "integer8"; + } else if (type->isStructTy()) { + llvm::StructType *st = llvm::cast(type); + if (st->hasName()) { + if (startswith(std::string(st->getName()), "complex_4")) { + return "complex4"; + } else if (startswith(std::string(st->getName()), "complex_8")) { + return "complex8"; + } else { + throw LCompilersException("LLVMModule::get_return_type(): Struct return type `" + std::string(st->getName()) + "` not supported"); + } + } else { + throw LCompilersException("LLVMModule::get_return_type(): Noname struct return type not supported"); + } + } else if (type->isVectorTy()) { + // Used for passing complex_4 on some platforms + return "complex4"; + } else if (type->isVoidTy()) { + return "void"; + } else { + throw LCompilersException("LLVMModule::get_return_type(): Return type not supported"); + } +} + +extern "C" { + +float _lfortran_stan(float x); + +} + +#if LLVM_VERSION_MAJOR >= 16 +# define RM_OPTIONAL_TYPE std::optional +#else +# define RM_OPTIONAL_TYPE llvm::Optional +#endif + +LLVMEvaluator::LLVMEvaluator(const std::string &t) +{ + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); + +#ifdef HAVE_TARGET_AARCH64 + LLVMInitializeAArch64Target(); + LLVMInitializeAArch64TargetInfo(); + LLVMInitializeAArch64TargetMC(); + LLVMInitializeAArch64AsmPrinter(); + LLVMInitializeAArch64AsmParser(); +#endif +#ifdef HAVE_TARGET_X86 + LLVMInitializeX86Target(); + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86TargetMC(); + LLVMInitializeX86AsmPrinter(); + LLVMInitializeX86AsmParser(); +#endif +#ifdef HAVE_TARGET_WASM + LLVMInitializeWebAssemblyTarget(); + LLVMInitializeWebAssemblyTargetInfo(); + LLVMInitializeWebAssemblyTargetMC(); + LLVMInitializeWebAssemblyAsmPrinter(); + LLVMInitializeWebAssemblyAsmParser(); +#endif + + context = std::make_unique(); + + if (t != "") + target_triple = t; + else + target_triple = LLVMGetDefaultTargetTriple(); + + std::string Error; + const llvm::Target *target = llvm::TargetRegistry::lookupTarget(target_triple, Error); + if (!target) { + throw LCompilersException(Error); + } + std::string CPU = "generic"; + std::string features = ""; + llvm::TargetOptions opt; + RM_OPTIONAL_TYPE RM = llvm::Reloc::Model::PIC_; + TM = target->createTargetMachine(target_triple, CPU, features, opt, RM); + + _lfortran_stan(0.5); +} + +LLVMEvaluator::~LLVMEvaluator() +{ + context.reset(); +} + +void LLVMEvaluator::save_object_file(llvm::Module &m, const std::string &filename) { + m.setTargetTriple(target_triple); + m.setDataLayout(TM->createDataLayout()); + + llvm::legacy::PassManager pass; + llvm::CodeGenFileType ft = llvm::CGFT_ObjectFile; + std::error_code EC; + llvm::raw_fd_ostream dest(filename, EC, llvm::sys::fs::OF_None); + if (EC) { + throw std::runtime_error("raw_fd_ostream failed"); + } + if (TM->addPassesToEmitFile(pass, dest, nullptr, ft)) { + throw std::runtime_error("TargetMachine can't emit a file of this type"); + } + pass.run(m); + dest.flush(); +} + +void LLVMEvaluator::opt(llvm::Module &m) { + m.setTargetTriple(target_triple); + m.setDataLayout(TM->createDataLayout()); + + llvm::legacy::PassManager mpm; + mpm.add(new llvm::TargetLibraryInfoWrapperPass(TM->getTargetTriple())); + mpm.add(llvm::createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); + llvm::legacy::FunctionPassManager fpm(&m); + fpm.add(llvm::createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); + + int optLevel = 3; + int sizeLevel = 0; + llvm::PassManagerBuilder builder; + builder.OptLevel = optLevel; + builder.SizeLevel = sizeLevel; + builder.Inliner = llvm::createFunctionInliningPass(optLevel, sizeLevel, + false); + builder.DisableUnrollLoops = false; + builder.LoopVectorize = true; + builder.SLPVectorize = true; + builder.populateFunctionPassManager(fpm); + builder.populateModulePassManager(mpm); + + fpm.doInitialization(); + for (llvm::Function &func : m) { + fpm.run(func); + } + fpm.doFinalization(); + + mpm.add(llvm::createVerifierPass()); + mpm.run(m); +} + +std::string LLVMEvaluator::module_to_string(llvm::Module &m) { + std::string buf; + llvm::raw_string_ostream os(buf); + m.print(os, nullptr); + os.flush(); + return buf; +} + +llvm::LLVMContext &LLVMEvaluator::get_context() +{ + return *context; +} + +std::string LLVMEvaluator::get_default_target_triple() +{ + return llvm::sys::getDefaultTargetTriple(); +} + +} // namespace LCompilers diff --git a/src/libasr/codegen/c_evaluator.h b/src/libasr/codegen/c_evaluator.h new file mode 100644 index 0000000..b16318c --- /dev/null +++ b/src/libasr/codegen/c_evaluator.h @@ -0,0 +1,56 @@ +#ifndef LFORTRAN_EVALUATOR_H +#define LFORTRAN_EVALUATOR_H + +#include +#include +#include + +#include +#include +#include +#include + +// Forward declare all needed LLVM classes without importing any LLVM header +// files. Those are only imported in evaluator.cpp and nowhere else, to speed +// up compilation. +namespace llvm { + class ExecutionEngine; + class LLVMContext; + class Module; + class Function; + class TargetMachine; +} + +namespace LCompilers { + +class LLVMModule +{ +public: + std::unique_ptr m_m; + LLVMModule(std::unique_ptr m); + ~LLVMModule(); + std::string str(); + // Return a function return type as a string (real / integer) + std::string get_return_type(const std::string &fn_name); +}; + +class LLVMEvaluator +{ +private: + std::unique_ptr context; + std::string target_triple; + llvm::TargetMachine *TM; +public: + LLVMEvaluator(const std::string &t = ""); + ~LLVMEvaluator(); + void save_object_file(llvm::Module &m, const std::string &filename); + void opt(llvm::Module &m); + static std::string module_to_string(llvm::Module &m); + llvm::LLVMContext &get_context(); + static std::string get_default_target_triple(); +}; + + +} // namespace LCompilers + +#endif // LFORTRAN_EVALUATOR_H From 74dcd7d523667bf9336c66f1edca6e043a1b5747 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Wed, 13 Dec 2023 21:01:10 +0530 Subject: [PATCH 2/4] CI: Test span_01.cpp on Ubuntu and macOS --- .github/workflows/CI.yml | 10 ++++------ environment_unix.yml | 17 +++++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e91fa0b..7e02b47 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -26,15 +26,9 @@ jobs: submodules: recursive - uses: mamba-org/provision-with-micromamba@main - if: contains(matrix.os, 'ubuntu') with: environment-file: environment_unix.yml - - uses: mamba-org/provision-with-micromamba@main - if: contains(matrix.os, 'macos') - with: - environment-file: environment_macos.yml - - uses: hendrikmuhs/ccache-action@main with: variant: sccache @@ -78,6 +72,10 @@ jobs: lc examples/expr2.c --backend llvm --extra-arg="-Isrc/runtime/include" -o llvm_o -c -- ls -1 + # Test including iostream and span headers + lc tests/span_01.cpp --ast-dump --ast-dump-file="tests/span_01.ast" --extra-arg="-std=c++20" -- + cat tests/span_01.ast + - name: Test3 (macOS) shell: bash -l -e {0} if: contains(matrix.os, 'macos') diff --git a/environment_unix.yml b/environment_unix.yml index e17a4d8..9857cca 100644 --- a/environment_unix.yml +++ b/environment_unix.yml @@ -2,11 +2,12 @@ name: lc channels: - conda-forge dependencies: - - cmake - - llvmdev=15.0.6 - - clangdev=15.0.6 - - make - - zlib - - git - - python=3.11.0 - - toml + - cmake=3.27.9 + - llvmdev=16.0.6 + - clangdev=16.0.6 + - compilers=1.7.0 + - make=4.3 + - zlib=1.2.13 + - git=2.43.0 + - python=3.12.0 + - toml=0.10.2 From 1a474b548542c16133d6742de56111e2be932143 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Wed, 13 Dec 2023 22:02:24 +0530 Subject: [PATCH 3/4] CI: Remove macOS specific testing --- .github/workflows/CI.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7e02b47..483d22f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -75,13 +75,3 @@ jobs: # Test including iostream and span headers lc tests/span_01.cpp --ast-dump --ast-dump-file="tests/span_01.ast" --extra-arg="-std=c++20" -- cat tests/span_01.ast - - - name: Test3 (macOS) - shell: bash -l -e {0} - if: contains(matrix.os, 'macos') - run: | - which clang - - # Test including iostream and span headers - lc tests/span_01.cpp --ast-dump --ast-dump-file="tests/span_01.ast" --extra-arg="-std=c++20" -- - cat tests/span_01.ast From 7af92a6eede3177b5e9aa4da544c338988485630 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Wed, 13 Dec 2023 22:03:12 +0530 Subject: [PATCH 4/4] BUILD: Remove environment_macos.yml --- environment_macos.yml | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 environment_macos.yml diff --git a/environment_macos.yml b/environment_macos.yml deleted file mode 100644 index 9857cca..0000000 --- a/environment_macos.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: lc -channels: - - conda-forge -dependencies: - - cmake=3.27.9 - - llvmdev=16.0.6 - - clangdev=16.0.6 - - compilers=1.7.0 - - make=4.3 - - zlib=1.2.13 - - git=2.43.0 - - python=3.12.0 - - toml=0.10.2