diff --git a/INSTALL.md b/INSTALL.md index 0d0707e99..c933f0c19 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -30,7 +30,7 @@ python3 -m pip install pybind11 lit ``` ## macOS / Homebrew - +In order to install the dependencies on macOS, you must have Homebrew installed and on your `PATH`. ```shell brew update brew install \ @@ -40,7 +40,9 @@ brew install \ fmt \ git \ gmp \ + grep \ jemalloc \ + libffi libyaml \ llvm@15 \ maven \ @@ -48,11 +50,60 @@ brew install \ pkg-config \ python3 \ z3 +``` + +To ensure that the backend can use pybind11 correctly, we must create an virtual +environment and install the `pybind11` package: +```shell +python3 -m venv venv +source venv/bin/activate python3 -m pip install pybind11 lit ``` +Guarantee that you have the JDK installed and on your `PATH`: +```shell +export PATH="/opt/homebrew/opt/openjdk/bin:$PATH" +``` + +Some tests rely on GNU Grep options, which are not available on macOS by +default. To ensure that the tests run correctly, you add the path of +GNU Grep to your `PATH` in your shell profile: +```shell +export PATH=/opt/homebrew/Cellar/grep/3.11/libexec/gnubin/:$PATH +``` + # Building +## Environment Variables + +If you're building on macOS, type the following command or epermanently +add it your `env` (`.zshrc`, `.bashrc`, etc.), so that the Homebrew +installation of LLVM gets picked up correctly. We recommend adding it to +your shell profile. +```shell +export LLVM_DIR=$($(brew --prefix llvm@15)/bin/llvm-config --cmakedir) +``` + +If you don't usually use the `clang` from your Homebrew installation as +your default compiler, you can set the following CMake flg to use these +`clang` and `clang++`: +```shell +-DCMAKE_C_COMPILER="$(brew --prefix llvm@15)/bin/clang" \ +-DCMAKE_CXX_COMPILER="$(brew --prefix llvm@15)/bin/clang++" +``` +Once again, we recommend adding them and other llvm binaries to your +`PATH` in your shell profile: +```shell +export PATH="$(brew --prefix llvm@15)/bin:$PATH" +``` + +Some tests rely on GNU Grep options, which are not available on macOS by +default. To ensure that the tests run correctly, you can create an alias +for GNU Grep: +```shell +alias grep=ggrep +``` + Once the system dependencies have been installed, the backend can be built locally with: ```shell @@ -66,12 +117,6 @@ cmake .. \ make -j$(nproc) install ``` -If you're building on macOS, add the following option to your CMake invocation -so that the Homebrew installation of LLVM gets picked up correctly. -```shell --DLLVM_DIR=$($(brew --prefix llvm@15)/bin/llvm-config --cmakedir) -``` - Additionally, to build the pattern-matching compiler, run: ```shell cd matching @@ -91,7 +136,8 @@ To run the integration tests, run: ```shell lit test ``` -from the root source directory. +from the root source directory. You can use `-v` to see which test is being executed +and the output of failling tests. There is also a unit test suite for backend internals; Add the following option to your CMake invocation to enable it: @@ -113,7 +159,7 @@ and conform to best practices. ```shell # Ubuntu -apt install shellcheck clang-format-12 iwyu +apt install shellcheck clang-format-15 iwyu # macOS brew install shellcheck clang-format iwyu diff --git a/bin/llvm-kompile b/bin/llvm-kompile index 77ba160b5..ffe57b58f 100755 --- a/bin/llvm-kompile +++ b/bin/llvm-kompile @@ -42,6 +42,7 @@ Options: (immutable) that are enabled by default. --hidden-visibility Set the visibility of all global symbols in generated code to "hidden" + --use-gcstrategy Use GC strategy defined for the LLVM backend. --profile-matching Instrument interpeter to emit a profile of time spent in top-level rule matching on stderr. --verify-ir Verify result of IR generation. @@ -197,6 +198,11 @@ while [[ $# -gt 0 ]]; do kompile_clang_flags+=("--hidden-visibility") shift ;; + --use-gcstrategy) + codegen_flags+=("--use-gcstrategy") + kompile_clang_flags+=("--use-gcstrategy") + shift + ;; --profile-matching) codegen_flags+=("--profile-matching") codegen_verify_flags+=("--profile-matching") diff --git a/bin/llvm-kompile-clang b/bin/llvm-kompile-clang index f35e6da9f..681d4378e 100644 --- a/bin/llvm-kompile-clang +++ b/bin/llvm-kompile-clang @@ -15,6 +15,7 @@ flags=() llc_flags=() llc_opt_flags="-O0" visibility_hidden=false +use_gcstrategy=false link=true export verbose=false export profile=false @@ -101,6 +102,10 @@ while [[ $# -gt 0 ]]; do visibility_hidden=true shift ;; + --use-gcstrategy) + use_gcstrategy=true + shift + ;; *) ;; esac @@ -188,6 +193,9 @@ if [ "$main" != "python_ast" ]; then run @OPT@ "$modopt" -load-pass-plugin "$passes" -set-visibility-hidden -o "$modhidden" modopt="$modhidden" fi + if $use_gcstrategy; then + llc_flags+=("-load="$passes"") + fi run @LLC@ \ "$modopt" -mtriple=@BACKEND_TARGET_TRIPLE@ \ -filetype=obj "$llc_opt_flags" "${llc_flags[@]}" -o "$modasm" diff --git a/cmake/FixHomebrew.cmake b/cmake/FixHomebrew.cmake index 84a82a24d..37595a2c1 100644 --- a/cmake/FixHomebrew.cmake +++ b/cmake/FixHomebrew.cmake @@ -20,5 +20,15 @@ if(APPLE) include_directories(AFTER SYSTEM "${BREW_PREFIX}/include") link_directories(AFTER "${BREW_PREFIX}/lib") set(ENV{PKG_CONFIG_PATH} "${BREW_PREFIX}/opt/libffi/lib/pkgconfig") + + # Use LLD as the linker + # This is necessary as the default linker used by CMake on macOS is + # ld64, which currently has some incompatibilities with Homebrew and XCode15. + # See: https://github.com/orgs/Homebrew/discussions/4794#discussioncomment-7044468 + # Adding this flag avoid the following errors: + # ld: warning: duplicate -rpath ... ignored + # ld: warning: ignoring duplicate libraries ... + add_link_options("-fuse-ld=lld") + endif() # USE_NIX endif() # APPLE diff --git a/include/kllvm/codegen/GCStrategy.h b/include/kllvm/codegen/GCStrategy.h new file mode 100644 index 000000000..aa52872f5 --- /dev/null +++ b/include/kllvm/codegen/GCStrategy.h @@ -0,0 +1,37 @@ +//===- Extend GCStrategy of llvm/CodeGen/GCStrategy.h ---------------------===// +// +// We extend the base GCStrategy as follows: +// - use gc.safepoints instead of (default) gc.roots. +// - specify that the RewriteStatepointsForGC pass should rewrite the calls of +// this function. +// - pointers with address space != 0 are pointing to GC-managed memory. +//===----------------------------------------------------------------------===// + +// NOLINTBEGIN + +#ifndef LLVM_BACKEND_GC_STRATEGY_H +#define LLVM_BACKEND_GC_STRATEGY_H + +#include "llvm/IR/GCStrategy.h" +#include "llvm/IR/Type.h" + +namespace kllvm { + +/// The GCStrategy for the LLVM Backend +class LLVMBackendGCStrategy : public llvm::GCStrategy { +public: + LLVMBackendGCStrategy(); + + // Override +#if LLVM_VERSION_MAJOR == 15 + llvm::Optional isGCManagedPointer(llvm::Type const *Ty) const override; +#else + std::optional isGCManagedPointer(llvm::Type const *Ty) const override; +#endif +}; + +} // namespace kllvm + +#endif // LLVM_BACKEND_GC_STRATEGY_H + +// NOLINTEND diff --git a/include/kllvm/codegen/Options.h b/include/kllvm/codegen/Options.h index fda9f8763..a156500a5 100644 --- a/include/kllvm/codegen/Options.h +++ b/include/kllvm/codegen/Options.h @@ -10,6 +10,7 @@ extern llvm::cl::opt no_optimize; extern llvm::cl::opt emit_object; extern llvm::cl::opt binary_ir; extern llvm::cl::opt force_binary; +extern llvm::cl::opt use_gcstrategy; extern llvm::cl::opt proof_hint_instrumentation; extern llvm::cl::opt proof_hint_instrumentation_slow; extern llvm::cl::opt keep_frame_pointer; diff --git a/include/runtime/alloc.h b/include/runtime/alloc.h index 4b10e4779..9208e5ee8 100644 --- a/include/runtime/alloc.h +++ b/include/runtime/alloc.h @@ -15,7 +15,6 @@ extern "C" { char youngspace_collection_id(void); char oldspace_collection_id(void); -//size_t youngspace_size(void); // allocates exactly requested bytes into the young generation void *kore_alloc(size_t requested); diff --git a/lib/codegen/CreateTerm.cpp b/lib/codegen/CreateTerm.cpp index 3bb71c900..ac1119c9c 100644 --- a/lib/codegen/CreateTerm.cpp +++ b/lib/codegen/CreateTerm.cpp @@ -1,6 +1,7 @@ #include "kllvm/codegen/CreateTerm.h" #include "kllvm/codegen/CreateStaticTerm.h" #include "kllvm/codegen/Debug.h" +#include "kllvm/codegen/Options.h" #include "kllvm/codegen/ProofEvent.h" #include "kllvm/codegen/Util.h" @@ -1224,6 +1225,9 @@ bool make_function( = llvm::FunctionType::get(return_type, param_types, false); llvm::Function *apply_rule = get_or_insert_function(module, name, func_type); apply_rule->setLinkage(llvm::GlobalValue::InternalLinkage); + if (use_gcstrategy) { + apply_rule->setGC("gcs-llvm-backend"); + } init_debug_axiom(axiom->attributes()); std::string debug_name = name; if (axiom->attributes().contains(attribute_set::key::Label)) { diff --git a/lib/codegen/Options.cpp b/lib/codegen/Options.cpp index b345f879d..106c7fca1 100644 --- a/lib/codegen/Options.cpp +++ b/lib/codegen/Options.cpp @@ -48,6 +48,10 @@ cl::opt force_binary( "f", cl::desc("Force binary bitcode output to stdout"), cl::Hidden, cl::cat(codegen_lib_cat)); +cl::opt use_gcstrategy( + "use-gcstrategy", cl::desc("Use GC strategy defined for the LLVM backend."), + cl::Hidden, cl::init(false), cl::cat(codegen_lib_cat)); + namespace kllvm { void validate_codegen_args(bool is_tty) { diff --git a/lib/passes/CMakeLists.txt b/lib/passes/CMakeLists.txt index be5bc572e..e6e0b68ca 100644 --- a/lib/passes/CMakeLists.txt +++ b/lib/passes/CMakeLists.txt @@ -2,6 +2,7 @@ add_library(KLLVMPassInternal SetVisibilityHidden.cpp RemoveDeadKFunctions.cpp MustTailDeadArgElimination.cpp + GCStrategy.cpp PluginInfo.cpp ) @@ -9,6 +10,7 @@ add_library(KLLVMPass MODULE SetVisibilityHidden.cpp RemoveDeadKFunctions.cpp MustTailDeadArgElimination.cpp + GCStrategy.cpp PluginInfo.cpp ) diff --git a/lib/passes/GCStrategy.cpp b/lib/passes/GCStrategy.cpp new file mode 100644 index 000000000..32609d8dc --- /dev/null +++ b/lib/passes/GCStrategy.cpp @@ -0,0 +1,52 @@ +//===- Extend GCStrategy of llvm/CodeGen/GCStrategy.h ---------------------===// +// +// We extend the base GCStrategy as follows: +// - use gc.safepoints instead of (default) gc.roots. +// - specify that the RewriteStatepointsForGC pass should rewrite the calls of +// this function. +// - pointers with address space != 0 are pointing to GC-managed memory. +//===----------------------------------------------------------------------===// + +// NOLINTBEGIN + +#include "kllvm/codegen/GCStrategy.h" + +#include "llvm/CodeGen/GCMetadata.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/Support/Compiler.h" + +using namespace llvm; +using namespace kllvm; + +LLVMBackendGCStrategy::LLVMBackendGCStrategy() { + UseStatepoints = true; // Use gc.statepoints +#if LLVM_VERSION_MAJOR != 15 + UseRS4GC = true; // Rewrite the calls of a function that has this GCStrategy +#endif +} + +// Override +#if LLVM_VERSION_MAJOR == 15 +llvm::Optional +LLVMBackendGCStrategy::isGCManagedPointer(Type const *Ty) const { +#else +std::optional +LLVMBackendGCStrategy::isGCManagedPointer(Type const *Ty) const { +#endif + // Return false for any non-pointer type + if (!Ty->isPointerTy()) { + return false; + } + // Any pointer with address space != 0 is to managed memory. + PointerType const *PTy = dyn_cast(Ty); + if (PTy->getAddressSpace()) { + return true; + } + return false; +} + +// Add LLVMBackendGCStrategy to the global GCRegistry +static GCRegistry::Add + X("gcs-llvm-backend", "GC Strategy for the LLVM Backend"); + +// NOLINTEND diff --git a/test/defn/imp.kore b/test/defn/imp.kore index 4ac67067f..bccd8575e 100644 --- a/test/defn/imp.kore +++ b/test/defn/imp.kore @@ -1,6 +1,8 @@ // RUN: %interpreter // RUN: %check-grep // RUN: %check-statistics +// RUN: %gcs-interpreter +// RUN: %check-grep // RUN: %proof-interpreter // RUN: %check-proof-out [topCellInitializer{}(LblinitGeneratedTopCell{}()), org'Stop'kframework'Stop'attributes'Stop'Source{}("Source(/home/robertorosmaninho/rv/k/llvm-backend/src/main/native/llvm-backend/test/defn/k-files/imp.md)")] diff --git a/test/lit.cfg.py b/test/lit.cfg.py index 28f223eeb..cca48be9c 100644 --- a/test/lit.cfg.py +++ b/test/lit.cfg.py @@ -98,6 +98,13 @@ def exclude_x86_and_llvm18(s): exit 1 fi ''')), + ('%gcs-interpreter', one_line(''' + output=$(%kompile %s main --use-gcstrategy -o %t.interpreter 2>&1) + if [[ -n "$output" ]]; then + echo "llvm-kompile error or warning: $output" + exit 1 + fi + ''')), ('%proof-interpreter', one_line(''' output=$(%kompile %s main --proof-hint-instrumentation -o %t.interpreter 2>&1) if [[ -n "$output" ]]; then diff --git a/tools/llvm-kompile-codegen/main.cpp b/tools/llvm-kompile-codegen/main.cpp index 9cc5c5b0c..f98f7a9c9 100644 --- a/tools/llvm-kompile-codegen/main.cpp +++ b/tools/llvm-kompile-codegen/main.cpp @@ -1,3 +1,4 @@ +#include "kllvm/codegen/GCStrategy.h" #include #include #include @@ -147,6 +148,8 @@ void emit_metadata(llvm::Module &mod) { // NOLINTNEXTLINE(*-cognitive-complexity) int main(int argc, char **argv) { + // NOLINTNEXTLINE(*-identifier-naming) + LLVMBackendGCStrategy _gcs; // Unused. This is needed to ensure linking. initialize_llvm(); cl::HideUnrelatedOptions({&codegen_tool_cat, &codegen_lib_cat});