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