Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions effekt/jvm/src/main/scala/effekt/Runner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -287,17 +287,24 @@ object LLVMRunner extends Runner[String] {

def libuvArgs(using C: Context): Seq[String] =
val OS = System.getProperty("os.name").toLowerCase

// only relevant for macOS
val arch: String = System.getProperty("os.arch")
assert(!OS.contains("mac") || List("aarch64", "x86_64").contains(arch), "If you have a macbook -> It should have either an aarch64 or x86_64 architecture.")

val libraries = C.config.clangLibraries.toOption.map(file).orElse {
OS match {
case os if os.contains("mac") => Some(file("/opt/homebrew/lib"))
case os if os.contains("mac") && arch.equals("aarch64") => Some(file("/opt/homebrew/lib"))
case os if os.contains("mac") => Some(file("/usr/local/lib"))
case os if os.contains("win") => None
case os if os.contains("linux") => Some(file("/usr/local/lib"))
case os => None
}
}
val includes = C.config.clangIncludes.toOption.map(file).orElse {
OS match {
case os if os.contains("mac") => Some(file("/opt/homebrew/include"))
case os if os.contains("mac") && arch.equals("aarch64") => Some(file("/opt/homebrew/include"))
case os if os.contains("mac") => Some(file("/usr/local/include"))
case os if os.contains("win") => None
case os if os.contains("linux") => Some(file("/usr/local/include"))
case os => None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package generator
package llvm

import effekt.machine
import effekt.machine.Variable
import effekt.util.intercalate
import effekt.util.messages.ErrorReporter
import effekt.machine.analysis.*
Expand All @@ -24,6 +25,7 @@ object Transformer {

val entryInstructions = List(
Call("stack", Ccc(), stackType, withEmptyStack, List()),
Call("", Ccc(), VoidType(), initializeMemory, List()),
Call("_", Tailcc(false), VoidType(), transform(entry), List(LocalReference(stackType, "stack"))))
val entryBlock = BasicBlock("entry", entryInstructions, RetVoid())
val entryFunction = Function(External(), Ccc(), VoidType(), "effektMain", List(), List(entryBlock))
Expand Down Expand Up @@ -520,12 +522,17 @@ object Transformer {
kind match {
case ObjectEraser =>
val eraser = ConstantGlobal(freshName("eraser"));
defineFunction(eraser.name, List(Parameter(environmentType, "environment"))) {
defineFunction(eraser.name, List(Parameter(objectType, "object"))) {
emit(Comment(s"${kind} eraser, ${freshEnvironment.length} free variables"))

// Use call @objectEnvironment to get environment pointer
emit(Call("environment", Ccc(), environmentType, ConstantGlobal("objectEnvironment"), List(LocalReference(objectType, "object"))));

// TODO avoid unnecessary loads
loadEnvironmentAt(LocalReference(environmentType, "environment"), freshEnvironment, Object);
eraseValues(freshEnvironment, Set());

emit(Call("", Ccc(), VoidType(), ConstantGlobal("cFree"), List(LocalReference(objectType, "object"))));
RetVoid()
};
eraser
Expand Down Expand Up @@ -719,6 +726,7 @@ object Transformer {
emit(Load(returnAddressName, returnAddressType, returnAddressPointer, StackPointer));
}

val initializeMemory = ConstantGlobal("cInitializeMemory");
val newObject = ConstantGlobal("newObject");
val objectEnvironment = ConstantGlobal("objectEnvironment");

Expand Down
7 changes: 4 additions & 3 deletions libraries/common/array.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ extern type Array[T]
extern llvm """
declare noalias ptr @calloc(i64, i64)

define void @array_erase_fields(ptr %array_pointer) {
define void @array_erase_fields(%Object %object) {
entry:
%data_pointer = getelementptr inbounds i64, ptr %array_pointer, i64 1
%size = load i64, ptr %array_pointer, align 8
%data_pointer = getelementptr inbounds i64, ptr %object, i64 1
%size = load i64, ptr %object, align 8
%size_eq_0 = icmp eq i64 %size, 0
br i1 %size_eq_0, label %exit, label %loop
loop:
Expand All @@ -36,6 +36,7 @@ loop:
%cmp = icmp ult i64 %inc, %size
br i1 %cmp, label %loop, label %exit
exit:
call void @free(%Object %object)
ret void
}
"""
Expand Down
9 changes: 6 additions & 3 deletions libraries/common/ref.effekt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ extern js """
"""

extern llvm """
define void @c_ref_erase_field(ptr %0) {
%field = load %Pos, ptr %0, align 8
tail call void @erasePositive(%Pos %field)
define void @c_ref_erase_field(%Object %object) {
%headerSize = ptrtoint ptr getelementptr (%Header, ptr null, i64 1) to i64
%environment = getelementptr i8, ptr %object, i64 %headerSize
%field = load %Pos, ptr %environment, align 8
call void @free(%Object %object)
call void @erasePositive(%Pos %field)
ret void
}
"""
Expand Down
6 changes: 4 additions & 2 deletions libraries/llvm/bytearray.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define EFFEKT_BYTEARRAY_C

#include <string.h> // For memcopy
#include <stdlib.h>

/** We represent bytearrays like positive types.
*
Expand All @@ -16,10 +17,11 @@
*/


void c_bytearray_erase_noop(void *envPtr) { (void)envPtr; }
void c_bytearray_erase_noop(void* object) { free(object); }

struct Pos c_bytearray_new(const Int size) {
void *objPtr = malloc(sizeof(struct Header) + size);
int object_size = sizeof(struct Header) + size;
void *objPtr = malloc(object_size);
struct Header *headerPtr = objPtr;
*headerPtr = (struct Header) { .rc = 0, .eraser = c_bytearray_erase_noop, };
return (struct Pos) {
Expand Down
124 changes: 124 additions & 0 deletions libraries/llvm/cMalloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

// -----------------------------
// Strukturdefinition
// -----------------------------

/**
* @brief Block-Struktur für die Freelist.
*
* Jeder freie Block zeigt auf den nächsten freien Block.
*/
typedef struct Block {
struct Block* next;
} Block;

// -----------------------------
// Globale Variablen
// -----------------------------

static Block* freeList = NULL; // Kopf der Freelist
static uint8_t* nextUnusedBlock = NULL; // Zeiger auf nächsten unbenutzten Block
static uint8_t* endOfChunk = NULL; // Ende des allokierten Speichers
static const int blockSize = 128; // Größe jedes Blocks (1KB)

// -----------------------------
// Initialisierung
// -----------------------------

/**
* @brief Initialisiert den großen Speicherbereich (4GB).
*/
void cInitializeMemory(void) {
size_t chunkSize = (size_t)4294967296ULL; // 4GB
uint8_t* mem = (uint8_t*)malloc(chunkSize);
if (!mem) {
fprintf(stderr, "malloc() failed!\n");
exit(1);
}

nextUnusedBlock = mem;
endOfChunk = mem + chunkSize;
// printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk);
}

// -----------------------------
// Allokator
// -----------------------------

/**
* @brief Einfacher Speicher-Allocator.
*
* Wenn die Freelist leer ist, nimmt er den nächsten Block im Chunk.
* Wenn die Freelist nicht leer ist, nimmt er den ersten Eintrag daraus.
*
* @param size Ignoriert in diesem simplen Modell (wir geben immer 1KB).
* @return void* Zeiger auf den allokierten Block.
*/
void* cMalloc(uint8_t size) {
// 1. Falls Freelist leer ist → neuer Block
if (freeList == NULL) {
if (nextUnusedBlock + blockSize > endOfChunk) {
fprintf(stderr, "Out of memory!\n");
return NULL;
}

void* block = nextUnusedBlock;
nextUnusedBlock += blockSize;
// printf("[malloc] New block: %p\n", block);
return block;
}


// 2. Falls Freelist nicht leer ist → wiederverwenden
Block* block = freeList;
freeList = block->next;
// printf("[malloc] Reusing block: %p\n", (void*)block);
// printf("[malloc] freeList: %p\n", (void*)freeList);
return (void*)block;
// return malloc(size);
}

// -----------------------------
// Free-Funktion
// -----------------------------

/**
* @brief Gibt einen Block zurück in die Freelist.
*
* @param ptr Zeiger auf den Block.
*/
void cFree(void* ptr) {
if (!ptr) return;

Block* block = (Block*)ptr;
block->next = freeList;
freeList = block;

// printf("[free] Freed block: %p\n", ptr);
// free(ptr);
}

void* cRealloc(void* ptr, uint8_t size) {
printf("cRealloc: %p, %d\n", ptr, size);
return realloc(ptr,size);
}

// -----------------------------
// Test / Demo
// -----------------------------

//int main(void) {
// cInitializeMemory();
//
// void* a = cMalloc(1024);
// void* b = cMalloc(1024);
// cFree(a);
// void* c = cMalloc(1024); // sollte a wiederverwenden
// cFree(b);
// cFree(c);
//
// return 0;
//}
4 changes: 2 additions & 2 deletions libraries/llvm/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ void c_promise_resolve(struct Pos promise, struct Pos value, Stack stack) {
}
// TODO stack overflow?
// We need to erase the promise now, since we consume it.
erasePositive(promise);
// erasePositive(promise);
}

void c_promise_await(struct Pos promise, Stack stack) {
Expand Down Expand Up @@ -429,7 +429,7 @@ void c_promise_await(struct Pos promise, Stack stack) {
break;
};
// TODO hmm, stack overflow?
erasePositive(promise);
erasePositive(promise); // df: Otherwise, interleave_promises.effekt fails.
}

struct Pos c_promise_make() {
Expand Down
1 change: 1 addition & 0 deletions libraries/llvm/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "types.c"
#include "bytearray.c"
#include "cMalloc.c"
#include "io.c"
#include "panic.c"

Expand Down
83 changes: 79 additions & 4 deletions libraries/llvm/rts.ll
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@

; Foreign imports

declare void @cInitializeMemory()
declare ptr @cMalloc(i64)
declare void @cFree(ptr)
declare ptr @cRealloc(ptr, i64)

declare ptr @malloc(i64)
declare void @free(ptr)
declare ptr @realloc(ptr, i64)
Expand Down Expand Up @@ -137,10 +142,82 @@ define private %Prompt @freshPrompt() {

; Garbage collection

; A type for the free list
%struct.Block = type { %struct.Block* }

@freeList = global %struct.Block* null
@nextUnusedBlock = global i8* null
@endOfChunk = global i8* null
@blockSize = global i64 1024 ; each Block is 1KB

define private void @initializeMemory() {
; Step 01: mem = malloc(4294967296)
%mem = call i8* @malloc(i64 4294967296)

; Step 02: nextUnusedBlock = mem
store i8* %mem, i8** @nextUnusedBlock

; Step 03: endOfChunk = mem + 4294967296
%endPtr = getelementptr i8, i8* %mem, i64 4294967296
store i8* %endPtr, i8** @endOfChunk

ret void
}

define private %Object @myMalloc(i64 %size) {
entry:
; Step 01: Check if the free list pointer is not null
%freeList = load %struct.Block*, %struct.Block** @freeList
%isNull = icmp eq %struct.Block* %freeList, null
br i1 %isNull, label %newAllocate, label %reuse

; In case we can recycle a block from the free list, we do so and jump to the reuse label.
reuse:
; Step 01: block = freeList
%block = load %struct.Block*, %struct.Block** @freeList

; Step 02: freeList = freeList.next
%nextPtr = getelementptr %struct.Block, %struct.Block* %block, i32 0, i32 0
%nextBlock = load %struct.Block*, %struct.Block** %nextPtr
store %struct.Block* %nextBlock, %struct.Block** @freeList

; Step 03: Return
%ret = bitcast %struct.Block* %block to %Object
ret %Object %ret

; In case we do not have a block to reuse
newAllocate:
%nu = load i8*, i8** @nextUnusedBlock
%end = load i8*, i8** @endOfChunk
%blockSize = load i64, i64* @blockSize

; block = next_unused
%next_plus = getelementptr i8, i8* %nu, i64 %blockSize
store i8* %next_plus, i8** @nextUnusedBlock

%ret2 = bitcast i8* %nu to %Object
ret %Object %ret2

}

define private void @myFree(%Object %object) {
; block = (Block*)object
%block = bitcast %Object %object to %struct.Block*

; block.next = freeList
%nextField = getelementptr %struct.Block, %struct.Block* %block, i32 0, i32 0
%freeList = load %struct.Block*, %struct.Block** @freeList
store %struct.Block* %freeList, %struct.Block** %nextField

; freeList = block
store %struct.Block* %block, %struct.Block** @freeList ; <- fails
ret void
}

define private %Object @newObject(%Eraser %eraser, i64 %environmentSize) alwaysinline {
%headerSize = ptrtoint ptr getelementptr (%Header, ptr null, i64 1) to i64
%size = add i64 %environmentSize, %headerSize
%object = call ptr @malloc(i64 %size)
%object = call ptr @cMalloc(i64 %size)
%objectReferenceCount = getelementptr %Header, ptr %object, i64 0, i32 0
%objectEraser = getelementptr %Header, ptr %object, i64 0, i32 1
store %ReferenceCount 0, ptr %objectReferenceCount, !alias.scope !14, !noalias !24
Expand Down Expand Up @@ -198,9 +275,7 @@ define private void @eraseObject(%Object %object) alwaysinline {
free:
%objectEraser = getelementptr %Header, ptr %object, i64 0, i32 1
%eraser = load %Eraser, ptr %objectEraser, !alias.scope !14, !noalias !24
%environment = call %Environment @objectEnvironment(%Object %object)
call void %eraser(%Environment %environment)
call void @free(%Object %object)
call void %eraser(%Object %object)
br label %done

done:
Expand Down
Loading