Skip to content

Commit

Permalink
Merge branch 'develop' into update-install-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Robertorosmaninho authored Dec 9, 2024
2 parents 8c19b10 + 8de42ea commit 69cc446
Show file tree
Hide file tree
Showing 16 changed files with 281 additions and 261 deletions.
6 changes: 6 additions & 0 deletions bin/llvm-kompile
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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")
Expand Down
8 changes: 8 additions & 0 deletions bin/llvm-kompile-clang
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -101,6 +102,10 @@ while [[ $# -gt 0 ]]; do
visibility_hidden=true
shift
;;
--use-gcstrategy)
use_gcstrategy=true
shift
;;
*)
;;
esac
Expand Down Expand Up @@ -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"
Expand Down
37 changes: 37 additions & 0 deletions include/kllvm/codegen/GCStrategy.h
Original file line number Diff line number Diff line change
@@ -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<bool> isGCManagedPointer(llvm::Type const *Ty) const override;
#else
std::optional<bool> isGCManagedPointer(llvm::Type const *Ty) const override;
#endif
};

} // namespace kllvm

#endif // LLVM_BACKEND_GC_STRATEGY_H

// NOLINTEND
1 change: 1 addition & 0 deletions include/kllvm/codegen/Options.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extern llvm::cl::opt<bool> no_optimize;
extern llvm::cl::opt<bool> emit_object;
extern llvm::cl::opt<bool> binary_ir;
extern llvm::cl::opt<bool> force_binary;
extern llvm::cl::opt<bool> use_gcstrategy;
extern llvm::cl::opt<bool> proof_hint_instrumentation;
extern llvm::cl::opt<bool> proof_hint_instrumentation_slow;
extern llvm::cl::opt<bool> keep_frame_pointer;
Expand Down
145 changes: 111 additions & 34 deletions include/runtime/arena.h
Original file line number Diff line number Diff line change
@@ -1,38 +1,49 @@
#ifndef ARENA_H
#define ARENA_H

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <sys/types.h>
#include <utility>

#include "runtime/alloc.h"

extern "C" {

size_t const HYPERBLOCK_SIZE = (size_t)BLOCK_SIZE * 1024 * 1024;

// An arena can be used to allocate objects that can then be deallocated all at
// once.
class arena {
public:
arena(char id)
: allocation_semispace_id(id) { }
: allocation_semispace_id(id) {
initialize_semispace();
}

// Allocates the requested number of bytes as a contiguous region and returns a
// pointer to the first allocated byte.
// If called with requested size greater than the maximun single allocation
// size, the space is allocated in a general (not garbage collected pool).
void *kore_arena_alloc(size_t requested);

// Returns the address of the first byte that belongs in the given arena.
// Returns 0 if nothing has been allocated ever in that arena.
char *arena_start_ptr() const;
char *arena_start_ptr() const {
return current_addr_ptr ? current_addr_ptr + sizeof(memory_block_header)
: nullptr;
}

// Returns a pointer to a location holding the address of last allocated
// byte in the given arena plus 1.
// This address is 0 if nothing has been allocated ever in that arena.
char **arena_end_ptr();
char **arena_end_ptr() { return &allocation_ptr; }

// return the total number of allocatable bytes currently in the arena in its
// active semispace.
size_t arena_size() const;
size_t arena_size() const {
update_num_blocks();
return BLOCK_SIZE * std::max(num_blocks, num_collection_blocks);
}

// Clears the current allocation space by setting its start back to its first
// block. It is used during garbage collection to effectively collect all of the
Expand All @@ -41,15 +52,18 @@ class arena {

// Resizes the last allocation as long as the resize does not require a new
// block allocation.
// Returns the address of the byte following the last newlly allocated byte when
// the resize succeeds, returns 0 otherwise.
void *arena_resize_last_alloc(ssize_t increase);
// Returns the address of the byte following the last newlly allocated byte.
void *arena_resize_last_alloc(ssize_t increase) {
return (allocation_ptr += increase);
}

// Returns the given arena's current collection semispace ID.
// Each arena has 2 semispace IDs one equal to the arena ID and the other equal
// to the 1's complement of the arena ID. At any time one of these semispaces
// is used for allocation and the other is used for collection.
char get_arena_collection_semispace_id() const;
char get_arena_collection_semispace_id() const {
return ~allocation_semispace_id;
}

// Exchanges the current allocation and collection semispaces and clears the new
// current allocation semispace by setting its start back to its first block.
Expand All @@ -61,7 +75,7 @@ class arena {
// by the blocks of that arena. This difference will include blocks containing
// sentinel bytes. Undefined behavior will result if the pointers belong to
// different arenas.
static ssize_t ptr_diff(char *ptr1, char *ptr2);
static ssize_t ptr_diff(char *ptr1, char *ptr2) { return ptr1 - ptr2; }

// Given a starting pointer to an address allocated in an arena and a size in
// bytes, this function returns a pointer to an address allocated in the
Expand All @@ -72,42 +86,70 @@ class arena {
// 3rd argument: the address of last allocated byte in the arena plus 1
// Return value: the address allocated in the arena after size bytes from the
// starting pointer, or 0 if this is equal to the 3rd argument.
static char *move_ptr(char *ptr, size_t size, char const *arena_end_ptr);
static char *move_ptr(char *ptr, size_t size, char const *arena_end_ptr) {
char *next_ptr = ptr + size;
return (next_ptr == arena_end_ptr) ? 0 : next_ptr;
}

// Returns the ID of the semispace where the given address was allocated.
// The behavior is undefined if called with an address that has not been
// allocated within an arena.
static char get_arena_semispace_id_of_object(void *ptr);

private:
struct memory_block_header {
char *next_block;
union memory_block_header {
//
// Currently the header just holds the semispace id. But we need it to be a
// multiple of sizeof(char*) for alignment purposes so we add a dummy char*.
//
char semispace;
char *alignment_dummy;
};

void fresh_block();
static memory_block_header *mem_block_header(void *ptr);
//
// We update the number of 1MB blocks actually written to, only when we need this value,
// or before a garbage collection rather than trying to determine when we write to a fresh block.
//
void update_num_blocks() const {
//
// Calculate how many 1M blocks of the current arena we used.
//
size_t num_used_blocks
= (allocation_ptr - current_addr_ptr - 1) / BLOCK_SIZE + 1;
if (num_used_blocks > num_blocks)
num_blocks = num_used_blocks;
}

void initialize_semispace();

// helper function for `kore_arena_alloc`. Do not call directly.
void *do_alloc_slow(size_t requested);
static memory_block_header *mem_block_header(void *ptr) {
uintptr_t address = reinterpret_cast<uintptr_t>(ptr);
return reinterpret_cast<arena::memory_block_header *>(
(address - 1) & ~(HYPERBLOCK_SIZE - 1));
}

char *first_block; // beginning of first block
char *block; // where allocations are being made in current block
char *block_start; // start of current block
char *block_end; // 1 past end of current block
char *first_collection_block; // beginning of other semispace
size_t num_blocks; // number of blocks in current semispace
size_t num_collection_blocks; // number of blocks in other semispace
//
// Current semispace where allocations are being made.
//
char *current_addr_ptr; // pointer to start of current address space
char *allocation_ptr; // next available location in current semispace
char *tripwire; // allocating past this triggers slow allocation
mutable size_t
num_blocks; // notional number of BLOCK_SIZE blocks in current semispace
char allocation_semispace_id; // id of current semispace
//
// Semispace where allocations will be made during and after garbage collect.
//
char *collection_addr_ptr
= nullptr; // pointer to start of collection address space
size_t num_collection_blocks
= 0; // notional number of BLOCK_SIZE blocks in collection semispace
};

// Macro to define a new arena with the given ID. Supports IDs ranging from 0 to
// 127.
#define REGISTER_ARENA(name, id) static thread_local arena name(id)

#define MEM_BLOCK_START(ptr) \
((char *)(((uintptr_t)(ptr)-1) & ~(BLOCK_SIZE - 1)))

#ifdef __MACH__
//
// thread_local disabled for Apple
Expand All @@ -120,16 +162,51 @@ extern thread_local bool time_for_collection;
size_t get_gc_threshold();

inline void *arena::kore_arena_alloc(size_t requested) {
if (block + requested > block_end) {
return do_alloc_slow(requested);
if (allocation_ptr + requested >= tripwire) {
//
// We got close to or past the last location accessed in this address range so far,
// depending on the requested size and tripwire setting. This triggers a garbage
// collect when allowed.
//
time_for_collection = true;
tripwire = current_addr_ptr
+ HYPERBLOCK_SIZE; // won't trigger again until arena swap
}
void *result = block;
block += requested;
void *result = allocation_ptr;
allocation_ptr += requested;
MEM_LOG(
"Allocation at %p (size %zd), next alloc at %p (if it fits)\n", result,
requested, block);
"Allocation at %p (size %zd), next alloc at %p\n", result, requested,
block);
return result;
}

inline void arena::arena_clear() {
//
// We set the allocation pointer to the first available address.
//
allocation_ptr = arena_start_ptr();
//
// If the number of blocks we've touched is >= threshold, we want to trigger
// a garbage collection if we get within 1 block of the end of this area.
// Otherwise we only want to generate a garbage collect if we allocate off the
// end of this area.
//
tripwire = current_addr_ptr
+ (num_blocks - (num_blocks >= get_gc_threshold())) * BLOCK_SIZE;
}

inline void arena::arena_swap_and_clear() {
update_num_blocks(); // so we save the correct number of touched blocks
std::swap(current_addr_ptr, collection_addr_ptr);
std::swap(num_blocks, num_collection_blocks);
allocation_semispace_id = ~allocation_semispace_id;
if (current_addr_ptr == nullptr) {
//
// The other semispace hasn't be initialized yet.
//
initialize_semispace();
} else
arena_clear();
}
}
#endif // ARENA_H
4 changes: 4 additions & 0 deletions lib/codegen/CreateTerm.cpp
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -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)) {
Expand Down
5 changes: 3 additions & 2 deletions lib/codegen/EmitConfigParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ static std::pair<llvm::Value *, llvm::BasicBlock *> get_eval(
auto *malloc = create_malloc(
creator.get_current_block(),
llvm::ConstantExpr::getSizeOf(result->getType()),
get_or_insert_function(mod, "malloc", ptr_ty, ptr_ty));
get_or_insert_function(mod, "kore_alloc_always_gc", ptr_ty, ptr_ty));
new llvm::StoreInst(result, malloc, creator.get_current_block());
retval = malloc;
break;
Expand Down Expand Up @@ -563,7 +563,8 @@ static void emit_get_token(kore_definition *definition, llvm::Module *module) {
case_block);
auto *malloc = create_malloc(
case_block, llvm::ConstantExpr::getSizeOf(compare->getType()),
get_or_insert_function(module, "malloc", ptr_ty, ptr_ty));
get_or_insert_function(
module, "kore_alloc_always_gc", ptr_ty, ptr_ty));
new llvm::StoreInst(compare, malloc, case_block);
phi->addIncoming(malloc, case_block);
llvm::BranchInst::Create(merge_block, case_block);
Expand Down
4 changes: 4 additions & 0 deletions lib/codegen/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ cl::opt<bool> force_binary(
"f", cl::desc("Force binary bitcode output to stdout"), cl::Hidden,
cl::cat(codegen_lib_cat));

cl::opt<bool> 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) {
Expand Down
2 changes: 2 additions & 0 deletions lib/passes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ add_library(KLLVMPassInternal
SetVisibilityHidden.cpp
RemoveDeadKFunctions.cpp
MustTailDeadArgElimination.cpp
GCStrategy.cpp
PluginInfo.cpp
)

add_library(KLLVMPass MODULE
SetVisibilityHidden.cpp
RemoveDeadKFunctions.cpp
MustTailDeadArgElimination.cpp
GCStrategy.cpp
PluginInfo.cpp
)

Expand Down
Loading

0 comments on commit 69cc446

Please sign in to comment.