From d24fd1a8b69790af51be43966c064d402a2b1903 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 10 Oct 2025 14:38:03 +0200 Subject: [PATCH 01/57] Fixed the linking of the uv library for Intel Macbooks --- effekt/jvm/src/main/scala/effekt/Runner.scala | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/effekt/jvm/src/main/scala/effekt/Runner.scala b/effekt/jvm/src/main/scala/effekt/Runner.scala index 056b86b396..b9f34f2535 100644 --- a/effekt/jvm/src/main/scala/effekt/Runner.scala +++ b/effekt/jvm/src/main/scala/effekt/Runner.scala @@ -287,9 +287,15 @@ 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 @@ -297,7 +303,8 @@ object LLVMRunner extends Runner[String] { } 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 From 4910656526366ec5d342eed661b2df475eab6171 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 24 Sep 2025 15:12:29 +0200 Subject: [PATCH 02/57] Basic Bumb allocation --- .../effekt/generator/llvm/Transformer.scala | 2 ++ libraries/llvm/rts.ll | 24 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index e4b41585e6..1082c8c76f 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -24,6 +24,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)) @@ -719,6 +720,7 @@ object Transformer { emit(Load(returnAddressName, returnAddressType, returnAddressPointer, StackPointer)); } + val initializeMemory = ConstantGlobal("initializeMemory"); val newObject = ConstantGlobal("newObject"); val objectEnvironment = ConstantGlobal("objectEnvironment"); diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 604d75a25f..8c95c1ae85 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -135,10 +135,30 @@ define private %Prompt @freshPrompt() { ; Garbage collection + +@freeList = global ptr null + +define private %Object @myMalloc(i64 %size) { + %freeList = load ptr, ptr @freeList + %nextFree = getelementptr i8, ptr %freeList, i64 %size + store ptr %nextFree, ptr @freeList + ret ptr %freeList +} + +define private void @myFree(ptr %object) { + ret void +} + +define private void @initializeMemory() { + %freeList = call ptr @malloc(i64 4294967296) + store ptr %freeList, ptr @freeList + 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 @myMalloc(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 @@ -198,7 +218,7 @@ define private void @eraseObject(%Object %object) alwaysinline { %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 @myFree(%Object %object) br label %done done: From 6d197f191def875719d5d7ab0b528556f46e73cb Mon Sep 17 00:00:00 2001 From: Flat Date: Mon, 6 Oct 2025 11:30:14 +0200 Subject: [PATCH 03/57] First approach of a 2nd step of memory-management, but first done in C instead of LLVM because of better debug possibilities --- .../effekt/generator/llvm/Transformer.scala | 2 +- libraries/llvm/bytearray.c | 6 +- libraries/llvm/cMalloc.c | 124 ++++++++++++++++++ libraries/llvm/main.c | 1 + libraries/llvm/rts.ll | 83 ++++++++++-- 5 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 libraries/llvm/cMalloc.c diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 1082c8c76f..8558995136 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -720,7 +720,7 @@ object Transformer { emit(Load(returnAddressName, returnAddressType, returnAddressPointer, StackPointer)); } - val initializeMemory = ConstantGlobal("initializeMemory"); + val initializeMemory = ConstantGlobal("cInitializeMemory"); val newObject = ConstantGlobal("newObject"); val objectEnvironment = ConstantGlobal("objectEnvironment"); diff --git a/libraries/llvm/bytearray.c b/libraries/llvm/bytearray.c index 1750994577..2b50d38c9c 100644 --- a/libraries/llvm/bytearray.c +++ b/libraries/llvm/bytearray.c @@ -3,6 +3,9 @@ #include // For memcopy +extern void* cMalloc(uint8_t size); + + /** We represent bytearrays like positive types. * * - The field `tag` contains the size @@ -19,7 +22,8 @@ void c_bytearray_erase_noop(void *envPtr) { (void)envPtr; } struct Pos c_bytearray_new(const Int size) { - void *objPtr = malloc(sizeof(struct Header) + size); + int object_size = sizeof(struct Header) + size; + void *objPtr = cMalloc(object_size); struct Header *headerPtr = objPtr; *headerPtr = (struct Header) { .rc = 0, .eraser = c_bytearray_erase_noop, }; return (struct Pos) { diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c new file mode 100644 index 0000000000..f7dd07c872 --- /dev/null +++ b/libraries/llvm/cMalloc.c @@ -0,0 +1,124 @@ +#include +#include +#include + +// ----------------------------- +// 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 = 1024; // 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; +//} diff --git a/libraries/llvm/main.c b/libraries/llvm/main.c index d95f9a5359..916da9650b 100644 --- a/libraries/llvm/main.c +++ b/libraries/llvm/main.c @@ -11,6 +11,7 @@ #include "types.c" #include "bytearray.c" +#include "cMalloc.c" #include "io.c" #include "panic.c" diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 8c95c1ae85..f73e4afa4a 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -94,6 +94,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) @@ -135,30 +140,82 @@ define private %Prompt @freshPrompt() { ; Garbage collection +; A type for the free list +%struct.Block = type { %struct.Block* } -@freeList = global ptr null +@freeList = global %struct.Block* null +@nextUnusedBlock = global i8* null +@endOfChunk = global i8* null +@blockSize = global i64 1024 ; each Block is 1KB -define private %Object @myMalloc(i64 %size) { - %freeList = load ptr, ptr @freeList - %nextFree = getelementptr i8, ptr %freeList, i64 %size - store ptr %nextFree, ptr @freeList - ret ptr %freeList -} +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 -define private void @myFree(ptr %object) { ret void } -define private void @initializeMemory() { - %freeList = call ptr @malloc(i64 4294967296) - store ptr %freeList, ptr @freeList +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 @myMalloc(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 @@ -218,7 +275,7 @@ define private void @eraseObject(%Object %object) alwaysinline { %eraser = load %Eraser, ptr %objectEraser, !alias.scope !14, !noalias !24 %environment = call %Environment @objectEnvironment(%Object %object) call void %eraser(%Environment %environment) - call void @myFree(%Object %object) + call void @cFree(%Object %object) br label %done done: From ec4ccb1fe6ef17906dbc723839c1d994a886d23c Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 24 Oct 2025 18:39:57 +0200 Subject: [PATCH 04/57] Fixed-sized allocation approach. --- .../scala/effekt/generator/llvm/Transformer.scala | 8 +++++++- libraries/common/array.effekt | 7 ++++--- libraries/common/ref.effekt | 9 ++++++--- libraries/llvm/bytearray.c | 8 +++----- libraries/llvm/cMalloc.c | 12 ++++++------ libraries/llvm/io.c | 4 ++-- libraries/llvm/rts.ll | 4 +--- 7 files changed, 29 insertions(+), 23 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 8558995136..8f012fd1e5 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -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.* @@ -521,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 diff --git a/libraries/common/array.effekt b/libraries/common/array.effekt index 57c621a199..ecd2ad3511 100644 --- a/libraries/common/array.effekt +++ b/libraries/common/array.effekt @@ -20,10 +20,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: @@ -37,6 +37,7 @@ loop: %cmp = icmp ult i64 %inc, %size br i1 %cmp, label %loop, label %exit exit: + call void @free(%Object %object) ret void } """ diff --git a/libraries/common/ref.effekt b/libraries/common/ref.effekt index b206a02b8f..ccf4eb09fa 100644 --- a/libraries/common/ref.effekt +++ b/libraries/common/ref.effekt @@ -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 } """ diff --git a/libraries/llvm/bytearray.c b/libraries/llvm/bytearray.c index 2b50d38c9c..3dce3a6d92 100644 --- a/libraries/llvm/bytearray.c +++ b/libraries/llvm/bytearray.c @@ -2,9 +2,7 @@ #define EFFEKT_BYTEARRAY_C #include // For memcopy - -extern void* cMalloc(uint8_t size); - +#include /** We represent bytearrays like positive types. * @@ -19,11 +17,11 @@ extern void* cMalloc(uint8_t size); */ -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) { int object_size = sizeof(struct Header) + size; - void *objPtr = cMalloc(object_size); + void *objPtr = malloc(object_size); struct Header *headerPtr = objPtr; *headerPtr = (struct Header) { .rc = 0, .eraser = c_bytearray_erase_noop, }; return (struct Pos) { diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index f7dd07c872..fcb4429962 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -22,7 +22,7 @@ typedef struct Block { 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 = 1024; // Größe jedes Blocks (1KB) +static const int blockSize = 128; // Größe jedes Blocks (1KB) // ----------------------------- // Initialisierung @@ -41,7 +41,7 @@ void cInitializeMemory(void) { nextUnusedBlock = mem; endOfChunk = mem + chunkSize; - printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); +// printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); } // ----------------------------- @@ -67,7 +67,7 @@ void* cMalloc(uint8_t size) { void* block = nextUnusedBlock; nextUnusedBlock += blockSize; - printf("[malloc] New block: %p\n", block); +// printf("[malloc] New block: %p\n", block); return block; } @@ -75,8 +75,8 @@ void* cMalloc(uint8_t size) { // 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); +// printf("[malloc] Reusing block: %p\n", (void*)block); +// printf("[malloc] freeList: %p\n", (void*)freeList); return (void*)block; // return malloc(size); } @@ -97,7 +97,7 @@ void cFree(void* ptr) { block->next = freeList; freeList = block; - printf("[free] Freed block: %p\n", ptr); +// printf("[free] Freed block: %p\n", ptr); // free(ptr); } diff --git a/libraries/llvm/io.c b/libraries/llvm/io.c index 6cfc08e0dc..c7eb64657f 100644 --- a/libraries/llvm/io.c +++ b/libraries/llvm/io.c @@ -670,7 +670,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) { @@ -702,7 +702,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() { diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index f73e4afa4a..cf9acfac08 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -273,9 +273,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 @cFree(%Object %object) + call void %eraser(%Object %object) br label %done done: From ecf05a59796503d76159b623b467aff26b4b658c Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 10:29:20 +0100 Subject: [PATCH 05/57] cMalloc -> acquire and cFree -> release --- .../effekt/generator/llvm/Transformer.scala | 2 +- libraries/llvm/cMalloc.c | 60 ++++--------------- libraries/llvm/rts.ll | 6 +- 3 files changed, 17 insertions(+), 51 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 8f012fd1e5..17b58deeaa 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -532,7 +532,7 @@ object Transformer { loadEnvironmentAt(LocalReference(environmentType, "environment"), freshEnvironment, Object); eraseValues(freshEnvironment, Set()); - emit(Call("", Ccc(), VoidType(), ConstantGlobal("cFree"), List(LocalReference(objectType, "object")))); + emit(Call("", Ccc(), VoidType(), ConstantGlobal("release"), List(LocalReference(objectType, "object")))); RetVoid() }; eraser diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index fcb4429962..a0517fa12e 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -2,9 +2,6 @@ #include #include -// ----------------------------- -// Strukturdefinition -// ----------------------------- /** * @brief Block-Struktur für die Freelist. @@ -15,21 +12,15 @@ 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 -// ----------------------------- +static const int blockSize = 128; // Größe jedes Blocks (128B) /** - * @brief Initialisiert den großen Speicherbereich (4GB). + * Initialisiert den großen Speicherbereich (4GB). */ void cInitializeMemory(void) { size_t chunkSize = (size_t)4294967296ULL; // 4GB @@ -41,7 +32,7 @@ void cInitializeMemory(void) { nextUnusedBlock = mem; endOfChunk = mem + chunkSize; -// printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); + printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); } // ----------------------------- @@ -49,15 +40,12 @@ void cInitializeMemory(void) { // ----------------------------- /** - * @brief Einfacher Speicher-Allocator. + * 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) { +void* acquire(uint8_t size) { // 1. Falls Freelist leer ist → neuer Block if (freeList == NULL) { if (nextUnusedBlock + blockSize > endOfChunk) { @@ -67,58 +55,36 @@ void* cMalloc(uint8_t size) { void* block = nextUnusedBlock; nextUnusedBlock += blockSize; -// printf("[malloc] New block: %p\n", block); + 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); + 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. + * Gibt einen Block zurück in die Freelist. * * @param ptr Zeiger auf den Block. */ -void cFree(void* ptr) { +void release(void* ptr) { if (!ptr) return; Block* block = (Block*)ptr; block->next = freeList; freeList = block; -// printf("[free] Freed block: %p\n", ptr); -// free(ptr); + printf("[free] Freed block: %p\n", ptr); } +// @Deprecated 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; -//} +} \ No newline at end of file diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index cf9acfac08..7d8525bc02 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -95,8 +95,8 @@ ; Foreign imports declare void @cInitializeMemory() -declare ptr @cMalloc(i64) -declare void @cFree(ptr) +declare ptr @acquire(i64) +declare void @release(ptr) declare ptr @cRealloc(ptr, i64) declare ptr @malloc(i64) @@ -215,7 +215,7 @@ define private void @myFree(%Object %object) { 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 @cMalloc(i64 %size) + %object = call ptr @acquire(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 From afc9ac3194c15a960198a8b29cc80adbc789dd78 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 15:12:51 +0100 Subject: [PATCH 06/57] renaming: array_erase_fields -> array_erase --- libraries/common/array.effekt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/common/array.effekt b/libraries/common/array.effekt index ecd2ad3511..89c85ffd86 100644 --- a/libraries/common/array.effekt +++ b/libraries/common/array.effekt @@ -20,7 +20,7 @@ extern type Array[T] extern llvm """ declare noalias ptr @calloc(i64, i64) -define void @array_erase_fields(%Object %object) { +define void @array_erase(%Object %object) { entry: %data_pointer = getelementptr inbounds i64, ptr %object, i64 1 %size = load i64, ptr %object, align 8 @@ -54,7 +54,7 @@ extern def allocate[T](size: Int) at global: Array[T] = %eraser_pointer = getelementptr ptr, ptr %calloc, i64 1 %size_pointer = getelementptr ptr, ptr %calloc, i64 2 store i64 0, ptr %calloc - store ptr @array_erase_fields, ptr %eraser_pointer + store ptr @array_erase, ptr %eraser_pointer store i64 ${size}, ptr %size_pointer %ret_pos = insertvalue %Pos { i64 0, ptr poison }, ptr %calloc, 1 ret %Pos %ret_pos From 19eac5780846274c558b75f6849ddcec683dab60 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 15:13:09 +0100 Subject: [PATCH 07/57] renaming: c_ref_erase_field -> c_ref_erase --- libraries/common/ref.effekt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/common/ref.effekt b/libraries/common/ref.effekt index ccf4eb09fa..871a8d4dfb 100644 --- a/libraries/common/ref.effekt +++ b/libraries/common/ref.effekt @@ -10,7 +10,7 @@ extern js """ """ extern llvm """ -define void @c_ref_erase_field(%Object %object) { +define void @c_ref_erase(%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 @@ -44,7 +44,7 @@ extern def allocate[T]() at global: Ref[T] = %fieldData_pointer = getelementptr ptr, ptr %ref, i64 3 store i64 0, ptr %ref, align 8 - store ptr @c_ref_erase_field, ptr %refEraser, align 8 + store ptr @c_ref_erase, ptr %refEraser, align 8 store i64 0, ptr %fieldTag, align 8 store ptr null, ptr %fieldData_pointer, align 8 @@ -65,7 +65,7 @@ extern def ref[T](init: T) at global: Ref[T] = %refField = getelementptr ptr, ptr %ref, i64 2 store i64 0, ptr %ref, align 8 - store ptr @c_ref_erase_field, ptr %refEraser, align 8 + store ptr @c_ref_erase, ptr %refEraser, align 8 store %Pos ${init}, ptr %refField, align 8 %refWrap = insertvalue %Pos { i64 0, ptr poison }, ptr %ref, 1 From 30388679fa7dd326bc82dc0d6fb50007302095f4 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 15:32:04 +0100 Subject: [PATCH 08/57] created a testclass for my tests. Testing records with several sizes --- effekt/jvm/src/test/scala/effekt/LLVMTests.scala | 2 +- examples/flat/big_object.check | 1 + examples/flat/big_object.effekt | 16 ++++++++++++++++ examples/flat/small_object.check | 1 + examples/flat/small_object.effekt | 10 ++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 examples/flat/big_object.check create mode 100644 examples/flat/big_object.effekt create mode 100644 examples/flat/small_object.check create mode 100644 examples/flat/small_object.effekt diff --git a/effekt/jvm/src/test/scala/effekt/LLVMTests.scala b/effekt/jvm/src/test/scala/effekt/LLVMTests.scala index 85d9a418ad..89b9c6f342 100644 --- a/effekt/jvm/src/test/scala/effekt/LLVMTests.scala +++ b/effekt/jvm/src/test/scala/effekt/LLVMTests.scala @@ -12,7 +12,7 @@ class LLVMTests extends EffektTests { override def valgrind = sys.env.get("EFFEKT_VALGRIND").nonEmpty override def debug = sys.env.get("EFFEKT_DEBUG").nonEmpty - + override lazy val positives: Set[File] = Set( examplesDir / "llvm", examplesDir / "pos", diff --git a/examples/flat/big_object.check b/examples/flat/big_object.check new file mode 100644 index 0000000000..f11c82a4cb --- /dev/null +++ b/examples/flat/big_object.check @@ -0,0 +1 @@ +9 \ No newline at end of file diff --git a/examples/flat/big_object.effekt b/examples/flat/big_object.effekt new file mode 100644 index 0000000000..8b92b086fe --- /dev/null +++ b/examples/flat/big_object.effekt @@ -0,0 +1,16 @@ +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int + ) + +def main() = { + val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9) + println(p.field9) +} \ No newline at end of file diff --git a/examples/flat/small_object.check b/examples/flat/small_object.check new file mode 100644 index 0000000000..e440e5c842 --- /dev/null +++ b/examples/flat/small_object.check @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/examples/flat/small_object.effekt b/examples/flat/small_object.effekt new file mode 100644 index 0000000000..354cf36fc2 --- /dev/null +++ b/examples/flat/small_object.effekt @@ -0,0 +1,10 @@ +record Person( + field1: Int, + field2: Int, + field3: Int + ) + +def main() = { + val p = Person(1, 2, 3) + println(p.field3) +} \ No newline at end of file From a0d41e4ca67c0f8981f4c5d691129e7fef37d09b Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 16:13:14 +0100 Subject: [PATCH 09/57] created a small test set for asserting the saving slot size --- .../jvm/src/test/scala/effekt/FlatTests.scala | 20 ++++++++++++++++ examples/flat/big_object.check | 1 - examples/flat/one_lg_object.check | 1 + examples/flat/one_lg_object.effekt | 20 ++++++++++++++++ examples/flat/one_md_object.check | 1 + ...big_object.effekt => one_md_object.effekt} | 9 ++++---- examples/flat/one_sm_object.check | 1 + examples/flat/one_sm_object.effekt | 14 +++++++++++ examples/flat/one_xl_object.check | 1 + examples/flat/one_xl_object.effekt | 21 +++++++++++++++++ ...small_object.check => one_xs_object.check} | 0 ...all_object.effekt => one_xs_object.effekt} | 1 + examples/flat/two_lg_objects.check | 2 ++ examples/flat/two_lg_objects.effekt | 22 ++++++++++++++++++ examples/flat/two_md_objects.check | 2 ++ examples/flat/two_md_objects.effekt | 17 ++++++++++++++ examples/flat/two_sm_objects.check | 2 ++ examples/flat/two_sm_objects.effekt | 16 +++++++++++++ examples/flat/two_xl_objects.check | 2 ++ examples/flat/two_xl_objects.effekt | 23 +++++++++++++++++++ examples/flat/two_xs_objects.check | 2 ++ examples/flat/two_xs_objects.effekt | 13 +++++++++++ 22 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 effekt/jvm/src/test/scala/effekt/FlatTests.scala delete mode 100644 examples/flat/big_object.check create mode 100644 examples/flat/one_lg_object.check create mode 100644 examples/flat/one_lg_object.effekt create mode 100644 examples/flat/one_md_object.check rename examples/flat/{big_object.effekt => one_md_object.effekt} (53%) create mode 100644 examples/flat/one_sm_object.check create mode 100644 examples/flat/one_sm_object.effekt create mode 100644 examples/flat/one_xl_object.check create mode 100644 examples/flat/one_xl_object.effekt rename examples/flat/{small_object.check => one_xs_object.check} (100%) rename examples/flat/{small_object.effekt => one_xs_object.effekt} (74%) create mode 100644 examples/flat/two_lg_objects.check create mode 100644 examples/flat/two_lg_objects.effekt create mode 100644 examples/flat/two_md_objects.check create mode 100644 examples/flat/two_md_objects.effekt create mode 100644 examples/flat/two_sm_objects.check create mode 100644 examples/flat/two_sm_objects.effekt create mode 100644 examples/flat/two_xl_objects.check create mode 100644 examples/flat/two_xl_objects.effekt create mode 100644 examples/flat/two_xs_objects.check create mode 100644 examples/flat/two_xs_objects.effekt diff --git a/effekt/jvm/src/test/scala/effekt/FlatTests.scala b/effekt/jvm/src/test/scala/effekt/FlatTests.scala new file mode 100644 index 0000000000..909e825c94 --- /dev/null +++ b/effekt/jvm/src/test/scala/effekt/FlatTests.scala @@ -0,0 +1,20 @@ +package effekt + +import sbt.io.* +import sbt.io.syntax.* + +import java.io.File +import scala.language.implicitConversions +import scala.sys.process.Process + +// df: My own test class to run my requested tests automatically +class FlatTests extends EffektTests { + + def backendName = "llvm" + + override lazy val positives: Set[File] = Set() + + override lazy val withoutOptimizations: Set[File] = Set( + examplesDir / "flat", + ) +} diff --git a/examples/flat/big_object.check b/examples/flat/big_object.check deleted file mode 100644 index f11c82a4cb..0000000000 --- a/examples/flat/big_object.check +++ /dev/null @@ -1 +0,0 @@ -9 \ No newline at end of file diff --git a/examples/flat/one_lg_object.check b/examples/flat/one_lg_object.check new file mode 100644 index 0000000000..3cacc0b93c --- /dev/null +++ b/examples/flat/one_lg_object.check @@ -0,0 +1 @@ +12 \ No newline at end of file diff --git a/examples/flat/one_lg_object.effekt b/examples/flat/one_lg_object.effekt new file mode 100644 index 0000000000..42691204c7 --- /dev/null +++ b/examples/flat/one_lg_object.effekt @@ -0,0 +1,20 @@ +// lg = Person fills 2 saving slots completely +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int + ) + +def main() = { + val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + println(p.field12) +} \ No newline at end of file diff --git a/examples/flat/one_md_object.check b/examples/flat/one_md_object.check new file mode 100644 index 0000000000..c7930257df --- /dev/null +++ b/examples/flat/one_md_object.check @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/examples/flat/big_object.effekt b/examples/flat/one_md_object.effekt similarity index 53% rename from examples/flat/big_object.effekt rename to examples/flat/one_md_object.effekt index 8b92b086fe..d68c5a9608 100644 --- a/examples/flat/big_object.effekt +++ b/examples/flat/one_md_object.effekt @@ -1,3 +1,4 @@ +// md = Person requires 2 saving slots record Person( field1: Int, field2: Int, @@ -5,12 +6,10 @@ record Person( field4: Int, field5: Int, field6: Int, - field7: Int, - field8: Int, - field9: Int + field7: Int ) def main() = { - val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9) - println(p.field9) + val p = Person(1, 2, 3, 4, 5, 6, 7) + println(p.field7) } \ No newline at end of file diff --git a/examples/flat/one_sm_object.check b/examples/flat/one_sm_object.check new file mode 100644 index 0000000000..62f9457511 --- /dev/null +++ b/examples/flat/one_sm_object.check @@ -0,0 +1 @@ +6 \ No newline at end of file diff --git a/examples/flat/one_sm_object.effekt b/examples/flat/one_sm_object.effekt new file mode 100644 index 0000000000..95ffb2305d --- /dev/null +++ b/examples/flat/one_sm_object.effekt @@ -0,0 +1,14 @@ +// sm = Person fills 1 saving slot completely +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int + ) + +def main() = { + val p = Person(1, 2, 3, 4, 5, 6) + println(p.field6) +} \ No newline at end of file diff --git a/examples/flat/one_xl_object.check b/examples/flat/one_xl_object.check new file mode 100644 index 0000000000..ca7bf83ac5 --- /dev/null +++ b/examples/flat/one_xl_object.check @@ -0,0 +1 @@ +13 \ No newline at end of file diff --git a/examples/flat/one_xl_object.effekt b/examples/flat/one_xl_object.effekt new file mode 100644 index 0000000000..aff3ed9e29 --- /dev/null +++ b/examples/flat/one_xl_object.effekt @@ -0,0 +1,21 @@ +// xl = Person needs 3 saving slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int + ) + +def main() = { + val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) + println(p.field13) +} \ No newline at end of file diff --git a/examples/flat/small_object.check b/examples/flat/one_xs_object.check similarity index 100% rename from examples/flat/small_object.check rename to examples/flat/one_xs_object.check diff --git a/examples/flat/small_object.effekt b/examples/flat/one_xs_object.effekt similarity index 74% rename from examples/flat/small_object.effekt rename to examples/flat/one_xs_object.effekt index 354cf36fc2..034c32ea5a 100644 --- a/examples/flat/small_object.effekt +++ b/examples/flat/one_xs_object.effekt @@ -1,3 +1,4 @@ +// xs = Person fits easily in 1 saving slot record Person( field1: Int, field2: Int, diff --git a/examples/flat/two_lg_objects.check b/examples/flat/two_lg_objects.check new file mode 100644 index 0000000000..89917fb8fd --- /dev/null +++ b/examples/flat/two_lg_objects.check @@ -0,0 +1,2 @@ +12 +19 \ No newline at end of file diff --git a/examples/flat/two_lg_objects.effekt b/examples/flat/two_lg_objects.effekt new file mode 100644 index 0000000000..62c0e7a235 --- /dev/null +++ b/examples/flat/two_lg_objects.effekt @@ -0,0 +1,22 @@ +// lg = Person fills 2 saving slots completely +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + val p2 = Person(13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) + println(p1.field12) + println(p2.field7) +} \ No newline at end of file diff --git a/examples/flat/two_md_objects.check b/examples/flat/two_md_objects.check new file mode 100644 index 0000000000..78bd7702cc --- /dev/null +++ b/examples/flat/two_md_objects.check @@ -0,0 +1,2 @@ +7 +9 \ No newline at end of file diff --git a/examples/flat/two_md_objects.effekt b/examples/flat/two_md_objects.effekt new file mode 100644 index 0000000000..d3bdb013f6 --- /dev/null +++ b/examples/flat/two_md_objects.effekt @@ -0,0 +1,17 @@ +// md = Person requires 2 saving slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6, 7) + val p2 = Person(8, 9, 10, 11, 12, 13, 14) + println(p1.field7) + println(p2.field2) +} \ No newline at end of file diff --git a/examples/flat/two_sm_objects.check b/examples/flat/two_sm_objects.check new file mode 100644 index 0000000000..b8a0ddcef6 --- /dev/null +++ b/examples/flat/two_sm_objects.check @@ -0,0 +1,2 @@ +6 +8 \ No newline at end of file diff --git a/examples/flat/two_sm_objects.effekt b/examples/flat/two_sm_objects.effekt new file mode 100644 index 0000000000..a1d1d206ca --- /dev/null +++ b/examples/flat/two_sm_objects.effekt @@ -0,0 +1,16 @@ +// sm = Person fills 1 saving slot completely +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6) + val p2 = Person(7, 8, 9, 10, 11, 12) + println(p1.field6) + println(p2.field2) +} \ No newline at end of file diff --git a/examples/flat/two_xl_objects.check b/examples/flat/two_xl_objects.check new file mode 100644 index 0000000000..bda4b3e2f4 --- /dev/null +++ b/examples/flat/two_xl_objects.check @@ -0,0 +1,2 @@ +13 +20 \ No newline at end of file diff --git a/examples/flat/two_xl_objects.effekt b/examples/flat/two_xl_objects.effekt new file mode 100644 index 0000000000..c1a445f31f --- /dev/null +++ b/examples/flat/two_xl_objects.effekt @@ -0,0 +1,23 @@ +// xl = Person needs 3 saving slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) + val p2 = Person(14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26) + println(p1.field13) + println(p2.field7) +} \ No newline at end of file diff --git a/examples/flat/two_xs_objects.check b/examples/flat/two_xs_objects.check new file mode 100644 index 0000000000..69cc3abe58 --- /dev/null +++ b/examples/flat/two_xs_objects.check @@ -0,0 +1,2 @@ +3 +5 \ No newline at end of file diff --git a/examples/flat/two_xs_objects.effekt b/examples/flat/two_xs_objects.effekt new file mode 100644 index 0000000000..02f18b3a9c --- /dev/null +++ b/examples/flat/two_xs_objects.effekt @@ -0,0 +1,13 @@ +// xs = Person fits easily in 1 saving slot +record Person( + field1: Int, + field2: Int, + field3: Int + ) + +def main() = { + val p1 = Person(1, 2, 3) + val p2 = Person(4, 5, 6) + println(p1.field3) + println(p2.field2) +} \ No newline at end of file From 114697a75d65f0c86c0f70effecbbeb64f1a1c08 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 16:31:27 +0100 Subject: [PATCH 10/57] approach for testing that no memory is leaked by acquire -> does not run yet, since we have 10 failing tests --- .../effekt/generator/llvm/Transformer.scala | 157 ++++++++++-------- libraries/llvm/cMalloc.c | 120 ++++++++++--- libraries/llvm/rts.ll | 2 +- 3 files changed, 191 insertions(+), 88 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 17b58deeaa..57dcd316aa 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -2,11 +2,12 @@ package effekt package generator package llvm +import effekt.generator.llvm.Instruction.Call import effekt.machine import effekt.machine.Variable +import effekt.machine.analysis.* import effekt.util.intercalate import effekt.util.messages.ErrorReporter -import effekt.machine.analysis.* import scala.annotation.tailrec import scala.collection.mutable @@ -21,12 +22,15 @@ object Transformer { given MC: ModuleContext = ModuleContext(); definitions.foreach(transform); - val globals = MC.definitions; MC.definitions = null; + val globals = MC.definitions; + MC.definitions = null; val entryInstructions = List( Call("stack", Ccc(), stackType, withEmptyStack, List()), - Call("", Ccc(), VoidType(), initializeMemory, List()), - Call("_", Tailcc(false), VoidType(), transform(entry), List(LocalReference(stackType, "stack")))) + Call("", Ccc(), VoidType(), initializeMemory, List()), + Call("_", Tailcc(false), VoidType(), transform(entry), List(LocalReference(stackType, "stack"))), + Call("", Ccc(), VoidType(), testIfAllBlocksAreFreed, List()), + ) val entryBlock = BasicBlock("entry", entryInstructions, RetVoid()) val entryFunction = Function(External(), Ccc(), VoidType(), "effektMain", List(), List(entryBlock)) @@ -35,7 +39,9 @@ object Transformer { // context getters private def MC(using MC: ModuleContext): ModuleContext = MC + private def FC(using FC: FunctionContext): FunctionContext = FC + private def BC(using BC: BlockContext): BlockContext = BC def transform(declaration: machine.Declaration)(using ErrorReporter): Definition = @@ -59,26 +65,26 @@ object Transformer { """call void @hole() |unreachable |""".stripMargin - } + } def transform(template: Template[machine.Variable]): String = "; variable\n " ++ intercalate(template.strings, template.args.map { case machine.Variable(name, tpe) => PrettyPrinter.localName(name) }).mkString def transform(definition: machine.Definition)(using ModuleContext): Unit = definition match { - case machine.Definition(machine.Label(name, environment), body) => - val parameters = environment.map { case machine.Variable(name, tpe) => Parameter(transform(tpe), name) } - defineLabel(name, parameters) { - emit(Comment(s"definition $name, environment length ${environment.length}")) - eraseValues(environment, freeVariables(body)) - transform(body) - } + case machine.Definition(machine.Label(name, environment), body) => + val parameters = environment.map { case machine.Variable(name, tpe) => Parameter(transform(tpe), name) } + defineLabel(name, parameters) { + emit(Comment(s"definition $name, environment length ${environment.length}")) + eraseValues(environment, freeVariables(body)) + transform(body) + } } def transform(statement: machine.Statement)(using ModuleContext, FunctionContext, BlockContext): Terminator = statement match { - case machine.Jump(label) => + case machine.Jump(label) => emit(Comment(s"jump ${label.name}")) shareValues(label.environment, Set()) @@ -114,6 +120,7 @@ object Transformer { emit(ExtractValue(objectName, transform(value), 1)) val stack = getStack() + def labelClause(clause: machine.Clause, isDefault: Boolean): String = { implicit val BC = BlockContext() BC.stack = stack @@ -190,7 +197,7 @@ object Transformer { emit(callLabel(LocalReference(methodType, functionName), LocalReference(objectType, objectName) +: arguments)) RetVoid() - case machine.Var(ref @ machine.Variable(name, machine.Type.Reference(tpe)), init, retType, rest) => + case machine.Var(ref@machine.Variable(name, machine.Type.Reference(tpe)), init, retType, rest) => val environment = List(init) val returnAddressName = freshName("returnAddress") val returnType = transform(retType) @@ -360,7 +367,7 @@ object Transformer { eraseValues(List(v), freeVariables(rest)); transform(rest) - case machine.ForeignCall(variable @ machine.Variable(resultName, resultType), foreign, values, rest) => + case machine.ForeignCall(variable@machine.Variable(resultName, resultType), foreign, values, rest) => emit(Comment(s"foreignCall $resultName : $resultType, foreign $foreign, ${values.length} values")) shareValues(values, freeVariables(rest)); emit(Call(resultName, Ccc(), transform(resultType), ConstantGlobal(foreign), values.map(transform))); @@ -393,17 +400,17 @@ object Transformer { } transform(rest) - case machine.Statement.Hole(span) => - val posfmt = span.range.from.format - emit(Comment(s"Hole @ $posfmt")) + case machine.Statement.Hole(span) => + val posfmt = span.range.from.format + emit(Comment(s"Hole @ $posfmt")) - // Reused from LiteralUTF8String - val utf8 = (posfmt + "\u0000").getBytes("UTF-8") // null-terminated - val litName = freshName("hole_pos") - emit(GlobalConstant(s"$litName.lit", ConstantArray(IntegerType8(), utf8.map { b => ConstantInteger8(b) }.toList))) + // Reused from LiteralUTF8String + val utf8 = (posfmt + "\u0000").getBytes("UTF-8") // null-terminated + val litName = freshName("hole_pos") + emit(GlobalConstant(s"$litName.lit", ConstantArray(IntegerType8(), utf8.map { b => ConstantInteger8(b) }.toList))) - emit(Call("_", Ccc(), VoidType(), ConstantGlobal("hole"), List(ConstantGlobal(s"$litName.lit")))) - RetVoid() + emit(Call("_", Ccc(), VoidType(), ConstantGlobal("hole"), List(ConstantGlobal(s"$litName.lit")))) + RetVoid() } def transform(label: machine.Label): ConstantGlobal = @@ -433,13 +440,13 @@ object Transformer { val referenceType = NamedType("Reference"); def transform(tpe: machine.Type): Type = tpe match { - case machine.Positive() => positiveType - case machine.Negative() => negativeType - case machine.Type.Prompt() => promptType - case machine.Type.Stack() => resumptionType - case machine.Type.Int() => IntegerType64() - case machine.Type.Byte() => IntegerType8() - case machine.Type.Double() => DoubleType() + case machine.Positive() => positiveType + case machine.Negative() => negativeType + case machine.Type.Prompt() => promptType + case machine.Type.Stack() => resumptionType + case machine.Type.Int() => IntegerType64() + case machine.Type.Byte() => IntegerType8() + case machine.Type.Double() => DoubleType() case machine.Type.Reference(tpe) => referenceType } @@ -448,13 +455,13 @@ object Transformer { def typeSize(tpe: machine.Type): Int = tpe match { - case machine.Positive() => 16 - case machine.Negative() => 16 - case machine.Type.Prompt() => 8 // TODO Make fat? - case machine.Type.Stack() => 8 // TODO Make fat? - case machine.Type.Int() => 8 // TODO Make fat? - case machine.Type.Byte() => 1 - case machine.Type.Double() => 8 // TODO Make fat? + case machine.Positive() => 16 + case machine.Negative() => 16 + case machine.Type.Prompt() => 8 // TODO Make fat? + case machine.Type.Stack() => 8 // TODO Make fat? + case machine.Type.Int() => 8 // TODO Make fat? + case machine.Type.Byte() => 1 + case machine.Type.Double() => 8 // TODO Make fat? case machine.Type.Reference(_) => 16 } @@ -464,8 +471,10 @@ object Transformer { val terminator = prog; - val basicBlocks = FC.basicBlocks; FC.basicBlocks = null; - val instructions = BC.instructions; BC.instructions = null; + val basicBlocks = FC.basicBlocks; + FC.basicBlocks = null; + val instructions = BC.instructions; + BC.instructions = null; val entryBlock = BasicBlock("entry", instructions, terminator); val function = Function(Private(), Ccc(), VoidType(), name, parameters, entryBlock :: basicBlocks); @@ -479,8 +488,10 @@ object Transformer { val terminator = prog; - val basicBlocks = FC.basicBlocks; FC.basicBlocks = null; - val instructions = BC.instructions; BC.instructions = null; + val basicBlocks = FC.basicBlocks; + FC.basicBlocks = null; + val instructions = BC.instructions; + BC.instructions = null; val entryBlock = BasicBlock("entry", instructions, terminator); val function = Function(Private(), Tailcc(true), VoidType(), name, parameters :+ Parameter(stackType, "stack"), entryBlock :: basicBlocks); @@ -513,8 +524,10 @@ object Transformer { } def getEraser(environment: machine.Environment, kind: EraserKind)(using C: ModuleContext): Operand = { - val types = environment.map{ _.tpe }; - val freshEnvironment = environment.map{ + val types = environment.map { + _.tpe + }; + val freshEnvironment = environment.map { case machine.Variable(name, tpe) => machine.Variable(freshName(name), tpe) }; @@ -527,11 +540,11 @@ object Transformer { // 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("release"), List(LocalReference(objectType, "object")))); RetVoid() }; @@ -556,8 +569,10 @@ object Transformer { } def getSharer(environment: machine.Environment, kind: SharerKind)(using C: ModuleContext): Operand = { - val types = environment.map{ _.tpe }; - val freshEnvironment = environment.map{ + val types = environment.map { + _.tpe + }; + val freshEnvironment = environment.map { case machine.Variable(name, tpe) => machine.Variable(freshName(name), tpe) }; @@ -583,6 +598,17 @@ object Transformer { def produceObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { if (environment.isEmpty) { ConstantNull(objectType) + } else if (environmentSize(environment) > 48) { // if the object does not fit in a single LLVM-Block + val objectReference = LocalReference(objectType, freshName(role)); + val environmentReference = LocalReference(environmentType, freshName("environment")); + val size = ConstantInt(environmentSize(environment)); + val eraser = getEraser(environment, ObjectEraser) + + emit(Call(objectReference.name, Ccc(), objectType, newObject, List(eraser, size))); + emit(Call(environmentReference.name, Ccc(), environmentType, objectEnvironment, List(objectReference))); + shareValues(environment, freeInBody); + storeEnvironment(environmentReference, environment, Object); + objectReference } else { val objectReference = LocalReference(objectType, freshName(role)); val environmentReference = LocalReference(environmentType, freshName("environment")); @@ -678,15 +704,15 @@ object Transformer { values match { case Nil => () case value :: values => - if values.map(substitute).contains(substitute(value)) then { - shareValue(value); - loop(values) - } else if freeInBody.map(substitute).contains(substitute(value)) then { - shareValue(value); - loop(values) - } else { - loop(values) - } + if values.map(substitute).contains(substitute(value)) then { + shareValue(value); + loop(values) + } else if freeInBody.map(substitute).contains(substitute(value)) then { + shareValue(value); + loop(values) + } else { + loop(values) + } } }; loop(values) @@ -699,17 +725,17 @@ object Transformer { def shareValue(value: machine.Variable)(using FunctionContext, BlockContext): Unit = { Option(value.tpe).collect { - case machine.Positive() => Call("_", Ccc(), VoidType(), sharePositive, List(transform(value))) - case machine.Negative() => Call("_", Ccc(), VoidType(), shareNegative, List(transform(value))) - case machine.Type.Stack() => Call("_", Ccc(), VoidType(), shareResumption, List(transform(value))) + case machine.Positive() => Call("_", Ccc(), VoidType(), sharePositive, List(transform(value))) + case machine.Negative() => Call("_", Ccc(), VoidType(), shareNegative, List(transform(value))) + case machine.Type.Stack() => Call("_", Ccc(), VoidType(), shareResumption, List(transform(value))) }.map(emit) } def eraseValue(value: machine.Variable)(using FunctionContext, BlockContext): Unit = { Option(value.tpe).collect { - case machine.Positive() => Call("_", Ccc(), VoidType(), erasePositive, List(transform(value))) - case machine.Negative() => Call("_", Ccc(), VoidType(), eraseNegative, List(transform(value))) - case machine.Type.Stack() => Call("_", Ccc(), VoidType(), eraseResumption, List(transform(value))) + case machine.Positive() => Call("_", Ccc(), VoidType(), erasePositive, List(transform(value))) + case machine.Negative() => Call("_", Ccc(), VoidType(), eraseNegative, List(transform(value))) + case machine.Type.Stack() => Call("_", Ccc(), VoidType(), eraseResumption, List(transform(value))) }.map(emit) } @@ -727,6 +753,7 @@ object Transformer { } val initializeMemory = ConstantGlobal("cInitializeMemory"); + val testIfAllBlocksAreFreed = ConstantGlobal("testIfAllBlocksAreFreed"); val newObject = ConstantGlobal("newObject"); val objectEnvironment = ConstantGlobal("objectEnvironment"); @@ -784,7 +811,7 @@ object Transformer { def withBindings[R](bindings: List[(machine.Variable, machine.Variable)])(prog: () => R)(using C: FunctionContext): R = { val substitution = C.substitution; - C.substitution = substitution ++ bindings.map { case (variable -> value) => (variable -> substitution.getOrElse(value, value) ) }.toMap; + C.substitution = substitution ++ bindings.map { case (variable -> value) => (variable -> substitution.getOrElse(value, value)) }.toMap; val result = prog(); C.substitution = substitution result @@ -807,7 +834,7 @@ object Transformer { def setStack(stack: Operand)(using C: BlockContext) = C.stack = stack; - val escapeSeqs: Map[Char, String] = Map('\'' -> raw"'", '\"' -> raw"\"", '\\' -> raw"\\", '\n' -> raw"\n", '\t' -> raw"\t", '\r' -> raw"\r") + val escapeSeqs: Map[Char, String] = Map('\'' -> raw"'", '\"' -> raw"\"", '\\' -> raw" \\ ", '\n' -> raw" \ n", '\t' -> raw" \ t", '\r' -> raw" \ r") def escape(scalaString: String): String = scalaString.foldLeft(StringBuilder()) { (acc, c) => diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index a0517fa12e..349b81a685 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -8,31 +8,39 @@ * * Jeder freie Block zeigt auf den nächsten freien Block. */ -typedef struct 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 (128B) +static const bool DEBUG = false; +static Block* freeList = NULL; // Head of the Freelist +static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block +static uint8_t* endOfChunk = NULL; // End of the allocated Storage +static const int blockSize = 128; // The size of each block (128B) + +// How much storage do we allocate at the beginning of a program? =4GB +static const size_t chunkSize = (size_t)4294967296ULL; /** * Initialisiert den großen Speicherbereich (4GB). */ -void cInitializeMemory(void) { - size_t chunkSize = (size_t)4294967296ULL; // 4GB +void cInitializeMemory(void) +{ uint8_t* mem = (uint8_t*)malloc(chunkSize); - if (!mem) { + 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); + if (DEBUG) { + printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); + } } // ----------------------------- @@ -45,25 +53,33 @@ void cInitializeMemory(void) { * 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. */ -void* acquire(uint8_t size) { +void* acquire(uint8_t size) +{ // 1. Falls Freelist leer ist → neuer Block - if (freeList == NULL) { - if (nextUnusedBlock + blockSize > endOfChunk) { + 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); + if (DEBUG) { + 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); + + if (DEBUG) { + printf("[malloc] Reusing block: %p\n", (void*)block); + printf("[malloc] freeList: %p\n", (void*)freeList); + } return (void*)block; } @@ -73,18 +89,78 @@ void* acquire(uint8_t size) { * * @param ptr Zeiger auf den Block. */ -void release(void* ptr) { +void release(void* ptr) +{ if (!ptr) return; Block* block = (Block*)ptr; block->next = freeList; freeList = block; - printf("[free] Freed block: %p\n", ptr); + if (DEBUG) { + printf("[free] Freed block: %p\n", ptr); + } +} + +/** +* +*/ +void assertNumberLeakedBlocks(int expected) { + // Count how many blocks are in the freelist + size_t freeCount = 0; + for (const Block* b = freeList; b != NULL; b = b->next) { + freeCount++; + } + + // Total number of blocks that were ever allocated + const uint8_t* firstBlock = (endOfChunk - chunkSize); // the start of the chunk + const size_t totalAllocated = (nextUnusedBlock - firstBlock) / blockSize; + + // Calculate the number of leaked blocks + const size_t numberOfLeakedBlocks = totalAllocated - freeCount; + + // Report if there are any leaked blocks +// if (numberOfLeakedBlocks != expected) { +// printf("firstBlock: %p\n", (void*)firstBlock); +// printf("nextUnusedBlock: %p\n", (void*)nextUnusedBlock); +// +// printf("Test failed!, Total Allocated: %zu, Free Count: %zu \n", totalAllocated, freeCount); +// exit(1); +// } +} + +/** +* Works as a Unit-Test, if all used blocks are freed at the end of the program. +* If not, we print an error message, making test fail. +*/ +void testIfAllBlocksAreFreed() +{ + assertNumberLeakedBlocks(0); } -// @Deprecated -void* cRealloc(void* ptr, uint8_t size) { - printf("cRealloc: %p, %d\n", ptr, size); - return realloc(ptr,size); -} \ No newline at end of file +// small testprogram to test the allocator +//int main(void) +//{ +// cInitializeMemory(); +// assertNumberLeakedBlocks(0); +// +// void* a = acquire((uint8_t)1024); +// assertNumberLeakedBlocks(1); +// +// void* b = acquire((uint8_t)1024); +// assertNumberLeakedBlocks(2); +// +// release(a); +// assertNumberLeakedBlocks(1); +// +// release(b); +// assertNumberLeakedBlocks(0); +// +// void* c = acquire((uint8_t)1024); // should reuse a +// assertNumberLeakedBlocks(1); +// +// release(c); +// testIfAllBlocksAreFreed(); +// +// return 0; +//} diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 7d8525bc02..a19bbd81bd 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -97,7 +97,7 @@ declare void @cInitializeMemory() declare ptr @acquire(i64) declare void @release(ptr) -declare ptr @cRealloc(ptr, i64) +declare void @testIfAllBlocksAreFreed() declare ptr @malloc(i64) declare void @free(ptr) From 580d55cfb7b704a90156618be9de63224d5ec1e5 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 31 Oct 2025 08:13:37 +0100 Subject: [PATCH 11/57] added some more testprograms --- examples/flat/array_record.check | 1 + examples/flat/array_record.effekt | 10 ++++++++++ examples/flat/nested_objects.check | 1 + examples/flat/nested_objects.effekt | 16 ++++++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 examples/flat/array_record.check create mode 100644 examples/flat/array_record.effekt create mode 100644 examples/flat/nested_objects.check create mode 100644 examples/flat/nested_objects.effekt diff --git a/examples/flat/array_record.check b/examples/flat/array_record.check new file mode 100644 index 0000000000..56ea891855 --- /dev/null +++ b/examples/flat/array_record.check @@ -0,0 +1 @@ +Array(1, 2, 3) \ No newline at end of file diff --git a/examples/flat/array_record.effekt b/examples/flat/array_record.effekt new file mode 100644 index 0000000000..0580fbeae2 --- /dev/null +++ b/examples/flat/array_record.effekt @@ -0,0 +1,10 @@ +record Person( + field1: Int, + field2: Array[Int] + ) + +def main() = { + val myArray: Array[Int] = build(3) { i => i + 1 } // Array(1, 2, 3) + val p = Person(1, myArray) + println(myArray) +} \ No newline at end of file diff --git a/examples/flat/nested_objects.check b/examples/flat/nested_objects.check new file mode 100644 index 0000000000..e440e5c842 --- /dev/null +++ b/examples/flat/nested_objects.check @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/examples/flat/nested_objects.effekt b/examples/flat/nested_objects.effekt new file mode 100644 index 0000000000..27a274b5fc --- /dev/null +++ b/examples/flat/nested_objects.effekt @@ -0,0 +1,16 @@ +record Person1( + field3: Int, + field4: Int +) + +record Person( + field1: Int, + field2: Int, + person1: Person1 + ) + +def main() = { + val p1 = Person1(3, 4) + val p = Person(1, 2, p1) + println(p.person1.field3) +} \ No newline at end of file From 101816e78c054725ce68dfbe85872945b4f9ef79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20S=C3=BCberkr=C3=BCb?= Date: Mon, 27 Oct 2025 16:36:47 +0100 Subject: [PATCH 12/57] Fix tail resumptive analysis for vars (#1154) Fixes #1153. --- .../test/scala/effekt/ChezSchemeTests.scala | 1 + .../optimizer/RemoveTailResumptions.scala | 7 ++- examples/pos/issue1153.check | 2 + examples/pos/issue1153.effekt | 49 +++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 examples/pos/issue1153.check create mode 100644 examples/pos/issue1153.effekt diff --git a/effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala b/effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala index 4dfe21c782..db68b1f3fb 100644 --- a/effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala +++ b/effekt/jvm/src/test/scala/effekt/ChezSchemeTests.scala @@ -27,6 +27,7 @@ abstract class ChezSchemeTests extends EffektTests { examplesDir / "pos" / "object", examplesDir / "pos" / "type_omission_op.effekt", examplesDir / "pos" / "issue972.effekt", + examplesDir / "pos" / "issue1153.effekt", // filesystem operations and bytearrays are not yet supported in our Chez backend examplesDir / "benchmarks" / "input_output" / "word_count_ascii.effekt", diff --git a/effekt/shared/src/main/scala/effekt/core/optimizer/RemoveTailResumptions.scala b/effekt/shared/src/main/scala/effekt/core/optimizer/RemoveTailResumptions.scala index c29bb27950..16dde427e8 100644 --- a/effekt/shared/src/main/scala/effekt/core/optimizer/RemoveTailResumptions.scala +++ b/effekt/shared/src/main/scala/effekt/core/optimizer/RemoveTailResumptions.scala @@ -33,9 +33,12 @@ object RemoveTailResumptions { case Stmt.Match(scrutinee, clauses, default) => !freeInExpr(scrutinee) && clauses.forall { case (_, BlockLit(tparams, cparams, vparams, bparams, body)) => tailResumptive(k, body) } && default.forall { stmt => tailResumptive(k, stmt) } - case Stmt.Region(BlockLit(tparams, cparams, vparams, bparams, body)) => tailResumptive(k, body) + case Stmt.Region(BlockLit(tparams, cparams, vparams, bparams, body)) => false case Stmt.Alloc(id, init, region, body) => tailResumptive(k, body) && !freeInExpr(init) - case Stmt.Var(ref, init, capture, body) => tailResumptive(k, body) && !freeInExpr(init) + // Conceptually, a mutable variable definition can be seen as a handler for get and put operations. + // Treating this as tail-resumptive leads to a failure of semantics preservation. + // See https://github.com/effekt-lang/effekt/issues/1153 for an example. + case Stmt.Var(ref, init, capture, body) => false case Stmt.Get(ref, annotatedCapt, tpe, id, body) => tailResumptive(k, body) case Stmt.Put(ref, annotatedCapt, value, body) => tailResumptive(k, body) && !freeInExpr(value) case Stmt.Reset(BlockLit(tparams, cparams, vparams, bparams, body)) => false diff --git a/examples/pos/issue1153.check b/examples/pos/issue1153.check new file mode 100644 index 0000000000..08a81a9329 --- /dev/null +++ b/examples/pos/issue1153.check @@ -0,0 +1,2 @@ +3 +3 \ No newline at end of file diff --git a/examples/pos/issue1153.effekt b/examples/pos/issue1153.effekt new file mode 100644 index 0000000000..004ada3fa3 --- /dev/null +++ b/examples/pos/issue1153.effekt @@ -0,0 +1,49 @@ +effect flip(): Bool + +def all{prog: => Unit / {flip}} = { + try { + prog() + } with flip { + resume(true) + resume(false) + } +} + +interface State[T] { + def get(): T + def set(x: T): Unit +} + +interface allocate { + def fresh[T, R](init: T){k: {State[T]} => R}: R +} + +def newRegion[T]{prog: {r: allocate} => T}: T = { + try { + prog{r} + } with r: allocate { + def fresh[T, R](init) = { + var x = init + def s = new State[T] { + def get() = x + def set(y) = { + x = y + } + } + resume{{k} => k{s}} + } + } +} + +def main() = { + newRegion { {r} => + all { + with def x = r.fresh(0) + if (do flip()) { + x.set(3) + } else { + } + println(x.get()) + } + } +} From 2c3e29263b0f83dc3c7fc9c4cebd874d5880b147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Wed, 29 Oct 2025 15:10:48 +0100 Subject: [PATCH 13/57] Fix #1165: improve position info for UnboxInference errors (#1167) Fix #1165 by attaching position info to messages originating in UnboxInference: when we visit a tree node, we should "zoom in" on it in the error reporting. --- .../shared/src/main/scala/effekt/typer/UnboxInference.scala | 4 ++-- examples/neg/issue1165a.check | 3 +++ examples/neg/issue1165a.effekt | 6 ++++++ examples/neg/issue1165b.check | 3 +++ examples/neg/issue1165b.effekt | 6 ++++++ 5 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 examples/neg/issue1165a.check create mode 100644 examples/neg/issue1165a.effekt create mode 100644 examples/neg/issue1165b.check create mode 100644 examples/neg/issue1165b.effekt diff --git a/effekt/shared/src/main/scala/effekt/typer/UnboxInference.scala b/effekt/shared/src/main/scala/effekt/typer/UnboxInference.scala index 0fad7093b0..71a39ff23d 100644 --- a/effekt/shared/src/main/scala/effekt/typer/UnboxInference.scala +++ b/effekt/shared/src/main/scala/effekt/typer/UnboxInference.scala @@ -53,7 +53,7 @@ object UnboxInference extends Phase[NameResolved, NameResolved] { // TODO maybe we should synthesize a call to get here already? case sym: (ValueSymbol | symbols.RefBinder) => v case sym: BlockSymbol => - C.error(pp"Computation ${sym} is used in an expression position, which requires boxing (e.g. `box ${sym}`)") + C.error(pp"Computation ${sym.name.name} is used in an expression position, which requires boxing (e.g. `box ${sym.name.name}`)") v } @@ -215,7 +215,7 @@ object UnboxInference extends Phase[NameResolved, NameResolved] { /** * Copies all annotations and position information from source to target */ - def visit[T <: Tree, R <: Tree](source: T)(block: T => R)(using C: Context): R = { + def visit[T <: Tree, R <: Tree](source: T)(block: T => R)(using C: Context): R = C.at(source) { val target = block(source) target.inheritPosition(source) C.copyAnnotations(source, target) diff --git a/examples/neg/issue1165a.check b/examples/neg/issue1165a.check new file mode 100644 index 0000000000..e7504117bc --- /dev/null +++ b/examples/neg/issue1165a.check @@ -0,0 +1,3 @@ +[error] examples/neg/issue1165a.effekt:5:11: Computation toString is used in an expression position, which requires boxing (e.g. `box toString`) + println(toString) + ^^^^^^^^ \ No newline at end of file diff --git a/examples/neg/issue1165a.effekt b/examples/neg/issue1165a.effekt new file mode 100644 index 0000000000..30076acba8 --- /dev/null +++ b/examples/neg/issue1165a.effekt @@ -0,0 +1,6 @@ +module issue1165a + +def toString(): String = "" +def main() = { + println(toString) +} \ No newline at end of file diff --git a/examples/neg/issue1165b.check b/examples/neg/issue1165b.check new file mode 100644 index 0000000000..0ac9422a20 --- /dev/null +++ b/examples/neg/issue1165b.check @@ -0,0 +1,3 @@ +[error] examples/neg/issue1165b.effekt:5:3: Computation e is used in an expression position, which requires boxing (e.g. `box e`) + e + ^ \ No newline at end of file diff --git a/examples/neg/issue1165b.effekt b/examples/neg/issue1165b.effekt new file mode 100644 index 0000000000..f7cc726eb8 --- /dev/null +++ b/examples/neg/issue1165b.effekt @@ -0,0 +1,6 @@ +module issue1165b + +effect exc(): Nothing +def foo() = try { + e +} with e: exc { <> } \ No newline at end of file From 80c481d360208e437ab9692d3a2afe47d146f05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Thu, 30 Oct 2025 11:57:59 +0100 Subject: [PATCH 14/57] Add 'did you mean' help for mistaken type names (#1162) Add a system to help the users when the compiler cannot find a name of a type: #### Heuristic 1: Some types are named differently in Effekt than in other languages Screenshot 2025-10-27 at 15 06 41 This specific one (`Boolean` instead of `Bool`) is done often by students and LLMs alike. #### Heuristic 2: Try case-insensitive comparison Screenshot 2025-10-27 at 15 06 26 #### Heuristic 3: Try full on edit distance! Screenshot 2025-10-27 at 22 31 43 --- .../src/main/scala/effekt/symbols/Scope.scala | 55 +++++++++++++++++- .../main/scala/effekt/util/EditDistance.scala | 58 +++++++++++++++++++ examples/neg/boolean_wrong_name.check | 6 ++ examples/neg/boolean_wrong_name.effekt | 3 + examples/neg/case_insensitive_type_hint.check | 6 ++ .../neg/case_insensitive_type_hint.effekt | 5 ++ .../neg/case_insensitive_type_hint2.check | 6 ++ .../neg/case_insensitive_type_hint2.effekt | 5 ++ .../pos/boolean_custom_type_no_error.check | 0 .../pos/boolean_custom_type_no_error.effekt | 11 ++++ 10 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 effekt/shared/src/main/scala/effekt/util/EditDistance.scala create mode 100644 examples/neg/boolean_wrong_name.check create mode 100644 examples/neg/boolean_wrong_name.effekt create mode 100644 examples/neg/case_insensitive_type_hint.check create mode 100644 examples/neg/case_insensitive_type_hint.effekt create mode 100644 examples/neg/case_insensitive_type_hint2.check create mode 100644 examples/neg/case_insensitive_type_hint2.effekt create mode 100644 examples/pos/boolean_custom_type_no_error.check create mode 100644 examples/pos/boolean_custom_type_no_error.effekt diff --git a/effekt/shared/src/main/scala/effekt/symbols/Scope.scala b/effekt/shared/src/main/scala/effekt/symbols/Scope.scala index d5bf156453..102ceef848 100644 --- a/effekt/shared/src/main/scala/effekt/symbols/Scope.scala +++ b/effekt/shared/src/main/scala/effekt/symbols/Scope.scala @@ -208,8 +208,61 @@ object scopes { else syms.head }} + // TODO: Use this for more than just types? + // + // NOTE: We might also want to generalize to return more possible candidates than one + // (1 works fine for types, but maybe not for fields/methods/functions/...) + private def didYouMean(nameNotFound: String, candidates: => Set[String], specificHeuristic: String => Option[String] = _ => None)(using E: ErrorReporter): Unit = + // Priority 1: A specific heuristic + specificHeuristic(nameNotFound) orElse { + // Priority 2: Exact case-insensitive match + candidates + .find { name => name.toUpperCase == nameNotFound.toUpperCase } + .map { exactMatch => pp"Did you mean $exactMatch?" } + .orElse { + // Priority 3: Edit distance + val threshold = ((nameNotFound.length + 2) max 3) / 3 + + candidates + .toSeq + .flatMap { candidate => + effekt.util.editDistance(nameNotFound, candidate, threshold).map((_, candidate)) + } + .sorted + .headOption + .map { case (_, name) => pp"Did you mean $name?" } + } + } foreach { msg => E.info(msg) } + + // NOTE: Most of these should be covered by the edit distance anyway... + private def didYouMeanTypeHeuristic(name: String): Option[String] = name match { + case "Boolean" | "boolean" => Some(pp"Did you mean Bool?") + case "Str" | "str" => Some(pp"Did you mean String?") + case "Integer" | "Int32" | "Int64" | "I32" | "I64" | "i32" | "i64" => + Some(pp"Did you mean Int?") + case "UInt32" | "UInt64" | "UInt" | "U32" | "U64" | "u32" | "u64" => + Some(pp"Effekt only supports signed integers, did you mean Int?") + case "Int8" | "UInt8" | "I8" | "U8" | "i8" | "u8" => + Some(pp"Did you mean Byte (8 bits) or Char (32 bits)?") + case "Float" | "float" | "F64" | "F32" | "f64" | "f32" => + Some(pp"Effekt only supports 64bit floating numbers, did you mean Double?") + case "Maybe" | "maybe" => Some(pp"Did you mean Option?") + case "Void" | "void" => Some(pp"Did you mean Unit?") + case _ => None + } + def lookupType(id: IdRef)(using E: ErrorReporter): TypeSymbol = - lookupTypeOption(id.path, id.name) getOrElse { E.abort(pp"Could not resolve type ${id}") } + lookupTypeOption(id.path, id.name) getOrElse { + // If we got here, we could not find an exact match. + // But let's try and find a close one! + def availableTypes = all(id.path, scope) { + _.types.keys.toList + }.flatten.toSet + + didYouMean(id.name, availableTypes, didYouMeanTypeHeuristic) + + E.abort(pp"Could not resolve type $id") + } def lookupTypeOption(path: List[String], name: String)(using E: ErrorReporter): Option[TypeSymbol] = first(path, scope) { _.types.get(name) } diff --git a/effekt/shared/src/main/scala/effekt/util/EditDistance.scala b/effekt/shared/src/main/scala/effekt/util/EditDistance.scala new file mode 100644 index 0000000000..bf4b67eb0b --- /dev/null +++ b/effekt/shared/src/main/scala/effekt/util/EditDistance.scala @@ -0,0 +1,58 @@ +package effekt.util + +def editDistance(a: String, b: String, limit: Int): Option[Int] = { + val n = a.length + val m = b.length + if (n == 0) return Some(m) + if (m == 0) return Some(n) + + // Early exit if minimum distance exceeds limit + val minDist = Math.abs(n - m) + if (minDist > limit) return None + + // Strip common prefix + val prefixLen = a.view.zip(b.view).takeWhile { case (a, b) => a == b }.size + + // Strip common suffix (but don't overlap with prefix) + val suffixLen = a.view.drop(prefixLen).reverse + .zip(b.view.drop(prefixLen).reverse) + .takeWhile { case (a, b) => a == b } + .size + + val s1 = a.substring(prefixLen, n - suffixLen) + val s2 = b.substring(prefixLen, m - suffixLen) + + // After stripping, if one is empty, distance is just the other's length + if (s1.isEmpty) return Some(s2.length) + if (s2.isEmpty) return Some(s1.length) + + val distance = levenshtein(s1, s2) + if (distance <= limit) Some(distance) else None +} + +inline def levenshtein(s1: String, s2: String): Int = { + val n = s1.length + val m = s2.length + + val d = Array.ofDim[Int](n + 1, m + 1) + for (i <- 0 to n) { d(i)(0) = i } + for (j <- 0 to m) { d(0)(j) = j } + + for { + i <- 1 to n; s1_i = s1(i - 1) + j <- 1 to m; s2_j = s2(j - 1) + } do { + val costDelete = d(i - 1)(j) + 1 + val costInsert = d(i)(j - 1) + 1 + val costSubstitute = d(i - 1)(j - 1) + (if (s1_i == s2_j) 0 else 1) + + d(i)(j) = costDelete min costInsert min costSubstitute + + // Transposition: swap adjacent characters (Bolo ~> Bool) + if (i > 1 && j > 1 && s1(i - 1) == s2(j - 2) && s1(i - 2) == s2(j - 1)) { + d(i)(j) = d(i)(j) min (d(i - 2)(j - 2) + 1) + } + } + + d(n)(m) +} \ No newline at end of file diff --git a/examples/neg/boolean_wrong_name.check b/examples/neg/boolean_wrong_name.check new file mode 100644 index 0000000000..e390228834 --- /dev/null +++ b/examples/neg/boolean_wrong_name.check @@ -0,0 +1,6 @@ +[info] examples/neg/boolean_wrong_name.effekt:3:19: Did you mean Bool? +def alwaysTrue(): Boolean = true + ^^^^^^^ +[error] examples/neg/boolean_wrong_name.effekt:3:19: Could not resolve type Boolean +def alwaysTrue(): Boolean = true + ^^^^^^^ \ No newline at end of file diff --git a/examples/neg/boolean_wrong_name.effekt b/examples/neg/boolean_wrong_name.effekt new file mode 100644 index 0000000000..64aec2a8b2 --- /dev/null +++ b/examples/neg/boolean_wrong_name.effekt @@ -0,0 +1,3 @@ +module boolean_wrong_name + +def alwaysTrue(): Boolean = true \ No newline at end of file diff --git a/examples/neg/case_insensitive_type_hint.check b/examples/neg/case_insensitive_type_hint.check new file mode 100644 index 0000000000..7ea319d5c0 --- /dev/null +++ b/examples/neg/case_insensitive_type_hint.check @@ -0,0 +1,6 @@ +[info] examples/neg/case_insensitive_type_hint.effekt:5:12: Did you mean MyType? +def fun(): Mytype = MyType() + ^^^^^^ +[error] examples/neg/case_insensitive_type_hint.effekt:5:12: Could not resolve type Mytype +def fun(): Mytype = MyType() + ^^^^^^ diff --git a/examples/neg/case_insensitive_type_hint.effekt b/examples/neg/case_insensitive_type_hint.effekt new file mode 100644 index 0000000000..1c52cadd9e --- /dev/null +++ b/examples/neg/case_insensitive_type_hint.effekt @@ -0,0 +1,5 @@ +module case_insensitive_type_hint + +record MyType() + +def fun(): Mytype = MyType() \ No newline at end of file diff --git a/examples/neg/case_insensitive_type_hint2.check b/examples/neg/case_insensitive_type_hint2.check new file mode 100644 index 0000000000..a4eb0a5dac --- /dev/null +++ b/examples/neg/case_insensitive_type_hint2.check @@ -0,0 +1,6 @@ +[info] examples/neg/case_insensitive_type_hint2.effekt:5:12: Did you mean Mytype? +def fun(): MyType = Mytype() + ^^^^^^ +[error] examples/neg/case_insensitive_type_hint2.effekt:5:12: Could not resolve type MyType +def fun(): MyType = Mytype() + ^^^^^^ diff --git a/examples/neg/case_insensitive_type_hint2.effekt b/examples/neg/case_insensitive_type_hint2.effekt new file mode 100644 index 0000000000..d5d4beab06 --- /dev/null +++ b/examples/neg/case_insensitive_type_hint2.effekt @@ -0,0 +1,5 @@ +module case_insensitive_type_hint2 + +record Mytype() + +def fun(): MyType = Mytype() \ No newline at end of file diff --git a/examples/pos/boolean_custom_type_no_error.check b/examples/pos/boolean_custom_type_no_error.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/pos/boolean_custom_type_no_error.effekt b/examples/pos/boolean_custom_type_no_error.effekt new file mode 100644 index 0000000000..9e5fcd9356 --- /dev/null +++ b/examples/pos/boolean_custom_type_no_error.effekt @@ -0,0 +1,11 @@ +module boolean_custom_type_no_error + +// see neg/boolean_wrong_name +// Here we define our own 'Boolean' type, +// so that the error "please try 'Bool' instead" never pops up + +record Boolean(b: Bool) + +def alwaysTrue(): Boolean = Boolean(true) + +def main() = () \ No newline at end of file From 6e585df1aacacb34edf0584b6be6596929b13b23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Bene=C5=A1?= Date: Thu, 30 Oct 2025 19:36:59 +0100 Subject: [PATCH 15/57] Multiple CI jobs for PRs (#1173) --- .../actions/restore-build-cache/action.yml | 22 +++ .github/workflows/ci-master.yml | 39 ++++++ .github/workflows/ci-pr.yml | 132 ++++++++++++++++++ .github/workflows/ci.yml | 48 ------- build.sbt | 54 +++++++ 5 files changed, 247 insertions(+), 48 deletions(-) create mode 100644 .github/actions/restore-build-cache/action.yml create mode 100644 .github/workflows/ci-master.yml create mode 100644 .github/workflows/ci-pr.yml delete mode 100644 .github/workflows/ci.yml diff --git a/.github/actions/restore-build-cache/action.yml b/.github/actions/restore-build-cache/action.yml new file mode 100644 index 0000000000..1ccca0570b --- /dev/null +++ b/.github/actions/restore-build-cache/action.yml @@ -0,0 +1,22 @@ +name: 'Restore Build Cache' +description: 'Restores cached build artifacts' + +runs: + using: "composite" + steps: + - name: Restore cached build + uses: actions/cache/restore@v4 + with: + path: | + target + project/target + project/project/target + kiama/jvm/target + kiama/js/target + effekt/jvm/target + effekt/js/target + ~/.ivy2/cache + ~/.sbt + ~/.cache/coursier + key: effekt-build-${{ github.sha }} + fail-on-cache-miss: true diff --git a/.github/workflows/ci-master.yml b/.github/workflows/ci-master.yml new file mode 100644 index 0000000000..89b8abfe4a --- /dev/null +++ b/.github/workflows/ci-master.yml @@ -0,0 +1,39 @@ +name: Master CI + +on: + push: + branches: + - master + +jobs: + full-test-suite: + name: Full Test Suite + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + submodules: 'true' + + - uses: ./.github/actions/setup-effekt + with: + install-dependencies: 'true' + install-valgrind: 'true' + + - uses: ./.github/actions/run-effekt-tests + with: + full-test: 'true' + use-retry: 'true' + + windows-smoke-test: + name: Windows Smoke Test + runs-on: windows-latest + steps: + - uses: actions/checkout@v5 + with: + submodules: 'true' + + - uses: ./.github/actions/setup-effekt + + - uses: ./.github/actions/run-effekt-tests + with: + full-test: 'false' diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml new file mode 100644 index 0000000000..8b99f0a2f7 --- /dev/null +++ b/.github/workflows/ci-pr.yml @@ -0,0 +1,132 @@ +name: CI + +on: + pull_request: + +jobs: + build-and-compile: + name: "Build & Compile" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + submodules: 'true' + + - uses: ./.github/actions/setup-effekt + + - name: Compile project + run: sbt compile Test/compile + + - name: Cache compiled artifacts + uses: actions/cache/save@v4 + with: + path: | + target + project/target + project/project/target + kiama/jvm/target + kiama/js/target + effekt/jvm/target + effekt/js/target + ~/.ivy2/cache + ~/.sbt + ~/.cache/coursier + key: effekt-build-${{ github.sha }} + + internal-tests: + name: "Internal Tests & Verify Binary" + needs: build-and-compile + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + submodules: 'true' + + - uses: ./.github/actions/setup-effekt + + - uses: ./.github/actions/restore-build-cache + + - name: Run internal tests + run: | + sbt kiamaJVM/test + sbt effektJVM/testRemaining + sbt effektJS/test + + - name: Assemble fully optimized JS file + run: sbt effektJS/fullOptJS + + - name: Install effekt binary + run: sbt install + + - name: Test effekt binary + run: effekt.sh --help + + windows-tests: + name: "Windows Smoke Test" + needs: build-and-compile + runs-on: windows-latest + steps: + - uses: actions/checkout@v5 + with: + submodules: 'true' + + - uses: ./.github/actions/setup-effekt + + - name: Compile project + run: sbt Test/compile + + - name: Run Windows smoke test + run: sbt "effektJVM/testOnly effekt.JavaScriptTests -- --tests=.*examples[\\/]*pos[\\/]*sideeffects.*" + + js-tests: + name: "JS Backend" + needs: build-and-compile + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + submodules: 'true' + + - uses: ./.github/actions/setup-effekt + + - uses: ./.github/actions/restore-build-cache + + - name: Run JavaScript backend tests + run: sbt effektJVM/testBackendJS + + chez-tests: + name: "Chez Backend" + needs: build-and-compile + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + submodules: 'true' + + - uses: ./.github/actions/setup-effekt + with: + install-dependencies: 'true' + + - uses: ./.github/actions/restore-build-cache + + - name: Run Chez Scheme backend tests + run: sbt effektJVM/testBackendChez + + llvm-tests: + name: "LLVM Backend" + needs: build-and-compile + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + with: + submodules: 'true' + + - uses: ./.github/actions/setup-effekt + with: + install-dependencies: 'true' + install-valgrind: 'true' + + - uses: ./.github/actions/restore-build-cache + + - name: Run LLVM backend tests + run: EFFEKT_VALGRIND=1 EFFEKT_DEBUG=1 sbt effektJVM/testBackendLLVM diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 8ff8591470..0000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: CI - -on: - push: - branches: - - master - pull_request: - -jobs: - run-hello-world: - strategy: - matrix: - os: [windows-latest] - - name: Build Effekt compiler and run one test - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v5 - with: - submodules: 'true' - - - uses: ./.github/actions/setup-effekt - - - uses: ./.github/actions/run-effekt-tests - with: - full-test: 'false' - - build-jar: - strategy: - matrix: - os: [ubuntu-latest] - - name: Build Effekt compiler and run tests - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v5 - with: - submodules: 'true' - - - uses: ./.github/actions/setup-effekt - with: - install-dependencies: 'true' - install-valgrind: 'true' - - - uses: ./.github/actions/run-effekt-tests - with: - full-test: 'true' - use-retry: 'false' diff --git a/build.sbt b/build.sbt index 26637111eb..535a5a30c4 100644 --- a/build.sbt +++ b/build.sbt @@ -13,6 +13,11 @@ lazy val assembleJS = taskKey[Unit]("Assemble the JS file in out/effekt.js") lazy val assembleBinary = taskKey[Unit]("Assembles the effekt binary in bin/effekt") lazy val generateDocumentation = taskKey[Unit]("Generates some documentation.") lazy val bumpMinorVersion = taskKey[Unit]("Bumps the minor version number (used in CI).") +// custom test tasks for CI +lazy val testBackendJS = taskKey[Unit]("Run JavaScript backend tests") +lazy val testBackendChez = taskKey[Unit]("Run Chez Scheme backend tests") +lazy val testBackendLLVM = taskKey[Unit]("Run LLVM backend tests") +lazy val testRemaining = taskKey[Unit]("Run all non-backend tests (internal tests) on effektJVM") lazy val noPublishSettings = Seq( publish := {}, @@ -206,6 +211,55 @@ lazy val effekt: CrossProject = crossProject(JSPlatform, JVMPlatform).in(file("e println(newVersion) }, + testBackendJS := { + (Test / testOnly).toTask( + " effekt.JavaScriptTests effekt.StdlibJavaScriptTests" + ).value + }, + + testBackendChez := { + (Test / testOnly).toTask( + " effekt.ChezSchemeMonadicTests effekt.ChezSchemeCallCCTests" + + " effekt.StdlibChezSchemeMonadicTests effekt.StdlibChezSchemeCallCCTests" + ).value + }, + + testBackendLLVM := { + (Test / testOnly).toTask( + " effekt.LLVMTests effekt.LLVMNoValgrindTests effekt.StdlibLLVMTests" + ).value + }, + + testRemaining := Def.taskDyn { + val log = streams.value.log + + val allTests = (Test / definedTestNames).value.toSet + + // Explicit list of backend tests (union of all testBackend targets) + val backendTests = Set( + "effekt.JavaScriptTests", + "effekt.StdlibJavaScriptTests", + "effekt.ChezSchemeMonadicTests", + "effekt.ChezSchemeCallCCTests", + "effekt.StdlibChezSchemeMonadicTests", + "effekt.StdlibChezSchemeCallCCTests", + "effekt.LLVMTests", + "effekt.LLVMNoValgrindTests", + "effekt.StdlibLLVMTests" + ) + + val remaining = allTests -- backendTests + + if (remaining.isEmpty) { + log.info("No remaining tests") + Def.task { () } + } else { + log.info(s"Running ${remaining.size} internal test(s):") + remaining.toSeq.sorted.foreach(t => log.info(s" - $t")) + (Test / testOnly).toTask(s" ${remaining.mkString(" ")}") + } + }.value, + generateDocumentation := TreeDocs.replacer.value, Compile / sourceGenerators += versionGenerator.taskValue, From addac58075a2130920fb86ed1380a8c70bd76a43 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 31 Oct 2025 11:46:48 +0100 Subject: [PATCH 16/57] refactored cMalloc.c --- libraries/llvm/cMalloc.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 349b81a685..c42296186b 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -67,7 +67,7 @@ void* acquire(uint8_t size) void* block = nextUnusedBlock; nextUnusedBlock += blockSize; if (DEBUG) { - printf("[malloc] New block: %p\n", block); + printf("[acquire] New block: %p\n", block); } return block; } @@ -77,8 +77,7 @@ void* acquire(uint8_t size) freeList = block->next; if (DEBUG) { - printf("[malloc] Reusing block: %p\n", (void*)block); - printf("[malloc] freeList: %p\n", (void*)freeList); + printf("[acquire] Reusing block: %p\n", (void*)block); } return (void*)block; } @@ -98,7 +97,7 @@ void release(void* ptr) freeList = block; if (DEBUG) { - printf("[free] Freed block: %p\n", ptr); + printf("[release] Freed block: %p\n", ptr); } } @@ -107,9 +106,9 @@ void release(void* ptr) */ void assertNumberLeakedBlocks(int expected) { // Count how many blocks are in the freelist - size_t freeCount = 0; + size_t numberOfElementsInFreeList = 0; for (const Block* b = freeList; b != NULL; b = b->next) { - freeCount++; + numberOfElementsInFreeList++; } // Total number of blocks that were ever allocated @@ -117,16 +116,16 @@ void assertNumberLeakedBlocks(int expected) { const size_t totalAllocated = (nextUnusedBlock - firstBlock) / blockSize; // Calculate the number of leaked blocks - const size_t numberOfLeakedBlocks = totalAllocated - freeCount; + const size_t numberOfLeakedBlocks = totalAllocated - numberOfElementsInFreeList; // Report if there are any leaked blocks -// if (numberOfLeakedBlocks != expected) { -// printf("firstBlock: %p\n", (void*)firstBlock); -// printf("nextUnusedBlock: %p\n", (void*)nextUnusedBlock); -// -// printf("Test failed!, Total Allocated: %zu, Free Count: %zu \n", totalAllocated, freeCount); -// exit(1); -// } + if (numberOfLeakedBlocks != expected) { + printf("firstBlock: %p\n", (void*)firstBlock); + printf("nextUnusedBlock: %p\n", (void*)nextUnusedBlock); + + printf("Test failed!, Total Allocated: %zu, Elements in Free List: %zu \n", totalAllocated, numberOfElementsInFreeList); + exit(1); + } } /** @@ -135,7 +134,7 @@ void assertNumberLeakedBlocks(int expected) { */ void testIfAllBlocksAreFreed() { - assertNumberLeakedBlocks(0); +// assertNumberLeakedBlocks(0); } // small testprogram to test the allocator From d2b29aa857026fcdc8f05b0d4b8c8cd67d161611 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 31 Oct 2025 13:44:45 +0100 Subject: [PATCH 17/57] Big objects are now splitted into a multiple objects with links to the next slot. Slot size are now 64 bytes --- .../effekt/generator/llvm/Transformer.scala | 180 +++++++++++++++--- libraries/llvm/cMalloc.c | 2 +- 2 files changed, 154 insertions(+), 28 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 57dcd316aa..d9ffbec7fd 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -2,6 +2,7 @@ package effekt package generator package llvm +import effekt.generator.llvm.{Type => LLVMType} import effekt.generator.llvm.Instruction.Call import effekt.machine import effekt.machine.Variable @@ -595,31 +596,16 @@ object Transformer { }) } + /** + * Produces an Object. It is always 64 bytes long + */ def produceObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { if (environment.isEmpty) { ConstantNull(objectType) - } else if (environmentSize(environment) > 48) { // if the object does not fit in a single LLVM-Block - val objectReference = LocalReference(objectType, freshName(role)); - val environmentReference = LocalReference(environmentType, freshName("environment")); - val size = ConstantInt(environmentSize(environment)); - val eraser = getEraser(environment, ObjectEraser) - - emit(Call(objectReference.name, Ccc(), objectType, newObject, List(eraser, size))); - emit(Call(environmentReference.name, Ccc(), environmentType, objectEnvironment, List(objectReference))); - shareValues(environment, freeInBody); - storeEnvironment(environmentReference, environment, Object); - objectReference + } else if (fitsInOneSavingSlot(environment)) { + produceSingleObject(role, environment, freeInBody) } else { - val objectReference = LocalReference(objectType, freshName(role)); - val environmentReference = LocalReference(environmentType, freshName("environment")); - val size = ConstantInt(environmentSize(environment)); - val eraser = getEraser(environment, ObjectEraser) - - emit(Call(objectReference.name, Ccc(), objectType, newObject, List(eraser, size))); - emit(Call(environmentReference.name, Ccc(), environmentType, objectEnvironment, List(objectReference))); - shareValues(environment, freeInBody); - storeEnvironment(environmentReference, environment, Object); - objectReference + produceShardedObject(role, environment, freeInBody) } } @@ -689,12 +675,58 @@ object Transformer { } def loadEnvironmentAt(pointer: Operand, environment: machine.Environment, alias: AliasInfo)(using ModuleContext, FunctionContext, BlockContext): Unit = { - val `type` = environmentType(environment) - environment.zipWithIndex.foreach { - case (machine.Variable(name, tpe), i) => - val field = LocalReference(PointerType(), freshName(name + "_pointer")); - emit(GetElementPtr(field.name, `type`, pointer, List(0, i))); - emit(Load(name, transform(tpe), field, alias)) + alias match { + // if we have a sharded object + case Object if !fitsInOneSavingSlot(environment) => + // Follow chained 64-byte blocks created in produceObject + val chunkedEnvironments = splitEnvironment(environment) + var currentPtr: Operand = pointer + + chunkedEnvironments.zipWithIndex.foreach { case (chunk, idx) => + val isLast = idx == chunkedEnvironments.length - 1 + if (isLast) { + val tpe = environmentType(chunk) + chunk.zipWithIndex.foreach { + case (machine.Variable(name, tpe0), i) => + val field = LocalReference(PointerType(), freshName(name + "_pointer")) + emit(GetElementPtr(field.name, tpe, currentPtr, List(0, i))) + emit(Load(name, transform(tpe0), field, alias)) + } + } else { + // For non-last chunkedEnvironments, layout is chunk ++ [Pos link] + val tpe = StructureType(chunk.map { case machine.Variable(_, t) => transform(t) } :+ positiveType) + + // Load actual chunk fields + chunk.zipWithIndex.foreach { + case (machine.Variable(name, tpe0), i) => + val field = LocalReference(PointerType(), freshName(name + "_pointer")) + emit(GetElementPtr(field.name, tpe, currentPtr, List(0, i))) + emit(Load(name, transform(tpe0), field, alias)) + } + + // Follow link to next block + val linkPtr = LocalReference(PointerType(), freshName("link_pointer")) + emit(GetElementPtr(linkPtr.name, tpe, currentPtr, List(0, chunk.length))) + + val linkVal = LocalReference(positiveType, freshName("link")) + emit(Load(linkVal.name, positiveType, linkPtr, alias)) + + val nextObj = LocalReference(objectType, freshName("next_object")) + emit(ExtractValue(nextObj.name, linkVal, 1)) + + val nextEnv = LocalReference(environmentType, freshName("environment")) + emit(Call(nextEnv.name, Ccc(), environmentType, objectEnvironment, List(nextObj))) + currentPtr = nextEnv + } + } + case _: effekt.generator.llvm.AliasInfo => + val `type` = environmentType(environment) + environment.zipWithIndex.foreach { + case (machine.Variable(name, tpe), i) => + val field = LocalReference(PointerType(), freshName(name + "_pointer")); + emit(GetElementPtr(field.name, `type`, pointer, List(0, i))); + emit(Load(name, transform(tpe), field, alias)) + } } } @@ -843,4 +875,98 @@ object Transformer { case None => acc += c } }.toString() + + /** + * if we can fit our object in one single saving block -> Easiest case + * Else we have to split our environment into multiple saving slots and reference in a linked list fashion + */ + private def fitsInOneSavingSlot(environment: machine.Environment): Boolean = { + environmentSize(environment) > 0 && environmentSize(environment) <= 48 + } + + /** + * Splits the environment into multiple environment such that you can store one object in multiple saving slots. + * Is required to do fixed-sized-allocation. + */ + private def splitEnvironment(environment: machine.Environment): List[machine.Environment] = { + val slotSize = 64 // we want to use 64 bytes for each saving slot + var headerSize = 16 // we use 16 byte for the last saving block only, the others are 32 bytes + var currentEnvironment = List[machine.Variable]() + var result = List[machine.Environment]() + var isLast = true + for (variable <- environment.reverse) { + val variableSize = typeSize(variable.tpe) + + if (headerSize + (environmentSize(currentEnvironment) + variableSize) <= slotSize) { + currentEnvironment = currentEnvironment :+ variable + } + else { + result = result :+ currentEnvironment.reverse + currentEnvironment = List(variable) + isLast = false + headerSize = 32 // normal header size + size of the positive type which is the reference to the next object + } + } + result = result :+ currentEnvironment.reverse + + result.reverse + } + + /** + * When you have a big object to produce, you shard it into multiple slots instead of one single big slot. + * The end of each slot references to the next one. + * Each slot is 64 byte long + */ + private def produceShardedObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { + // Split into multiple 64-byte blocks and chain them via a boxed link (%Pos with tag 0 pointing to next %Object) + val shardedEnvironments = splitEnvironment(environment) + + // Allocate the last block first. + val lastEnvironment = shardedEnvironments.last + val lastObjectReference = LocalReference(objectType, freshName(role)) + val lastEnvPtr = LocalReference(environmentType, freshName("environment")) + val lastSize = ConstantInt(environmentSize(lastEnvironment)) + val lastEraser = getEraser(lastEnvironment, ObjectEraser) + + emit(Call(lastObjectReference.name, Ccc(), objectType, newObject, List(lastEraser, lastSize))) + emit(Call(lastEnvPtr.name, Ccc(), environmentType, objectEnvironment, List(lastObjectReference))) + shareValues(lastEnvironment, freeInBody) + storeEnvironment(lastEnvPtr, lastEnvironment, Object) + + // Chain preceding blocks, each storing a boxed link to the next object + var headObject: LocalReference = lastObjectReference + + // loop through all shardedEnvironments except the last one in reversed order + shardedEnvironments.init.reverse.foreach { envChunk => + val temporaryName = freshName("link_temporary") + val linkValName = freshName("link") + + // we create a new Positive Object with tag 0... + emit(InsertValue(temporaryName, ConstantAggregateZero(positiveType), ConstantInt(0), 0)) // we create a fake Link Tag 0 that we just use for store a tag in the Pos + emit(InsertValue(linkValName, LocalReference(positiveType, temporaryName), headObject, 1)) + + // ... and inject it into our environment... + val extendedEnv = envChunk :+ machine.Variable(linkValName, machine.Positive()) + + // ...finally, we do the usual behavior when we produce an object + headObject = produceSingleObject(role, extendedEnv, freeInBody).asInstanceOf[LocalReference] + } + headObject + } + + /** + * Creates a new object and stores its environment in it + */ + private def produceSingleObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { + val objectReference = LocalReference(objectType, freshName(role)) + val environmentReference = LocalReference(environmentType, freshName("environment")) + val size = ConstantInt(environmentSize(environment)); + val eraser = getEraser(environment, ObjectEraser) + + emit(Call(objectReference.name, Ccc(), objectType, newObject, List(eraser, size))); + emit(Call(environmentReference.name, Ccc(), environmentType, objectEnvironment, List(objectReference))); + shareValues(environment, freeInBody); + storeEnvironment(environmentReference, environment, Object); + objectReference + } } diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index c42296186b..834bd54e8a 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -19,7 +19,7 @@ static const bool DEBUG = false; static Block* freeList = NULL; // Head of the Freelist static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage -static const int blockSize = 128; // The size of each block (128B) +static const int blockSize = 64; // The size of each block (128B) // How much storage do we allocate at the beginning of a program? =4GB static const size_t chunkSize = (size_t)4294967296ULL; From 4d121f5d77588fe23f123a8b38ae912220e4b020 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 31 Oct 2025 13:44:45 +0100 Subject: [PATCH 18/57] Big objects are now splitted into a multiple objects with links to the next slot. Slot size are now 64 bytes --- .../effekt/generator/llvm/Transformer.scala | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index d9ffbec7fd..93adf3a0c9 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -2,10 +2,10 @@ package effekt package generator package llvm -import effekt.generator.llvm.{Type => LLVMType} +import effekt.generator.llvm.Type as LLVMType import effekt.generator.llvm.Instruction.Call import effekt.machine -import effekt.machine.Variable +import effekt.machine.{Environment, Variable} import effekt.machine.analysis.* import effekt.util.intercalate import effekt.util.messages.ErrorReporter @@ -674,39 +674,30 @@ object Transformer { } } - def loadEnvironmentAt(pointer: Operand, environment: machine.Environment, alias: AliasInfo)(using ModuleContext, FunctionContext, BlockContext): Unit = { + def loadEnvironmentAt(elementPointer: Operand, environment: machine.Environment, alias: AliasInfo)(using ModuleContext, FunctionContext, BlockContext): Unit = { alias match { // if we have a sharded object case Object if !fitsInOneSavingSlot(environment) => // Follow chained 64-byte blocks created in produceObject val chunkedEnvironments = splitEnvironment(environment) - var currentPtr: Operand = pointer + var currentEnvironmentPtr: Operand = elementPointer + // here we loop over all environments *in* order chunkedEnvironments.zipWithIndex.foreach { case (chunk, idx) => val isLast = idx == chunkedEnvironments.length - 1 if (isLast) { val tpe = environmentType(chunk) - chunk.zipWithIndex.foreach { - case (machine.Variable(name, tpe0), i) => - val field = LocalReference(PointerType(), freshName(name + "_pointer")) - emit(GetElementPtr(field.name, tpe, currentPtr, List(0, i))) - emit(Load(name, transform(tpe0), field, alias)) - } + loadAllVariables(currentEnvironmentPtr, chunk, alias, tpe) } else { + // Here we do the same as for the normal slot plus some extra stuff + // For non-last chunkedEnvironments, layout is chunk ++ [Pos link] - val tpe = StructureType(chunk.map { case machine.Variable(_, t) => transform(t) } :+ positiveType) - - // Load actual chunk fields - chunk.zipWithIndex.foreach { - case (machine.Variable(name, tpe0), i) => - val field = LocalReference(PointerType(), freshName(name + "_pointer")) - emit(GetElementPtr(field.name, tpe, currentPtr, List(0, i))) - emit(Load(name, transform(tpe0), field, alias)) - } - - // Follow link to next block + val tpe = StructureType(chunk.map { case machine.Variable(_, t) => transform(t) } :+ positiveType) // the same as for the normal case + positive Type for the link + loadAllVariables(currentEnvironmentPtr, chunk, alias, tpe) + + // Follow link to next block. The Link pointer is the last element of the chunk val linkPtr = LocalReference(PointerType(), freshName("link_pointer")) - emit(GetElementPtr(linkPtr.name, tpe, currentPtr, List(0, chunk.length))) + emit(GetElementPtr(linkPtr.name, tpe, currentEnvironmentPtr, List(0, chunk.length))) val linkVal = LocalReference(positiveType, freshName("link")) emit(Load(linkVal.name, positiveType, linkPtr, alias)) @@ -716,17 +707,12 @@ object Transformer { val nextEnv = LocalReference(environmentType, freshName("environment")) emit(Call(nextEnv.name, Ccc(), environmentType, objectEnvironment, List(nextObj))) - currentPtr = nextEnv + currentEnvironmentPtr = nextEnv } } - case _: effekt.generator.llvm.AliasInfo => - val `type` = environmentType(environment) - environment.zipWithIndex.foreach { - case (machine.Variable(name, tpe), i) => - val field = LocalReference(PointerType(), freshName(name + "_pointer")); - emit(GetElementPtr(field.name, `type`, pointer, List(0, i))); - emit(Load(name, transform(tpe), field, alias)) - } + case _: AliasInfo => + val tpe = environmentType(environment) + loadAllVariables(elementPointer, environment, alias, tpe) } } @@ -969,4 +955,13 @@ object Transformer { storeEnvironment(environmentReference, environment, Object); objectReference } + + private def loadAllVariables(pointer: Operand, environment: machine.Environment, alias: AliasInfo, typ: Type)(using ModuleContext, FunctionContext, BlockContext): Unit = { + environment.zipWithIndex.foreach { + case (machine.Variable(name, tpe), i) => + val field = LocalReference(PointerType(), freshName(name + "_pointer")) + emit(GetElementPtr(field.name, typ, pointer, List(0, i))) + emit(Load(name, transform(tpe), field, alias)) + } + } } From db8fd0a242f1577420a618fc3ad81652537dcf21 Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 6 Nov 2025 08:24:05 +0100 Subject: [PATCH 19/57] two more test programs two_xxl_objects.effekt and two_xxxl_objects.effekt --- examples/flat/two_xxl_objects.check | 2 + examples/flat/two_xxl_objects.effekt | 312 +++ examples/flat/two_xxxl_objects.check | 2 + examples/flat/two_xxxl_objects.effekt | 3012 +++++++++++++++++++++++++ 4 files changed, 3328 insertions(+) create mode 100644 examples/flat/two_xxl_objects.check create mode 100644 examples/flat/two_xxl_objects.effekt create mode 100644 examples/flat/two_xxxl_objects.check create mode 100644 examples/flat/two_xxxl_objects.effekt diff --git a/examples/flat/two_xxl_objects.check b/examples/flat/two_xxl_objects.check new file mode 100644 index 0000000000..2a817d1436 --- /dev/null +++ b/examples/flat/two_xxl_objects.check @@ -0,0 +1,2 @@ +100 +151 \ No newline at end of file diff --git a/examples/flat/two_xxl_objects.effekt b/examples/flat/two_xxl_objects.effekt new file mode 100644 index 0000000000..7258d179af --- /dev/null +++ b/examples/flat/two_xxl_objects.effekt @@ -0,0 +1,312 @@ +// xxl: Person has 100 fields -> need x slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int, + field14: Int, + field15: Int, + field16: Int, + field17: Int, + field18: Int, + field19: Int, + field20: Int, + field21: Int, + field22: Int, + field23: Int, + field24: Int, + field25: Int, + field26: Int, + field27: Int, + field28: Int, + field29: Int, + field30: Int, + field31: Int, + field32: Int, + field33: Int, + field34: Int, + field35: Int, + field36: Int, + field37: Int, + field38: Int, + field39: Int, + field40: Int, + field41: Int, + field42: Int, + field43: Int, + field44: Int, + field45: Int, + field46: Int, + field47: Int, + field48: Int, + field49: Int, + field50: Int, + field51: Int, + field52: Int, + field53: Int, + field54: Int, + field55: Int, + field56: Int, + field57: Int, + field58: Int, + field59: Int, + field60: Int, + field61: Int, + field62: Int, + field63: Int, + field64: Int, + field65: Int, + field66: Int, + field67: Int, + field68: Int, + field69: Int, + field70: Int, + field71: Int, + field72: Int, + field73: Int, + field74: Int, + field75: Int, + field76: Int, + field77: Int, + field78: Int, + field79: Int, + field80: Int, + field81: Int, + field82: Int, + field83: Int, + field84: Int, + field85: Int, + field86: Int, + field87: Int, + field88: Int, + field89: Int, + field90: Int, + field91: Int, + field92: Int, + field93: Int, + field94: Int, + field95: Int, + field96: Int, + field97: Int, + field98: Int, + field99: Int, + field100: Int +) + +def main() = { + val p1 = Person( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100 +) + val p2 = Person( + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200 +) + println(p1.field100) + println(p2.field51) +} \ No newline at end of file diff --git a/examples/flat/two_xxxl_objects.check b/examples/flat/two_xxxl_objects.check new file mode 100644 index 0000000000..e3cf26f115 --- /dev/null +++ b/examples/flat/two_xxxl_objects.check @@ -0,0 +1,2 @@ +1000 +1999 \ No newline at end of file diff --git a/examples/flat/two_xxxl_objects.effekt b/examples/flat/two_xxxl_objects.effekt new file mode 100644 index 0000000000..5d4002bf11 --- /dev/null +++ b/examples/flat/two_xxxl_objects.effekt @@ -0,0 +1,3012 @@ +// xxxl: Person has 1000 fields -> need x slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int, + field14: Int, + field15: Int, + field16: Int, + field17: Int, + field18: Int, + field19: Int, + field20: Int, + field21: Int, + field22: Int, + field23: Int, + field24: Int, + field25: Int, + field26: Int, + field27: Int, + field28: Int, + field29: Int, + field30: Int, + field31: Int, + field32: Int, + field33: Int, + field34: Int, + field35: Int, + field36: Int, + field37: Int, + field38: Int, + field39: Int, + field40: Int, + field41: Int, + field42: Int, + field43: Int, + field44: Int, + field45: Int, + field46: Int, + field47: Int, + field48: Int, + field49: Int, + field50: Int, + field51: Int, + field52: Int, + field53: Int, + field54: Int, + field55: Int, + field56: Int, + field57: Int, + field58: Int, + field59: Int, + field60: Int, + field61: Int, + field62: Int, + field63: Int, + field64: Int, + field65: Int, + field66: Int, + field67: Int, + field68: Int, + field69: Int, + field70: Int, + field71: Int, + field72: Int, + field73: Int, + field74: Int, + field75: Int, + field76: Int, + field77: Int, + field78: Int, + field79: Int, + field80: Int, + field81: Int, + field82: Int, + field83: Int, + field84: Int, + field85: Int, + field86: Int, + field87: Int, + field88: Int, + field89: Int, + field90: Int, + field91: Int, + field92: Int, + field93: Int, + field94: Int, + field95: Int, + field96: Int, + field97: Int, + field98: Int, + field99: Int, + field100: Int, + field101: Int, + field102: Int, + field103: Int, + field104: Int, + field105: Int, + field106: Int, + field107: Int, + field108: Int, + field109: Int, + field110: Int, + field111: Int, + field112: Int, + field113: Int, + field114: Int, + field115: Int, + field116: Int, + field117: Int, + field118: Int, + field119: Int, + field120: Int, + field121: Int, + field122: Int, + field123: Int, + field124: Int, + field125: Int, + field126: Int, + field127: Int, + field128: Int, + field129: Int, + field130: Int, + field131: Int, + field132: Int, + field133: Int, + field134: Int, + field135: Int, + field136: Int, + field137: Int, + field138: Int, + field139: Int, + field140: Int, + field141: Int, + field142: Int, + field143: Int, + field144: Int, + field145: Int, + field146: Int, + field147: Int, + field148: Int, + field149: Int, + field150: Int, + field151: Int, + field152: Int, + field153: Int, + field154: Int, + field155: Int, + field156: Int, + field157: Int, + field158: Int, + field159: Int, + field160: Int, + field161: Int, + field162: Int, + field163: Int, + field164: Int, + field165: Int, + field166: Int, + field167: Int, + field168: Int, + field169: Int, + field170: Int, + field171: Int, + field172: Int, + field173: Int, + field174: Int, + field175: Int, + field176: Int, + field177: Int, + field178: Int, + field179: Int, + field180: Int, + field181: Int, + field182: Int, + field183: Int, + field184: Int, + field185: Int, + field186: Int, + field187: Int, + field188: Int, + field189: Int, + field190: Int, + field191: Int, + field192: Int, + field193: Int, + field194: Int, + field195: Int, + field196: Int, + field197: Int, + field198: Int, + field199: Int, + field200: Int, + field201: Int, + field202: Int, + field203: Int, + field204: Int, + field205: Int, + field206: Int, + field207: Int, + field208: Int, + field209: Int, + field210: Int, + field211: Int, + field212: Int, + field213: Int, + field214: Int, + field215: Int, + field216: Int, + field217: Int, + field218: Int, + field219: Int, + field220: Int, + field221: Int, + field222: Int, + field223: Int, + field224: Int, + field225: Int, + field226: Int, + field227: Int, + field228: Int, + field229: Int, + field230: Int, + field231: Int, + field232: Int, + field233: Int, + field234: Int, + field235: Int, + field236: Int, + field237: Int, + field238: Int, + field239: Int, + field240: Int, + field241: Int, + field242: Int, + field243: Int, + field244: Int, + field245: Int, + field246: Int, + field247: Int, + field248: Int, + field249: Int, + field250: Int, + field251: Int, + field252: Int, + field253: Int, + field254: Int, + field255: Int, + field256: Int, + field257: Int, + field258: Int, + field259: Int, + field260: Int, + field261: Int, + field262: Int, + field263: Int, + field264: Int, + field265: Int, + field266: Int, + field267: Int, + field268: Int, + field269: Int, + field270: Int, + field271: Int, + field272: Int, + field273: Int, + field274: Int, + field275: Int, + field276: Int, + field277: Int, + field278: Int, + field279: Int, + field280: Int, + field281: Int, + field282: Int, + field283: Int, + field284: Int, + field285: Int, + field286: Int, + field287: Int, + field288: Int, + field289: Int, + field290: Int, + field291: Int, + field292: Int, + field293: Int, + field294: Int, + field295: Int, + field296: Int, + field297: Int, + field298: Int, + field299: Int, + field300: Int, + field301: Int, + field302: Int, + field303: Int, + field304: Int, + field305: Int, + field306: Int, + field307: Int, + field308: Int, + field309: Int, + field310: Int, + field311: Int, + field312: Int, + field313: Int, + field314: Int, + field315: Int, + field316: Int, + field317: Int, + field318: Int, + field319: Int, + field320: Int, + field321: Int, + field322: Int, + field323: Int, + field324: Int, + field325: Int, + field326: Int, + field327: Int, + field328: Int, + field329: Int, + field330: Int, + field331: Int, + field332: Int, + field333: Int, + field334: Int, + field335: Int, + field336: Int, + field337: Int, + field338: Int, + field339: Int, + field340: Int, + field341: Int, + field342: Int, + field343: Int, + field344: Int, + field345: Int, + field346: Int, + field347: Int, + field348: Int, + field349: Int, + field350: Int, + field351: Int, + field352: Int, + field353: Int, + field354: Int, + field355: Int, + field356: Int, + field357: Int, + field358: Int, + field359: Int, + field360: Int, + field361: Int, + field362: Int, + field363: Int, + field364: Int, + field365: Int, + field366: Int, + field367: Int, + field368: Int, + field369: Int, + field370: Int, + field371: Int, + field372: Int, + field373: Int, + field374: Int, + field375: Int, + field376: Int, + field377: Int, + field378: Int, + field379: Int, + field380: Int, + field381: Int, + field382: Int, + field383: Int, + field384: Int, + field385: Int, + field386: Int, + field387: Int, + field388: Int, + field389: Int, + field390: Int, + field391: Int, + field392: Int, + field393: Int, + field394: Int, + field395: Int, + field396: Int, + field397: Int, + field398: Int, + field399: Int, + field400: Int, + field401: Int, + field402: Int, + field403: Int, + field404: Int, + field405: Int, + field406: Int, + field407: Int, + field408: Int, + field409: Int, + field410: Int, + field411: Int, + field412: Int, + field413: Int, + field414: Int, + field415: Int, + field416: Int, + field417: Int, + field418: Int, + field419: Int, + field420: Int, + field421: Int, + field422: Int, + field423: Int, + field424: Int, + field425: Int, + field426: Int, + field427: Int, + field428: Int, + field429: Int, + field430: Int, + field431: Int, + field432: Int, + field433: Int, + field434: Int, + field435: Int, + field436: Int, + field437: Int, + field438: Int, + field439: Int, + field440: Int, + field441: Int, + field442: Int, + field443: Int, + field444: Int, + field445: Int, + field446: Int, + field447: Int, + field448: Int, + field449: Int, + field450: Int, + field451: Int, + field452: Int, + field453: Int, + field454: Int, + field455: Int, + field456: Int, + field457: Int, + field458: Int, + field459: Int, + field460: Int, + field461: Int, + field462: Int, + field463: Int, + field464: Int, + field465: Int, + field466: Int, + field467: Int, + field468: Int, + field469: Int, + field470: Int, + field471: Int, + field472: Int, + field473: Int, + field474: Int, + field475: Int, + field476: Int, + field477: Int, + field478: Int, + field479: Int, + field480: Int, + field481: Int, + field482: Int, + field483: Int, + field484: Int, + field485: Int, + field486: Int, + field487: Int, + field488: Int, + field489: Int, + field490: Int, + field491: Int, + field492: Int, + field493: Int, + field494: Int, + field495: Int, + field496: Int, + field497: Int, + field498: Int, + field499: Int, + field500: Int, + field501: Int, + field502: Int, + field503: Int, + field504: Int, + field505: Int, + field506: Int, + field507: Int, + field508: Int, + field509: Int, + field510: Int, + field511: Int, + field512: Int, + field513: Int, + field514: Int, + field515: Int, + field516: Int, + field517: Int, + field518: Int, + field519: Int, + field520: Int, + field521: Int, + field522: Int, + field523: Int, + field524: Int, + field525: Int, + field526: Int, + field527: Int, + field528: Int, + field529: Int, + field530: Int, + field531: Int, + field532: Int, + field533: Int, + field534: Int, + field535: Int, + field536: Int, + field537: Int, + field538: Int, + field539: Int, + field540: Int, + field541: Int, + field542: Int, + field543: Int, + field544: Int, + field545: Int, + field546: Int, + field547: Int, + field548: Int, + field549: Int, + field550: Int, + field551: Int, + field552: Int, + field553: Int, + field554: Int, + field555: Int, + field556: Int, + field557: Int, + field558: Int, + field559: Int, + field560: Int, + field561: Int, + field562: Int, + field563: Int, + field564: Int, + field565: Int, + field566: Int, + field567: Int, + field568: Int, + field569: Int, + field570: Int, + field571: Int, + field572: Int, + field573: Int, + field574: Int, + field575: Int, + field576: Int, + field577: Int, + field578: Int, + field579: Int, + field580: Int, + field581: Int, + field582: Int, + field583: Int, + field584: Int, + field585: Int, + field586: Int, + field587: Int, + field588: Int, + field589: Int, + field590: Int, + field591: Int, + field592: Int, + field593: Int, + field594: Int, + field595: Int, + field596: Int, + field597: Int, + field598: Int, + field599: Int, + field600: Int, + field601: Int, + field602: Int, + field603: Int, + field604: Int, + field605: Int, + field606: Int, + field607: Int, + field608: Int, + field609: Int, + field610: Int, + field611: Int, + field612: Int, + field613: Int, + field614: Int, + field615: Int, + field616: Int, + field617: Int, + field618: Int, + field619: Int, + field620: Int, + field621: Int, + field622: Int, + field623: Int, + field624: Int, + field625: Int, + field626: Int, + field627: Int, + field628: Int, + field629: Int, + field630: Int, + field631: Int, + field632: Int, + field633: Int, + field634: Int, + field635: Int, + field636: Int, + field637: Int, + field638: Int, + field639: Int, + field640: Int, + field641: Int, + field642: Int, + field643: Int, + field644: Int, + field645: Int, + field646: Int, + field647: Int, + field648: Int, + field649: Int, + field650: Int, + field651: Int, + field652: Int, + field653: Int, + field654: Int, + field655: Int, + field656: Int, + field657: Int, + field658: Int, + field659: Int, + field660: Int, + field661: Int, + field662: Int, + field663: Int, + field664: Int, + field665: Int, + field666: Int, + field667: Int, + field668: Int, + field669: Int, + field670: Int, + field671: Int, + field672: Int, + field673: Int, + field674: Int, + field675: Int, + field676: Int, + field677: Int, + field678: Int, + field679: Int, + field680: Int, + field681: Int, + field682: Int, + field683: Int, + field684: Int, + field685: Int, + field686: Int, + field687: Int, + field688: Int, + field689: Int, + field690: Int, + field691: Int, + field692: Int, + field693: Int, + field694: Int, + field695: Int, + field696: Int, + field697: Int, + field698: Int, + field699: Int, + field700: Int, + field701: Int, + field702: Int, + field703: Int, + field704: Int, + field705: Int, + field706: Int, + field707: Int, + field708: Int, + field709: Int, + field710: Int, + field711: Int, + field712: Int, + field713: Int, + field714: Int, + field715: Int, + field716: Int, + field717: Int, + field718: Int, + field719: Int, + field720: Int, + field721: Int, + field722: Int, + field723: Int, + field724: Int, + field725: Int, + field726: Int, + field727: Int, + field728: Int, + field729: Int, + field730: Int, + field731: Int, + field732: Int, + field733: Int, + field734: Int, + field735: Int, + field736: Int, + field737: Int, + field738: Int, + field739: Int, + field740: Int, + field741: Int, + field742: Int, + field743: Int, + field744: Int, + field745: Int, + field746: Int, + field747: Int, + field748: Int, + field749: Int, + field750: Int, + field751: Int, + field752: Int, + field753: Int, + field754: Int, + field755: Int, + field756: Int, + field757: Int, + field758: Int, + field759: Int, + field760: Int, + field761: Int, + field762: Int, + field763: Int, + field764: Int, + field765: Int, + field766: Int, + field767: Int, + field768: Int, + field769: Int, + field770: Int, + field771: Int, + field772: Int, + field773: Int, + field774: Int, + field775: Int, + field776: Int, + field777: Int, + field778: Int, + field779: Int, + field780: Int, + field781: Int, + field782: Int, + field783: Int, + field784: Int, + field785: Int, + field786: Int, + field787: Int, + field788: Int, + field789: Int, + field790: Int, + field791: Int, + field792: Int, + field793: Int, + field794: Int, + field795: Int, + field796: Int, + field797: Int, + field798: Int, + field799: Int, + field800: Int, + field801: Int, + field802: Int, + field803: Int, + field804: Int, + field805: Int, + field806: Int, + field807: Int, + field808: Int, + field809: Int, + field810: Int, + field811: Int, + field812: Int, + field813: Int, + field814: Int, + field815: Int, + field816: Int, + field817: Int, + field818: Int, + field819: Int, + field820: Int, + field821: Int, + field822: Int, + field823: Int, + field824: Int, + field825: Int, + field826: Int, + field827: Int, + field828: Int, + field829: Int, + field830: Int, + field831: Int, + field832: Int, + field833: Int, + field834: Int, + field835: Int, + field836: Int, + field837: Int, + field838: Int, + field839: Int, + field840: Int, + field841: Int, + field842: Int, + field843: Int, + field844: Int, + field845: Int, + field846: Int, + field847: Int, + field848: Int, + field849: Int, + field850: Int, + field851: Int, + field852: Int, + field853: Int, + field854: Int, + field855: Int, + field856: Int, + field857: Int, + field858: Int, + field859: Int, + field860: Int, + field861: Int, + field862: Int, + field863: Int, + field864: Int, + field865: Int, + field866: Int, + field867: Int, + field868: Int, + field869: Int, + field870: Int, + field871: Int, + field872: Int, + field873: Int, + field874: Int, + field875: Int, + field876: Int, + field877: Int, + field878: Int, + field879: Int, + field880: Int, + field881: Int, + field882: Int, + field883: Int, + field884: Int, + field885: Int, + field886: Int, + field887: Int, + field888: Int, + field889: Int, + field890: Int, + field891: Int, + field892: Int, + field893: Int, + field894: Int, + field895: Int, + field896: Int, + field897: Int, + field898: Int, + field899: Int, + field900: Int, + field901: Int, + field902: Int, + field903: Int, + field904: Int, + field905: Int, + field906: Int, + field907: Int, + field908: Int, + field909: Int, + field910: Int, + field911: Int, + field912: Int, + field913: Int, + field914: Int, + field915: Int, + field916: Int, + field917: Int, + field918: Int, + field919: Int, + field920: Int, + field921: Int, + field922: Int, + field923: Int, + field924: Int, + field925: Int, + field926: Int, + field927: Int, + field928: Int, + field929: Int, + field930: Int, + field931: Int, + field932: Int, + field933: Int, + field934: Int, + field935: Int, + field936: Int, + field937: Int, + field938: Int, + field939: Int, + field940: Int, + field941: Int, + field942: Int, + field943: Int, + field944: Int, + field945: Int, + field946: Int, + field947: Int, + field948: Int, + field949: Int, + field950: Int, + field951: Int, + field952: Int, + field953: Int, + field954: Int, + field955: Int, + field956: Int, + field957: Int, + field958: Int, + field959: Int, + field960: Int, + field961: Int, + field962: Int, + field963: Int, + field964: Int, + field965: Int, + field966: Int, + field967: Int, + field968: Int, + field969: Int, + field970: Int, + field971: Int, + field972: Int, + field973: Int, + field974: Int, + field975: Int, + field976: Int, + field977: Int, + field978: Int, + field979: Int, + field980: Int, + field981: Int, + field982: Int, + field983: Int, + field984: Int, + field985: Int, + field986: Int, + field987: Int, + field988: Int, + field989: Int, + field990: Int, + field991: Int, + field992: Int, + field993: Int, + field994: Int, + field995: Int, + field996: Int, + field997: Int, + field998: Int, + field999: Int, + field1000: Int +) + +def main() = { + val p1 = Person( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 534, + 535, + 536, + 537, + 538, + 539, + 540, + 541, + 542, + 543, + 544, + 545, + 546, + 547, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 556, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 574, + 575, + 576, + 577, + 578, + 579, + 580, + 581, + 582, + 583, + 584, + 585, + 586, + 587, + 588, + 589, + 590, + 591, + 592, + 593, + 594, + 595, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 659, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 675, + 676, + 677, + 678, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 694, + 695, + 696, + 697, + 698, + 699, + 700, + 701, + 702, + 703, + 704, + 705, + 706, + 707, + 708, + 709, + 710, + 711, + 712, + 713, + 714, + 715, + 716, + 717, + 718, + 719, + 720, + 721, + 722, + 723, + 724, + 725, + 726, + 727, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 755, + 756, + 757, + 758, + 759, + 760, + 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, + 773, + 774, + 775, + 776, + 777, + 778, + 779, + 780, + 781, + 782, + 783, + 784, + 785, + 786, + 787, + 788, + 789, + 790, + 791, + 792, + 793, + 794, + 795, + 796, + 797, + 798, + 799, + 800, + 801, + 802, + 803, + 804, + 805, + 806, + 807, + 808, + 809, + 810, + 811, + 812, + 813, + 814, + 815, + 816, + 817, + 818, + 819, + 820, + 821, + 822, + 823, + 824, + 825, + 826, + 827, + 828, + 829, + 830, + 831, + 832, + 833, + 834, + 835, + 836, + 837, + 838, + 839, + 840, + 841, + 842, + 843, + 844, + 845, + 846, + 847, + 848, + 849, + 850, + 851, + 852, + 853, + 854, + 855, + 856, + 857, + 858, + 859, + 860, + 861, + 862, + 863, + 864, + 865, + 866, + 867, + 868, + 869, + 870, + 871, + 872, + 873, + 874, + 875, + 876, + 877, + 878, + 879, + 880, + 881, + 882, + 883, + 884, + 885, + 886, + 887, + 888, + 889, + 890, + 891, + 892, + 893, + 894, + 895, + 896, + 897, + 898, + 899, + 900, + 901, + 902, + 903, + 904, + 905, + 906, + 907, + 908, + 909, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 918, + 919, + 920, + 921, + 922, + 923, + 924, + 925, + 926, + 927, + 928, + 929, + 930, + 931, + 932, + 933, + 934, + 935, + 936, + 937, + 938, + 939, + 940, + 941, + 942, + 943, + 944, + 945, + 946, + 947, + 948, + 949, + 950, + 951, + 952, + 953, + 954, + 955, + 956, + 957, + 958, + 959, + 960, + 961, + 962, + 963, + 964, + 965, + 966, + 967, + 968, + 969, + 970, + 971, + 972, + 973, + 974, + 975, + 976, + 977, + 978, + 979, + 980, + 981, + 982, + 983, + 984, + 985, + 986, + 987, + 988, + 989, + 990, + 991, + 992, + 993, + 994, + 995, + 996, + 997, + 998, + 999, + 1000 +) + val p2 = Person( + 1001, + 1002, + 1003, + 1004, + 1005, + 1006, + 1007, + 1008, + 1009, + 1010, + 1011, + 1012, + 1013, + 1014, + 1015, + 1016, + 1017, + 1018, + 1019, + 1020, + 1021, + 1022, + 1023, + 1024, + 1025, + 1026, + 1027, + 1028, + 1029, + 1030, + 1031, + 1032, + 1033, + 1034, + 1035, + 1036, + 1037, + 1038, + 1039, + 1040, + 1041, + 1042, + 1043, + 1044, + 1045, + 1046, + 1047, + 1048, + 1049, + 1050, + 1051, + 1052, + 1053, + 1054, + 1055, + 1056, + 1057, + 1058, + 1059, + 1060, + 1061, + 1062, + 1063, + 1064, + 1065, + 1066, + 1067, + 1068, + 1069, + 1070, + 1071, + 1072, + 1073, + 1074, + 1075, + 1076, + 1077, + 1078, + 1079, + 1080, + 1081, + 1082, + 1083, + 1084, + 1085, + 1086, + 1087, + 1088, + 1089, + 1090, + 1091, + 1092, + 1093, + 1094, + 1095, + 1096, + 1097, + 1098, + 1099, + 1100, + 1101, + 1102, + 1103, + 1104, + 1105, + 1106, + 1107, + 1108, + 1109, + 1110, + 1111, + 1112, + 1113, + 1114, + 1115, + 1116, + 1117, + 1118, + 1119, + 1120, + 1121, + 1122, + 1123, + 1124, + 1125, + 1126, + 1127, + 1128, + 1129, + 1130, + 1131, + 1132, + 1133, + 1134, + 1135, + 1136, + 1137, + 1138, + 1139, + 1140, + 1141, + 1142, + 1143, + 1144, + 1145, + 1146, + 1147, + 1148, + 1149, + 1150, + 1151, + 1152, + 1153, + 1154, + 1155, + 1156, + 1157, + 1158, + 1159, + 1160, + 1161, + 1162, + 1163, + 1164, + 1165, + 1166, + 1167, + 1168, + 1169, + 1170, + 1171, + 1172, + 1173, + 1174, + 1175, + 1176, + 1177, + 1178, + 1179, + 1180, + 1181, + 1182, + 1183, + 1184, + 1185, + 1186, + 1187, + 1188, + 1189, + 1190, + 1191, + 1192, + 1193, + 1194, + 1195, + 1196, + 1197, + 1198, + 1199, + 1200, + 1201, + 1202, + 1203, + 1204, + 1205, + 1206, + 1207, + 1208, + 1209, + 1210, + 1211, + 1212, + 1213, + 1214, + 1215, + 1216, + 1217, + 1218, + 1219, + 1220, + 1221, + 1222, + 1223, + 1224, + 1225, + 1226, + 1227, + 1228, + 1229, + 1230, + 1231, + 1232, + 1233, + 1234, + 1235, + 1236, + 1237, + 1238, + 1239, + 1240, + 1241, + 1242, + 1243, + 1244, + 1245, + 1246, + 1247, + 1248, + 1249, + 1250, + 1251, + 1252, + 1253, + 1254, + 1255, + 1256, + 1257, + 1258, + 1259, + 1260, + 1261, + 1262, + 1263, + 1264, + 1265, + 1266, + 1267, + 1268, + 1269, + 1270, + 1271, + 1272, + 1273, + 1274, + 1275, + 1276, + 1277, + 1278, + 1279, + 1280, + 1281, + 1282, + 1283, + 1284, + 1285, + 1286, + 1287, + 1288, + 1289, + 1290, + 1291, + 1292, + 1293, + 1294, + 1295, + 1296, + 1297, + 1298, + 1299, + 1300, + 1301, + 1302, + 1303, + 1304, + 1305, + 1306, + 1307, + 1308, + 1309, + 1310, + 1311, + 1312, + 1313, + 1314, + 1315, + 1316, + 1317, + 1318, + 1319, + 1320, + 1321, + 1322, + 1323, + 1324, + 1325, + 1326, + 1327, + 1328, + 1329, + 1330, + 1331, + 1332, + 1333, + 1334, + 1335, + 1336, + 1337, + 1338, + 1339, + 1340, + 1341, + 1342, + 1343, + 1344, + 1345, + 1346, + 1347, + 1348, + 1349, + 1350, + 1351, + 1352, + 1353, + 1354, + 1355, + 1356, + 1357, + 1358, + 1359, + 1360, + 1361, + 1362, + 1363, + 1364, + 1365, + 1366, + 1367, + 1368, + 1369, + 1370, + 1371, + 1372, + 1373, + 1374, + 1375, + 1376, + 1377, + 1378, + 1379, + 1380, + 1381, + 1382, + 1383, + 1384, + 1385, + 1386, + 1387, + 1388, + 1389, + 1390, + 1391, + 1392, + 1393, + 1394, + 1395, + 1396, + 1397, + 1398, + 1399, + 1400, + 1401, + 1402, + 1403, + 1404, + 1405, + 1406, + 1407, + 1408, + 1409, + 1410, + 1411, + 1412, + 1413, + 1414, + 1415, + 1416, + 1417, + 1418, + 1419, + 1420, + 1421, + 1422, + 1423, + 1424, + 1425, + 1426, + 1427, + 1428, + 1429, + 1430, + 1431, + 1432, + 1433, + 1434, + 1435, + 1436, + 1437, + 1438, + 1439, + 1440, + 1441, + 1442, + 1443, + 1444, + 1445, + 1446, + 1447, + 1448, + 1449, + 1450, + 1451, + 1452, + 1453, + 1454, + 1455, + 1456, + 1457, + 1458, + 1459, + 1460, + 1461, + 1462, + 1463, + 1464, + 1465, + 1466, + 1467, + 1468, + 1469, + 1470, + 1471, + 1472, + 1473, + 1474, + 1475, + 1476, + 1477, + 1478, + 1479, + 1480, + 1481, + 1482, + 1483, + 1484, + 1485, + 1486, + 1487, + 1488, + 1489, + 1490, + 1491, + 1492, + 1493, + 1494, + 1495, + 1496, + 1497, + 1498, + 1499, + 1500, + 1501, + 1502, + 1503, + 1504, + 1505, + 1506, + 1507, + 1508, + 1509, + 1510, + 1511, + 1512, + 1513, + 1514, + 1515, + 1516, + 1517, + 1518, + 1519, + 1520, + 1521, + 1522, + 1523, + 1524, + 1525, + 1526, + 1527, + 1528, + 1529, + 1530, + 1531, + 1532, + 1533, + 1534, + 1535, + 1536, + 1537, + 1538, + 1539, + 1540, + 1541, + 1542, + 1543, + 1544, + 1545, + 1546, + 1547, + 1548, + 1549, + 1550, + 1551, + 1552, + 1553, + 1554, + 1555, + 1556, + 1557, + 1558, + 1559, + 1560, + 1561, + 1562, + 1563, + 1564, + 1565, + 1566, + 1567, + 1568, + 1569, + 1570, + 1571, + 1572, + 1573, + 1574, + 1575, + 1576, + 1577, + 1578, + 1579, + 1580, + 1581, + 1582, + 1583, + 1584, + 1585, + 1586, + 1587, + 1588, + 1589, + 1590, + 1591, + 1592, + 1593, + 1594, + 1595, + 1596, + 1597, + 1598, + 1599, + 1600, + 1601, + 1602, + 1603, + 1604, + 1605, + 1606, + 1607, + 1608, + 1609, + 1610, + 1611, + 1612, + 1613, + 1614, + 1615, + 1616, + 1617, + 1618, + 1619, + 1620, + 1621, + 1622, + 1623, + 1624, + 1625, + 1626, + 1627, + 1628, + 1629, + 1630, + 1631, + 1632, + 1633, + 1634, + 1635, + 1636, + 1637, + 1638, + 1639, + 1640, + 1641, + 1642, + 1643, + 1644, + 1645, + 1646, + 1647, + 1648, + 1649, + 1650, + 1651, + 1652, + 1653, + 1654, + 1655, + 1656, + 1657, + 1658, + 1659, + 1660, + 1661, + 1662, + 1663, + 1664, + 1665, + 1666, + 1667, + 1668, + 1669, + 1670, + 1671, + 1672, + 1673, + 1674, + 1675, + 1676, + 1677, + 1678, + 1679, + 1680, + 1681, + 1682, + 1683, + 1684, + 1685, + 1686, + 1687, + 1688, + 1689, + 1690, + 1691, + 1692, + 1693, + 1694, + 1695, + 1696, + 1697, + 1698, + 1699, + 1700, + 1701, + 1702, + 1703, + 1704, + 1705, + 1706, + 1707, + 1708, + 1709, + 1710, + 1711, + 1712, + 1713, + 1714, + 1715, + 1716, + 1717, + 1718, + 1719, + 1720, + 1721, + 1722, + 1723, + 1724, + 1725, + 1726, + 1727, + 1728, + 1729, + 1730, + 1731, + 1732, + 1733, + 1734, + 1735, + 1736, + 1737, + 1738, + 1739, + 1740, + 1741, + 1742, + 1743, + 1744, + 1745, + 1746, + 1747, + 1748, + 1749, + 1750, + 1751, + 1752, + 1753, + 1754, + 1755, + 1756, + 1757, + 1758, + 1759, + 1760, + 1761, + 1762, + 1763, + 1764, + 1765, + 1766, + 1767, + 1768, + 1769, + 1770, + 1771, + 1772, + 1773, + 1774, + 1775, + 1776, + 1777, + 1778, + 1779, + 1780, + 1781, + 1782, + 1783, + 1784, + 1785, + 1786, + 1787, + 1788, + 1789, + 1790, + 1791, + 1792, + 1793, + 1794, + 1795, + 1796, + 1797, + 1798, + 1799, + 1800, + 1801, + 1802, + 1803, + 1804, + 1805, + 1806, + 1807, + 1808, + 1809, + 1810, + 1811, + 1812, + 1813, + 1814, + 1815, + 1816, + 1817, + 1818, + 1819, + 1820, + 1821, + 1822, + 1823, + 1824, + 1825, + 1826, + 1827, + 1828, + 1829, + 1830, + 1831, + 1832, + 1833, + 1834, + 1835, + 1836, + 1837, + 1838, + 1839, + 1840, + 1841, + 1842, + 1843, + 1844, + 1845, + 1846, + 1847, + 1848, + 1849, + 1850, + 1851, + 1852, + 1853, + 1854, + 1855, + 1856, + 1857, + 1858, + 1859, + 1860, + 1861, + 1862, + 1863, + 1864, + 1865, + 1866, + 1867, + 1868, + 1869, + 1870, + 1871, + 1872, + 1873, + 1874, + 1875, + 1876, + 1877, + 1878, + 1879, + 1880, + 1881, + 1882, + 1883, + 1884, + 1885, + 1886, + 1887, + 1888, + 1889, + 1890, + 1891, + 1892, + 1893, + 1894, + 1895, + 1896, + 1897, + 1898, + 1899, + 1900, + 1901, + 1902, + 1903, + 1904, + 1905, + 1906, + 1907, + 1908, + 1909, + 1910, + 1911, + 1912, + 1913, + 1914, + 1915, + 1916, + 1917, + 1918, + 1919, + 1920, + 1921, + 1922, + 1923, + 1924, + 1925, + 1926, + 1927, + 1928, + 1929, + 1930, + 1931, + 1932, + 1933, + 1934, + 1935, + 1936, + 1937, + 1938, + 1939, + 1940, + 1941, + 1942, + 1943, + 1944, + 1945, + 1946, + 1947, + 1948, + 1949, + 1950, + 1951, + 1952, + 1953, + 1954, + 1955, + 1956, + 1957, + 1958, + 1959, + 1960, + 1961, + 1962, + 1963, + 1964, + 1965, + 1966, + 1967, + 1968, + 1969, + 1970, + 1971, + 1972, + 1973, + 1974, + 1975, + 1976, + 1977, + 1978, + 1979, + 1980, + 1981, + 1982, + 1983, + 1984, + 1985, + 1986, + 1987, + 1988, + 1989, + 1990, + 1991, + 1992, + 1993, + 1994, + 1995, + 1996, + 1997, + 1998, + 1999, + 2000 +) + println(p1.field1000) + println(p2.field999) +} \ No newline at end of file From 00f5b0378a9f8b681ffdaf16ad22357bdbe413b5 Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 6 Nov 2025 08:24:37 +0100 Subject: [PATCH 20/57] short review with Philipp on 05.11.2025 of splitted allocation approach --- .../src/main/scala/effekt/generator/llvm/Transformer.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 93adf3a0c9..47522a40d9 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -935,7 +935,7 @@ object Transformer { val extendedEnv = envChunk :+ machine.Variable(linkValName, machine.Positive()) // ...finally, we do the usual behavior when we produce an object - headObject = produceSingleObject(role, extendedEnv, freeInBody).asInstanceOf[LocalReference] + headObject = produceSingleObject(role, extendedEnv, freeInBody) } headObject } @@ -943,7 +943,7 @@ object Transformer { /** * Creates a new object and stores its environment in it */ - private def produceSingleObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { + private def produceSingleObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): LocalReference = { val objectReference = LocalReference(objectType, freshName(role)) val environmentReference = LocalReference(environmentType, freshName("environment")) val size = ConstantInt(environmentSize(environment)); From 5490daa2cdf72e3f737634f8aebaa7c992bf7962 Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 6 Nov 2025 08:24:57 +0100 Subject: [PATCH 21/57] comment adjusted --- libraries/llvm/cMalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 834bd54e8a..11699526d2 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -19,7 +19,7 @@ static const bool DEBUG = false; static Block* freeList = NULL; // Head of the Freelist static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage -static const int blockSize = 64; // The size of each block (128B) +static const int blockSize = 64; // The size of each block (64B) // How much storage do we allocate at the beginning of a program? =4GB static const size_t chunkSize = (size_t)4294967296ULL; From f7de6d89501af337f9486a9e91cb85f54ee2fad3 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 10 Oct 2025 14:38:03 +0200 Subject: [PATCH 22/57] Fixed the linking of the uv library for Intel Macbooks --- effekt/jvm/src/main/scala/effekt/Runner.scala | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/effekt/jvm/src/main/scala/effekt/Runner.scala b/effekt/jvm/src/main/scala/effekt/Runner.scala index 056b86b396..b9f34f2535 100644 --- a/effekt/jvm/src/main/scala/effekt/Runner.scala +++ b/effekt/jvm/src/main/scala/effekt/Runner.scala @@ -287,9 +287,15 @@ 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 @@ -297,7 +303,8 @@ object LLVMRunner extends Runner[String] { } 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 From 617ecbe6422ae6d49b427a3984e218c5094f77b1 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 24 Sep 2025 15:12:29 +0200 Subject: [PATCH 23/57] Basic Bumb allocation --- .../effekt/generator/llvm/Transformer.scala | 2 ++ libraries/llvm/rts.ll | 24 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 87755c4074..4e32724da1 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -24,6 +24,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)) @@ -715,6 +716,7 @@ object Transformer { emit(Load(returnAddressName, returnAddressType, returnAddressPointer, StackPointer)); } + val initializeMemory = ConstantGlobal("initializeMemory"); val newObject = ConstantGlobal("newObject"); val objectEnvironment = ConstantGlobal("objectEnvironment"); diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 604d75a25f..8c95c1ae85 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -135,10 +135,30 @@ define private %Prompt @freshPrompt() { ; Garbage collection + +@freeList = global ptr null + +define private %Object @myMalloc(i64 %size) { + %freeList = load ptr, ptr @freeList + %nextFree = getelementptr i8, ptr %freeList, i64 %size + store ptr %nextFree, ptr @freeList + ret ptr %freeList +} + +define private void @myFree(ptr %object) { + ret void +} + +define private void @initializeMemory() { + %freeList = call ptr @malloc(i64 4294967296) + store ptr %freeList, ptr @freeList + 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 @myMalloc(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 @@ -198,7 +218,7 @@ define private void @eraseObject(%Object %object) alwaysinline { %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 @myFree(%Object %object) br label %done done: From 29ea467f92867e169f61078f01b9a7a3657da3d1 Mon Sep 17 00:00:00 2001 From: Flat Date: Mon, 6 Oct 2025 11:30:14 +0200 Subject: [PATCH 24/57] First approach of a 2nd step of memory-management, but first done in C instead of LLVM because of better debug possibilities --- .../effekt/generator/llvm/Transformer.scala | 2 +- libraries/llvm/bytearray.c | 6 +- libraries/llvm/cMalloc.c | 124 ++++++++++++++++++ libraries/llvm/main.c | 1 + libraries/llvm/rts.ll | 83 ++++++++++-- 5 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 libraries/llvm/cMalloc.c diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 4e32724da1..cfba444be4 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -716,7 +716,7 @@ object Transformer { emit(Load(returnAddressName, returnAddressType, returnAddressPointer, StackPointer)); } - val initializeMemory = ConstantGlobal("initializeMemory"); + val initializeMemory = ConstantGlobal("cInitializeMemory"); val newObject = ConstantGlobal("newObject"); val objectEnvironment = ConstantGlobal("objectEnvironment"); diff --git a/libraries/llvm/bytearray.c b/libraries/llvm/bytearray.c index 1750994577..2b50d38c9c 100644 --- a/libraries/llvm/bytearray.c +++ b/libraries/llvm/bytearray.c @@ -3,6 +3,9 @@ #include // For memcopy +extern void* cMalloc(uint8_t size); + + /** We represent bytearrays like positive types. * * - The field `tag` contains the size @@ -19,7 +22,8 @@ void c_bytearray_erase_noop(void *envPtr) { (void)envPtr; } struct Pos c_bytearray_new(const Int size) { - void *objPtr = malloc(sizeof(struct Header) + size); + int object_size = sizeof(struct Header) + size; + void *objPtr = cMalloc(object_size); struct Header *headerPtr = objPtr; *headerPtr = (struct Header) { .rc = 0, .eraser = c_bytearray_erase_noop, }; return (struct Pos) { diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c new file mode 100644 index 0000000000..f7dd07c872 --- /dev/null +++ b/libraries/llvm/cMalloc.c @@ -0,0 +1,124 @@ +#include +#include +#include + +// ----------------------------- +// 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 = 1024; // 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; +//} diff --git a/libraries/llvm/main.c b/libraries/llvm/main.c index d95f9a5359..916da9650b 100644 --- a/libraries/llvm/main.c +++ b/libraries/llvm/main.c @@ -11,6 +11,7 @@ #include "types.c" #include "bytearray.c" +#include "cMalloc.c" #include "io.c" #include "panic.c" diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 8c95c1ae85..f73e4afa4a 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -94,6 +94,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) @@ -135,30 +140,82 @@ define private %Prompt @freshPrompt() { ; Garbage collection +; A type for the free list +%struct.Block = type { %struct.Block* } -@freeList = global ptr null +@freeList = global %struct.Block* null +@nextUnusedBlock = global i8* null +@endOfChunk = global i8* null +@blockSize = global i64 1024 ; each Block is 1KB -define private %Object @myMalloc(i64 %size) { - %freeList = load ptr, ptr @freeList - %nextFree = getelementptr i8, ptr %freeList, i64 %size - store ptr %nextFree, ptr @freeList - ret ptr %freeList -} +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 -define private void @myFree(ptr %object) { ret void } -define private void @initializeMemory() { - %freeList = call ptr @malloc(i64 4294967296) - store ptr %freeList, ptr @freeList +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 @myMalloc(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 @@ -218,7 +275,7 @@ define private void @eraseObject(%Object %object) alwaysinline { %eraser = load %Eraser, ptr %objectEraser, !alias.scope !14, !noalias !24 %environment = call %Environment @objectEnvironment(%Object %object) call void %eraser(%Environment %environment) - call void @myFree(%Object %object) + call void @cFree(%Object %object) br label %done done: From 453dc580e203372d1327f6f4862e4d7aad6bf6b0 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 24 Oct 2025 18:39:57 +0200 Subject: [PATCH 25/57] Fixed-sized allocation approach. --- .../scala/effekt/generator/llvm/Transformer.scala | 8 +++++++- libraries/common/array.effekt | 7 ++++--- libraries/common/ref.effekt | 9 ++++++--- libraries/llvm/bytearray.c | 8 +++----- libraries/llvm/cMalloc.c | 12 ++++++------ libraries/llvm/io.c | 4 ++-- libraries/llvm/rts.ll | 4 +--- 7 files changed, 29 insertions(+), 23 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index cfba444be4..2b94bbbda0 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -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.* @@ -517,12 +518,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 diff --git a/libraries/common/array.effekt b/libraries/common/array.effekt index 57c621a199..ecd2ad3511 100644 --- a/libraries/common/array.effekt +++ b/libraries/common/array.effekt @@ -20,10 +20,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: @@ -37,6 +37,7 @@ loop: %cmp = icmp ult i64 %inc, %size br i1 %cmp, label %loop, label %exit exit: + call void @free(%Object %object) ret void } """ diff --git a/libraries/common/ref.effekt b/libraries/common/ref.effekt index b206a02b8f..ccf4eb09fa 100644 --- a/libraries/common/ref.effekt +++ b/libraries/common/ref.effekt @@ -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 } """ diff --git a/libraries/llvm/bytearray.c b/libraries/llvm/bytearray.c index 2b50d38c9c..3dce3a6d92 100644 --- a/libraries/llvm/bytearray.c +++ b/libraries/llvm/bytearray.c @@ -2,9 +2,7 @@ #define EFFEKT_BYTEARRAY_C #include // For memcopy - -extern void* cMalloc(uint8_t size); - +#include /** We represent bytearrays like positive types. * @@ -19,11 +17,11 @@ extern void* cMalloc(uint8_t size); */ -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) { int object_size = sizeof(struct Header) + size; - void *objPtr = cMalloc(object_size); + void *objPtr = malloc(object_size); struct Header *headerPtr = objPtr; *headerPtr = (struct Header) { .rc = 0, .eraser = c_bytearray_erase_noop, }; return (struct Pos) { diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index f7dd07c872..fcb4429962 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -22,7 +22,7 @@ typedef struct Block { 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 = 1024; // Größe jedes Blocks (1KB) +static const int blockSize = 128; // Größe jedes Blocks (1KB) // ----------------------------- // Initialisierung @@ -41,7 +41,7 @@ void cInitializeMemory(void) { nextUnusedBlock = mem; endOfChunk = mem + chunkSize; - printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); +// printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); } // ----------------------------- @@ -67,7 +67,7 @@ void* cMalloc(uint8_t size) { void* block = nextUnusedBlock; nextUnusedBlock += blockSize; - printf("[malloc] New block: %p\n", block); +// printf("[malloc] New block: %p\n", block); return block; } @@ -75,8 +75,8 @@ void* cMalloc(uint8_t size) { // 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); +// printf("[malloc] Reusing block: %p\n", (void*)block); +// printf("[malloc] freeList: %p\n", (void*)freeList); return (void*)block; // return malloc(size); } @@ -97,7 +97,7 @@ void cFree(void* ptr) { block->next = freeList; freeList = block; - printf("[free] Freed block: %p\n", ptr); +// printf("[free] Freed block: %p\n", ptr); // free(ptr); } diff --git a/libraries/llvm/io.c b/libraries/llvm/io.c index 3e5d045d18..5396915dd5 100644 --- a/libraries/llvm/io.c +++ b/libraries/llvm/io.c @@ -674,7 +674,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) { @@ -706,7 +706,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() { diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index f73e4afa4a..cf9acfac08 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -273,9 +273,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 @cFree(%Object %object) + call void %eraser(%Object %object) br label %done done: From f501879e0a711a069b6a76153e19d4f76ab5bc7f Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 10:29:20 +0100 Subject: [PATCH 26/57] cMalloc -> acquire and cFree -> release --- .../effekt/generator/llvm/Transformer.scala | 2 +- libraries/llvm/cMalloc.c | 60 ++++--------------- libraries/llvm/rts.ll | 6 +- 3 files changed, 17 insertions(+), 51 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 2b94bbbda0..09f33d07ba 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -528,7 +528,7 @@ object Transformer { loadEnvironmentAt(LocalReference(environmentType, "environment"), freshEnvironment, Object); eraseValues(freshEnvironment, Set()); - emit(Call("", Ccc(), VoidType(), ConstantGlobal("cFree"), List(LocalReference(objectType, "object")))); + emit(Call("", Ccc(), VoidType(), ConstantGlobal("release"), List(LocalReference(objectType, "object")))); RetVoid() }; eraser diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index fcb4429962..a0517fa12e 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -2,9 +2,6 @@ #include #include -// ----------------------------- -// Strukturdefinition -// ----------------------------- /** * @brief Block-Struktur für die Freelist. @@ -15,21 +12,15 @@ 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 -// ----------------------------- +static const int blockSize = 128; // Größe jedes Blocks (128B) /** - * @brief Initialisiert den großen Speicherbereich (4GB). + * Initialisiert den großen Speicherbereich (4GB). */ void cInitializeMemory(void) { size_t chunkSize = (size_t)4294967296ULL; // 4GB @@ -41,7 +32,7 @@ void cInitializeMemory(void) { nextUnusedBlock = mem; endOfChunk = mem + chunkSize; -// printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); + printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); } // ----------------------------- @@ -49,15 +40,12 @@ void cInitializeMemory(void) { // ----------------------------- /** - * @brief Einfacher Speicher-Allocator. + * 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) { +void* acquire(uint8_t size) { // 1. Falls Freelist leer ist → neuer Block if (freeList == NULL) { if (nextUnusedBlock + blockSize > endOfChunk) { @@ -67,58 +55,36 @@ void* cMalloc(uint8_t size) { void* block = nextUnusedBlock; nextUnusedBlock += blockSize; -// printf("[malloc] New block: %p\n", block); + 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); + 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. + * Gibt einen Block zurück in die Freelist. * * @param ptr Zeiger auf den Block. */ -void cFree(void* ptr) { +void release(void* ptr) { if (!ptr) return; Block* block = (Block*)ptr; block->next = freeList; freeList = block; -// printf("[free] Freed block: %p\n", ptr); -// free(ptr); + printf("[free] Freed block: %p\n", ptr); } +// @Deprecated 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; -//} +} \ No newline at end of file diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index cf9acfac08..7d8525bc02 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -95,8 +95,8 @@ ; Foreign imports declare void @cInitializeMemory() -declare ptr @cMalloc(i64) -declare void @cFree(ptr) +declare ptr @acquire(i64) +declare void @release(ptr) declare ptr @cRealloc(ptr, i64) declare ptr @malloc(i64) @@ -215,7 +215,7 @@ define private void @myFree(%Object %object) { 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 @cMalloc(i64 %size) + %object = call ptr @acquire(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 From 69737a52d6e08bfe053e690e7bce796700460664 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 15:12:51 +0100 Subject: [PATCH 27/57] renaming: array_erase_fields -> array_erase --- libraries/common/array.effekt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/common/array.effekt b/libraries/common/array.effekt index ecd2ad3511..89c85ffd86 100644 --- a/libraries/common/array.effekt +++ b/libraries/common/array.effekt @@ -20,7 +20,7 @@ extern type Array[T] extern llvm """ declare noalias ptr @calloc(i64, i64) -define void @array_erase_fields(%Object %object) { +define void @array_erase(%Object %object) { entry: %data_pointer = getelementptr inbounds i64, ptr %object, i64 1 %size = load i64, ptr %object, align 8 @@ -54,7 +54,7 @@ extern def allocate[T](size: Int) at global: Array[T] = %eraser_pointer = getelementptr ptr, ptr %calloc, i64 1 %size_pointer = getelementptr ptr, ptr %calloc, i64 2 store i64 0, ptr %calloc - store ptr @array_erase_fields, ptr %eraser_pointer + store ptr @array_erase, ptr %eraser_pointer store i64 ${size}, ptr %size_pointer %ret_pos = insertvalue %Pos { i64 0, ptr poison }, ptr %calloc, 1 ret %Pos %ret_pos From cbe6d36c9cd27762de9116d7e598a86fb2ea3ec9 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 15:13:09 +0100 Subject: [PATCH 28/57] renaming: c_ref_erase_field -> c_ref_erase --- libraries/common/ref.effekt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/common/ref.effekt b/libraries/common/ref.effekt index ccf4eb09fa..871a8d4dfb 100644 --- a/libraries/common/ref.effekt +++ b/libraries/common/ref.effekt @@ -10,7 +10,7 @@ extern js """ """ extern llvm """ -define void @c_ref_erase_field(%Object %object) { +define void @c_ref_erase(%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 @@ -44,7 +44,7 @@ extern def allocate[T]() at global: Ref[T] = %fieldData_pointer = getelementptr ptr, ptr %ref, i64 3 store i64 0, ptr %ref, align 8 - store ptr @c_ref_erase_field, ptr %refEraser, align 8 + store ptr @c_ref_erase, ptr %refEraser, align 8 store i64 0, ptr %fieldTag, align 8 store ptr null, ptr %fieldData_pointer, align 8 @@ -65,7 +65,7 @@ extern def ref[T](init: T) at global: Ref[T] = %refField = getelementptr ptr, ptr %ref, i64 2 store i64 0, ptr %ref, align 8 - store ptr @c_ref_erase_field, ptr %refEraser, align 8 + store ptr @c_ref_erase, ptr %refEraser, align 8 store %Pos ${init}, ptr %refField, align 8 %refWrap = insertvalue %Pos { i64 0, ptr poison }, ptr %ref, 1 From 1d9ef45e7dc83d41d21aca6dbb8dffc991b98255 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 15:32:04 +0100 Subject: [PATCH 29/57] created a testclass for my tests. Testing records with several sizes --- effekt/jvm/src/test/scala/effekt/LLVMTests.scala | 2 +- examples/flat/big_object.check | 1 + examples/flat/big_object.effekt | 16 ++++++++++++++++ examples/flat/small_object.check | 1 + examples/flat/small_object.effekt | 10 ++++++++++ 5 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 examples/flat/big_object.check create mode 100644 examples/flat/big_object.effekt create mode 100644 examples/flat/small_object.check create mode 100644 examples/flat/small_object.effekt diff --git a/effekt/jvm/src/test/scala/effekt/LLVMTests.scala b/effekt/jvm/src/test/scala/effekt/LLVMTests.scala index 85d9a418ad..89b9c6f342 100644 --- a/effekt/jvm/src/test/scala/effekt/LLVMTests.scala +++ b/effekt/jvm/src/test/scala/effekt/LLVMTests.scala @@ -12,7 +12,7 @@ class LLVMTests extends EffektTests { override def valgrind = sys.env.get("EFFEKT_VALGRIND").nonEmpty override def debug = sys.env.get("EFFEKT_DEBUG").nonEmpty - + override lazy val positives: Set[File] = Set( examplesDir / "llvm", examplesDir / "pos", diff --git a/examples/flat/big_object.check b/examples/flat/big_object.check new file mode 100644 index 0000000000..f11c82a4cb --- /dev/null +++ b/examples/flat/big_object.check @@ -0,0 +1 @@ +9 \ No newline at end of file diff --git a/examples/flat/big_object.effekt b/examples/flat/big_object.effekt new file mode 100644 index 0000000000..8b92b086fe --- /dev/null +++ b/examples/flat/big_object.effekt @@ -0,0 +1,16 @@ +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int + ) + +def main() = { + val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9) + println(p.field9) +} \ No newline at end of file diff --git a/examples/flat/small_object.check b/examples/flat/small_object.check new file mode 100644 index 0000000000..e440e5c842 --- /dev/null +++ b/examples/flat/small_object.check @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/examples/flat/small_object.effekt b/examples/flat/small_object.effekt new file mode 100644 index 0000000000..354cf36fc2 --- /dev/null +++ b/examples/flat/small_object.effekt @@ -0,0 +1,10 @@ +record Person( + field1: Int, + field2: Int, + field3: Int + ) + +def main() = { + val p = Person(1, 2, 3) + println(p.field3) +} \ No newline at end of file From 1b9ab8726e2a9b0d45b93212cc7ad565a66c30fe Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 16:13:14 +0100 Subject: [PATCH 30/57] created a small test set for asserting the saving slot size --- .../jvm/src/test/scala/effekt/FlatTests.scala | 20 ++++++++++++++++ examples/flat/big_object.check | 1 - examples/flat/one_lg_object.check | 1 + examples/flat/one_lg_object.effekt | 20 ++++++++++++++++ examples/flat/one_md_object.check | 1 + ...big_object.effekt => one_md_object.effekt} | 9 ++++---- examples/flat/one_sm_object.check | 1 + examples/flat/one_sm_object.effekt | 14 +++++++++++ examples/flat/one_xl_object.check | 1 + examples/flat/one_xl_object.effekt | 21 +++++++++++++++++ ...small_object.check => one_xs_object.check} | 0 ...all_object.effekt => one_xs_object.effekt} | 1 + examples/flat/two_lg_objects.check | 2 ++ examples/flat/two_lg_objects.effekt | 22 ++++++++++++++++++ examples/flat/two_md_objects.check | 2 ++ examples/flat/two_md_objects.effekt | 17 ++++++++++++++ examples/flat/two_sm_objects.check | 2 ++ examples/flat/two_sm_objects.effekt | 16 +++++++++++++ examples/flat/two_xl_objects.check | 2 ++ examples/flat/two_xl_objects.effekt | 23 +++++++++++++++++++ examples/flat/two_xs_objects.check | 2 ++ examples/flat/two_xs_objects.effekt | 13 +++++++++++ 22 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 effekt/jvm/src/test/scala/effekt/FlatTests.scala delete mode 100644 examples/flat/big_object.check create mode 100644 examples/flat/one_lg_object.check create mode 100644 examples/flat/one_lg_object.effekt create mode 100644 examples/flat/one_md_object.check rename examples/flat/{big_object.effekt => one_md_object.effekt} (53%) create mode 100644 examples/flat/one_sm_object.check create mode 100644 examples/flat/one_sm_object.effekt create mode 100644 examples/flat/one_xl_object.check create mode 100644 examples/flat/one_xl_object.effekt rename examples/flat/{small_object.check => one_xs_object.check} (100%) rename examples/flat/{small_object.effekt => one_xs_object.effekt} (74%) create mode 100644 examples/flat/two_lg_objects.check create mode 100644 examples/flat/two_lg_objects.effekt create mode 100644 examples/flat/two_md_objects.check create mode 100644 examples/flat/two_md_objects.effekt create mode 100644 examples/flat/two_sm_objects.check create mode 100644 examples/flat/two_sm_objects.effekt create mode 100644 examples/flat/two_xl_objects.check create mode 100644 examples/flat/two_xl_objects.effekt create mode 100644 examples/flat/two_xs_objects.check create mode 100644 examples/flat/two_xs_objects.effekt diff --git a/effekt/jvm/src/test/scala/effekt/FlatTests.scala b/effekt/jvm/src/test/scala/effekt/FlatTests.scala new file mode 100644 index 0000000000..909e825c94 --- /dev/null +++ b/effekt/jvm/src/test/scala/effekt/FlatTests.scala @@ -0,0 +1,20 @@ +package effekt + +import sbt.io.* +import sbt.io.syntax.* + +import java.io.File +import scala.language.implicitConversions +import scala.sys.process.Process + +// df: My own test class to run my requested tests automatically +class FlatTests extends EffektTests { + + def backendName = "llvm" + + override lazy val positives: Set[File] = Set() + + override lazy val withoutOptimizations: Set[File] = Set( + examplesDir / "flat", + ) +} diff --git a/examples/flat/big_object.check b/examples/flat/big_object.check deleted file mode 100644 index f11c82a4cb..0000000000 --- a/examples/flat/big_object.check +++ /dev/null @@ -1 +0,0 @@ -9 \ No newline at end of file diff --git a/examples/flat/one_lg_object.check b/examples/flat/one_lg_object.check new file mode 100644 index 0000000000..3cacc0b93c --- /dev/null +++ b/examples/flat/one_lg_object.check @@ -0,0 +1 @@ +12 \ No newline at end of file diff --git a/examples/flat/one_lg_object.effekt b/examples/flat/one_lg_object.effekt new file mode 100644 index 0000000000..42691204c7 --- /dev/null +++ b/examples/flat/one_lg_object.effekt @@ -0,0 +1,20 @@ +// lg = Person fills 2 saving slots completely +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int + ) + +def main() = { + val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + println(p.field12) +} \ No newline at end of file diff --git a/examples/flat/one_md_object.check b/examples/flat/one_md_object.check new file mode 100644 index 0000000000..c7930257df --- /dev/null +++ b/examples/flat/one_md_object.check @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/examples/flat/big_object.effekt b/examples/flat/one_md_object.effekt similarity index 53% rename from examples/flat/big_object.effekt rename to examples/flat/one_md_object.effekt index 8b92b086fe..d68c5a9608 100644 --- a/examples/flat/big_object.effekt +++ b/examples/flat/one_md_object.effekt @@ -1,3 +1,4 @@ +// md = Person requires 2 saving slots record Person( field1: Int, field2: Int, @@ -5,12 +6,10 @@ record Person( field4: Int, field5: Int, field6: Int, - field7: Int, - field8: Int, - field9: Int + field7: Int ) def main() = { - val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9) - println(p.field9) + val p = Person(1, 2, 3, 4, 5, 6, 7) + println(p.field7) } \ No newline at end of file diff --git a/examples/flat/one_sm_object.check b/examples/flat/one_sm_object.check new file mode 100644 index 0000000000..62f9457511 --- /dev/null +++ b/examples/flat/one_sm_object.check @@ -0,0 +1 @@ +6 \ No newline at end of file diff --git a/examples/flat/one_sm_object.effekt b/examples/flat/one_sm_object.effekt new file mode 100644 index 0000000000..95ffb2305d --- /dev/null +++ b/examples/flat/one_sm_object.effekt @@ -0,0 +1,14 @@ +// sm = Person fills 1 saving slot completely +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int + ) + +def main() = { + val p = Person(1, 2, 3, 4, 5, 6) + println(p.field6) +} \ No newline at end of file diff --git a/examples/flat/one_xl_object.check b/examples/flat/one_xl_object.check new file mode 100644 index 0000000000..ca7bf83ac5 --- /dev/null +++ b/examples/flat/one_xl_object.check @@ -0,0 +1 @@ +13 \ No newline at end of file diff --git a/examples/flat/one_xl_object.effekt b/examples/flat/one_xl_object.effekt new file mode 100644 index 0000000000..aff3ed9e29 --- /dev/null +++ b/examples/flat/one_xl_object.effekt @@ -0,0 +1,21 @@ +// xl = Person needs 3 saving slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int + ) + +def main() = { + val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) + println(p.field13) +} \ No newline at end of file diff --git a/examples/flat/small_object.check b/examples/flat/one_xs_object.check similarity index 100% rename from examples/flat/small_object.check rename to examples/flat/one_xs_object.check diff --git a/examples/flat/small_object.effekt b/examples/flat/one_xs_object.effekt similarity index 74% rename from examples/flat/small_object.effekt rename to examples/flat/one_xs_object.effekt index 354cf36fc2..034c32ea5a 100644 --- a/examples/flat/small_object.effekt +++ b/examples/flat/one_xs_object.effekt @@ -1,3 +1,4 @@ +// xs = Person fits easily in 1 saving slot record Person( field1: Int, field2: Int, diff --git a/examples/flat/two_lg_objects.check b/examples/flat/two_lg_objects.check new file mode 100644 index 0000000000..89917fb8fd --- /dev/null +++ b/examples/flat/two_lg_objects.check @@ -0,0 +1,2 @@ +12 +19 \ No newline at end of file diff --git a/examples/flat/two_lg_objects.effekt b/examples/flat/two_lg_objects.effekt new file mode 100644 index 0000000000..62c0e7a235 --- /dev/null +++ b/examples/flat/two_lg_objects.effekt @@ -0,0 +1,22 @@ +// lg = Person fills 2 saving slots completely +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + val p2 = Person(13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) + println(p1.field12) + println(p2.field7) +} \ No newline at end of file diff --git a/examples/flat/two_md_objects.check b/examples/flat/two_md_objects.check new file mode 100644 index 0000000000..78bd7702cc --- /dev/null +++ b/examples/flat/two_md_objects.check @@ -0,0 +1,2 @@ +7 +9 \ No newline at end of file diff --git a/examples/flat/two_md_objects.effekt b/examples/flat/two_md_objects.effekt new file mode 100644 index 0000000000..d3bdb013f6 --- /dev/null +++ b/examples/flat/two_md_objects.effekt @@ -0,0 +1,17 @@ +// md = Person requires 2 saving slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6, 7) + val p2 = Person(8, 9, 10, 11, 12, 13, 14) + println(p1.field7) + println(p2.field2) +} \ No newline at end of file diff --git a/examples/flat/two_sm_objects.check b/examples/flat/two_sm_objects.check new file mode 100644 index 0000000000..b8a0ddcef6 --- /dev/null +++ b/examples/flat/two_sm_objects.check @@ -0,0 +1,2 @@ +6 +8 \ No newline at end of file diff --git a/examples/flat/two_sm_objects.effekt b/examples/flat/two_sm_objects.effekt new file mode 100644 index 0000000000..a1d1d206ca --- /dev/null +++ b/examples/flat/two_sm_objects.effekt @@ -0,0 +1,16 @@ +// sm = Person fills 1 saving slot completely +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6) + val p2 = Person(7, 8, 9, 10, 11, 12) + println(p1.field6) + println(p2.field2) +} \ No newline at end of file diff --git a/examples/flat/two_xl_objects.check b/examples/flat/two_xl_objects.check new file mode 100644 index 0000000000..bda4b3e2f4 --- /dev/null +++ b/examples/flat/two_xl_objects.check @@ -0,0 +1,2 @@ +13 +20 \ No newline at end of file diff --git a/examples/flat/two_xl_objects.effekt b/examples/flat/two_xl_objects.effekt new file mode 100644 index 0000000000..c1a445f31f --- /dev/null +++ b/examples/flat/two_xl_objects.effekt @@ -0,0 +1,23 @@ +// xl = Person needs 3 saving slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) + val p2 = Person(14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26) + println(p1.field13) + println(p2.field7) +} \ No newline at end of file diff --git a/examples/flat/two_xs_objects.check b/examples/flat/two_xs_objects.check new file mode 100644 index 0000000000..69cc3abe58 --- /dev/null +++ b/examples/flat/two_xs_objects.check @@ -0,0 +1,2 @@ +3 +5 \ No newline at end of file diff --git a/examples/flat/two_xs_objects.effekt b/examples/flat/two_xs_objects.effekt new file mode 100644 index 0000000000..02f18b3a9c --- /dev/null +++ b/examples/flat/two_xs_objects.effekt @@ -0,0 +1,13 @@ +// xs = Person fits easily in 1 saving slot +record Person( + field1: Int, + field2: Int, + field3: Int + ) + +def main() = { + val p1 = Person(1, 2, 3) + val p2 = Person(4, 5, 6) + println(p1.field3) + println(p2.field2) +} \ No newline at end of file From 5d5fb53158f3466c3631a4f6b3c480e8134e3f3f Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 29 Oct 2025 16:31:27 +0100 Subject: [PATCH 31/57] approach for testing that no memory is leaked by acquire -> does not run yet, since we have 10 failing tests --- .../effekt/generator/llvm/Transformer.scala | 24 +++- libraries/llvm/cMalloc.c | 120 ++++++++++++++---- libraries/llvm/rts.ll | 2 +- 3 files changed, 119 insertions(+), 27 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 09f33d07ba..e383c2096d 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -2,11 +2,12 @@ package effekt package generator package llvm +import effekt.generator.llvm.Instruction.Call import effekt.machine import effekt.machine.Variable +import effekt.machine.analysis.* import effekt.util.intercalate import effekt.util.messages.ErrorReporter -import effekt.machine.analysis.* import scala.annotation.tailrec import scala.collection.mutable @@ -25,8 +26,10 @@ 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")))) + Call("", Ccc(), VoidType(), initializeMemory, List()), + Call("_", Tailcc(false), VoidType(), transform(entry), List(LocalReference(stackType, "stack"))), + Call("", Ccc(), VoidType(), testIfAllBlocksAreFreed, List()), + ) val entryBlock = BasicBlock("entry", entryInstructions, RetVoid()) val entryFunction = Function(External(), Ccc(), VoidType(), "effektMain", List(), List(entryBlock)) @@ -105,6 +108,7 @@ object Transformer { emit(ExtractValue(objectName, transform(value), 1)) val stack = getStack() + def labelClause(clause: machine.Clause, isDefault: Boolean): String = { implicit val BC = BlockContext() BC.stack = stack @@ -356,7 +360,7 @@ object Transformer { eraseValues(List(v), freeVariables(rest)); transform(rest) - case machine.ForeignCall(variable @ machine.Variable(resultName, resultType), foreign, values, rest) => + case machine.ForeignCall(variable@machine.Variable(resultName, resultType), foreign, values, rest) => emit(Comment(s"foreignCall $resultName : $resultType, foreign $foreign, ${values.length} values")) shareValues(values, freeVariables(rest)); emit(Call(resultName, Ccc(), transform(resultType), ConstantGlobal(foreign), values.map(transform))); @@ -579,6 +583,17 @@ object Transformer { def produceObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { if (environment.isEmpty) { ConstantNull(objectType) + } else if (environmentSize(environment) > 48) { // if the object does not fit in a single LLVM-Block + val objectReference = LocalReference(objectType, freshName(role)); + val environmentReference = LocalReference(environmentType, freshName("environment")); + val size = ConstantInt(environmentSize(environment)); + val eraser = getEraser(environment, ObjectEraser) + + emit(Call(objectReference.name, Ccc(), objectType, newObject, List(eraser, size))); + emit(Call(environmentReference.name, Ccc(), environmentType, objectEnvironment, List(objectReference))); + shareValues(environment, freeInBody); + storeEnvironment(environmentReference, environment, Object); + objectReference } else { val objectReference = LocalReference(objectType, freshName(role)); val environmentReference = LocalReference(environmentType, freshName("environment")); @@ -723,6 +738,7 @@ object Transformer { } val initializeMemory = ConstantGlobal("cInitializeMemory"); + val testIfAllBlocksAreFreed = ConstantGlobal("testIfAllBlocksAreFreed"); val newObject = ConstantGlobal("newObject"); val objectEnvironment = ConstantGlobal("objectEnvironment"); diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index a0517fa12e..349b81a685 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -8,31 +8,39 @@ * * Jeder freie Block zeigt auf den nächsten freien Block. */ -typedef struct 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 (128B) +static const bool DEBUG = false; +static Block* freeList = NULL; // Head of the Freelist +static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block +static uint8_t* endOfChunk = NULL; // End of the allocated Storage +static const int blockSize = 128; // The size of each block (128B) + +// How much storage do we allocate at the beginning of a program? =4GB +static const size_t chunkSize = (size_t)4294967296ULL; /** * Initialisiert den großen Speicherbereich (4GB). */ -void cInitializeMemory(void) { - size_t chunkSize = (size_t)4294967296ULL; // 4GB +void cInitializeMemory(void) +{ uint8_t* mem = (uint8_t*)malloc(chunkSize); - if (!mem) { + 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); + if (DEBUG) { + printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); + } } // ----------------------------- @@ -45,25 +53,33 @@ void cInitializeMemory(void) { * 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. */ -void* acquire(uint8_t size) { +void* acquire(uint8_t size) +{ // 1. Falls Freelist leer ist → neuer Block - if (freeList == NULL) { - if (nextUnusedBlock + blockSize > endOfChunk) { + 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); + if (DEBUG) { + 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); + + if (DEBUG) { + printf("[malloc] Reusing block: %p\n", (void*)block); + printf("[malloc] freeList: %p\n", (void*)freeList); + } return (void*)block; } @@ -73,18 +89,78 @@ void* acquire(uint8_t size) { * * @param ptr Zeiger auf den Block. */ -void release(void* ptr) { +void release(void* ptr) +{ if (!ptr) return; Block* block = (Block*)ptr; block->next = freeList; freeList = block; - printf("[free] Freed block: %p\n", ptr); + if (DEBUG) { + printf("[free] Freed block: %p\n", ptr); + } +} + +/** +* +*/ +void assertNumberLeakedBlocks(int expected) { + // Count how many blocks are in the freelist + size_t freeCount = 0; + for (const Block* b = freeList; b != NULL; b = b->next) { + freeCount++; + } + + // Total number of blocks that were ever allocated + const uint8_t* firstBlock = (endOfChunk - chunkSize); // the start of the chunk + const size_t totalAllocated = (nextUnusedBlock - firstBlock) / blockSize; + + // Calculate the number of leaked blocks + const size_t numberOfLeakedBlocks = totalAllocated - freeCount; + + // Report if there are any leaked blocks +// if (numberOfLeakedBlocks != expected) { +// printf("firstBlock: %p\n", (void*)firstBlock); +// printf("nextUnusedBlock: %p\n", (void*)nextUnusedBlock); +// +// printf("Test failed!, Total Allocated: %zu, Free Count: %zu \n", totalAllocated, freeCount); +// exit(1); +// } +} + +/** +* Works as a Unit-Test, if all used blocks are freed at the end of the program. +* If not, we print an error message, making test fail. +*/ +void testIfAllBlocksAreFreed() +{ + assertNumberLeakedBlocks(0); } -// @Deprecated -void* cRealloc(void* ptr, uint8_t size) { - printf("cRealloc: %p, %d\n", ptr, size); - return realloc(ptr,size); -} \ No newline at end of file +// small testprogram to test the allocator +//int main(void) +//{ +// cInitializeMemory(); +// assertNumberLeakedBlocks(0); +// +// void* a = acquire((uint8_t)1024); +// assertNumberLeakedBlocks(1); +// +// void* b = acquire((uint8_t)1024); +// assertNumberLeakedBlocks(2); +// +// release(a); +// assertNumberLeakedBlocks(1); +// +// release(b); +// assertNumberLeakedBlocks(0); +// +// void* c = acquire((uint8_t)1024); // should reuse a +// assertNumberLeakedBlocks(1); +// +// release(c); +// testIfAllBlocksAreFreed(); +// +// return 0; +//} diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 7d8525bc02..a19bbd81bd 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -97,7 +97,7 @@ declare void @cInitializeMemory() declare ptr @acquire(i64) declare void @release(ptr) -declare ptr @cRealloc(ptr, i64) +declare void @testIfAllBlocksAreFreed() declare ptr @malloc(i64) declare void @free(ptr) From 6166e6bd007af4352b0fd9cdc4f50a1c34813456 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 31 Oct 2025 08:13:37 +0100 Subject: [PATCH 32/57] added some more testprograms --- examples/flat/array_record.check | 1 + examples/flat/array_record.effekt | 10 ++++++++++ examples/flat/nested_objects.check | 1 + examples/flat/nested_objects.effekt | 16 ++++++++++++++++ 4 files changed, 28 insertions(+) create mode 100644 examples/flat/array_record.check create mode 100644 examples/flat/array_record.effekt create mode 100644 examples/flat/nested_objects.check create mode 100644 examples/flat/nested_objects.effekt diff --git a/examples/flat/array_record.check b/examples/flat/array_record.check new file mode 100644 index 0000000000..56ea891855 --- /dev/null +++ b/examples/flat/array_record.check @@ -0,0 +1 @@ +Array(1, 2, 3) \ No newline at end of file diff --git a/examples/flat/array_record.effekt b/examples/flat/array_record.effekt new file mode 100644 index 0000000000..0580fbeae2 --- /dev/null +++ b/examples/flat/array_record.effekt @@ -0,0 +1,10 @@ +record Person( + field1: Int, + field2: Array[Int] + ) + +def main() = { + val myArray: Array[Int] = build(3) { i => i + 1 } // Array(1, 2, 3) + val p = Person(1, myArray) + println(myArray) +} \ No newline at end of file diff --git a/examples/flat/nested_objects.check b/examples/flat/nested_objects.check new file mode 100644 index 0000000000..e440e5c842 --- /dev/null +++ b/examples/flat/nested_objects.check @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/examples/flat/nested_objects.effekt b/examples/flat/nested_objects.effekt new file mode 100644 index 0000000000..27a274b5fc --- /dev/null +++ b/examples/flat/nested_objects.effekt @@ -0,0 +1,16 @@ +record Person1( + field3: Int, + field4: Int +) + +record Person( + field1: Int, + field2: Int, + person1: Person1 + ) + +def main() = { + val p1 = Person1(3, 4) + val p = Person(1, 2, p1) + println(p.person1.field3) +} \ No newline at end of file From 307fb44a216eed6b1c72635021ef2e1c9eb92e2a Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 31 Oct 2025 11:46:48 +0100 Subject: [PATCH 33/57] refactored cMalloc.c --- libraries/llvm/cMalloc.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 349b81a685..c42296186b 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -67,7 +67,7 @@ void* acquire(uint8_t size) void* block = nextUnusedBlock; nextUnusedBlock += blockSize; if (DEBUG) { - printf("[malloc] New block: %p\n", block); + printf("[acquire] New block: %p\n", block); } return block; } @@ -77,8 +77,7 @@ void* acquire(uint8_t size) freeList = block->next; if (DEBUG) { - printf("[malloc] Reusing block: %p\n", (void*)block); - printf("[malloc] freeList: %p\n", (void*)freeList); + printf("[acquire] Reusing block: %p\n", (void*)block); } return (void*)block; } @@ -98,7 +97,7 @@ void release(void* ptr) freeList = block; if (DEBUG) { - printf("[free] Freed block: %p\n", ptr); + printf("[release] Freed block: %p\n", ptr); } } @@ -107,9 +106,9 @@ void release(void* ptr) */ void assertNumberLeakedBlocks(int expected) { // Count how many blocks are in the freelist - size_t freeCount = 0; + size_t numberOfElementsInFreeList = 0; for (const Block* b = freeList; b != NULL; b = b->next) { - freeCount++; + numberOfElementsInFreeList++; } // Total number of blocks that were ever allocated @@ -117,16 +116,16 @@ void assertNumberLeakedBlocks(int expected) { const size_t totalAllocated = (nextUnusedBlock - firstBlock) / blockSize; // Calculate the number of leaked blocks - const size_t numberOfLeakedBlocks = totalAllocated - freeCount; + const size_t numberOfLeakedBlocks = totalAllocated - numberOfElementsInFreeList; // Report if there are any leaked blocks -// if (numberOfLeakedBlocks != expected) { -// printf("firstBlock: %p\n", (void*)firstBlock); -// printf("nextUnusedBlock: %p\n", (void*)nextUnusedBlock); -// -// printf("Test failed!, Total Allocated: %zu, Free Count: %zu \n", totalAllocated, freeCount); -// exit(1); -// } + if (numberOfLeakedBlocks != expected) { + printf("firstBlock: %p\n", (void*)firstBlock); + printf("nextUnusedBlock: %p\n", (void*)nextUnusedBlock); + + printf("Test failed!, Total Allocated: %zu, Elements in Free List: %zu \n", totalAllocated, numberOfElementsInFreeList); + exit(1); + } } /** @@ -135,7 +134,7 @@ void assertNumberLeakedBlocks(int expected) { */ void testIfAllBlocksAreFreed() { - assertNumberLeakedBlocks(0); +// assertNumberLeakedBlocks(0); } // small testprogram to test the allocator From c81568ca590fb72eb0f4d547c268cabae068f7ee Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 31 Oct 2025 13:44:45 +0100 Subject: [PATCH 34/57] Big objects are now splitted into a multiple objects with links to the next slot. Slot size are now 64 bytes --- .../effekt/generator/llvm/Transformer.scala | 180 +++++++++++++++--- libraries/llvm/cMalloc.c | 2 +- 2 files changed, 154 insertions(+), 28 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index e383c2096d..ca7fa31adb 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -2,6 +2,7 @@ package effekt package generator package llvm +import effekt.generator.llvm.{Type => LLVMType} import effekt.generator.llvm.Instruction.Call import effekt.machine import effekt.machine.Variable @@ -580,31 +581,16 @@ object Transformer { }) } + /** + * Produces an Object. It is always 64 bytes long + */ def produceObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { if (environment.isEmpty) { ConstantNull(objectType) - } else if (environmentSize(environment) > 48) { // if the object does not fit in a single LLVM-Block - val objectReference = LocalReference(objectType, freshName(role)); - val environmentReference = LocalReference(environmentType, freshName("environment")); - val size = ConstantInt(environmentSize(environment)); - val eraser = getEraser(environment, ObjectEraser) - - emit(Call(objectReference.name, Ccc(), objectType, newObject, List(eraser, size))); - emit(Call(environmentReference.name, Ccc(), environmentType, objectEnvironment, List(objectReference))); - shareValues(environment, freeInBody); - storeEnvironment(environmentReference, environment, Object); - objectReference + } else if (fitsInOneSavingSlot(environment)) { + produceSingleObject(role, environment, freeInBody) } else { - val objectReference = LocalReference(objectType, freshName(role)); - val environmentReference = LocalReference(environmentType, freshName("environment")); - val size = ConstantInt(environmentSize(environment)); - val eraser = getEraser(environment, ObjectEraser) - - emit(Call(objectReference.name, Ccc(), objectType, newObject, List(eraser, size))); - emit(Call(environmentReference.name, Ccc(), environmentType, objectEnvironment, List(objectReference))); - shareValues(environment, freeInBody); - storeEnvironment(environmentReference, environment, Object); - objectReference + produceShardedObject(role, environment, freeInBody) } } @@ -674,12 +660,58 @@ object Transformer { } def loadEnvironmentAt(pointer: Operand, environment: machine.Environment, alias: AliasInfo)(using ModuleContext, FunctionContext, BlockContext): Unit = { - val `type` = environmentType(environment) - environment.zipWithIndex.foreach { - case (machine.Variable(name, tpe), i) => - val field = LocalReference(PointerType(), freshName(name + "_pointer")); - emit(GetElementPtr(field.name, `type`, pointer, List(0, i))); - emit(Load(name, transform(tpe), field, alias)) + alias match { + // if we have a sharded object + case Object if !fitsInOneSavingSlot(environment) => + // Follow chained 64-byte blocks created in produceObject + val chunkedEnvironments = splitEnvironment(environment) + var currentPtr: Operand = pointer + + chunkedEnvironments.zipWithIndex.foreach { case (chunk, idx) => + val isLast = idx == chunkedEnvironments.length - 1 + if (isLast) { + val tpe = environmentType(chunk) + chunk.zipWithIndex.foreach { + case (machine.Variable(name, tpe0), i) => + val field = LocalReference(PointerType(), freshName(name + "_pointer")) + emit(GetElementPtr(field.name, tpe, currentPtr, List(0, i))) + emit(Load(name, transform(tpe0), field, alias)) + } + } else { + // For non-last chunkedEnvironments, layout is chunk ++ [Pos link] + val tpe = StructureType(chunk.map { case machine.Variable(_, t) => transform(t) } :+ positiveType) + + // Load actual chunk fields + chunk.zipWithIndex.foreach { + case (machine.Variable(name, tpe0), i) => + val field = LocalReference(PointerType(), freshName(name + "_pointer")) + emit(GetElementPtr(field.name, tpe, currentPtr, List(0, i))) + emit(Load(name, transform(tpe0), field, alias)) + } + + // Follow link to next block + val linkPtr = LocalReference(PointerType(), freshName("link_pointer")) + emit(GetElementPtr(linkPtr.name, tpe, currentPtr, List(0, chunk.length))) + + val linkVal = LocalReference(positiveType, freshName("link")) + emit(Load(linkVal.name, positiveType, linkPtr, alias)) + + val nextObj = LocalReference(objectType, freshName("next_object")) + emit(ExtractValue(nextObj.name, linkVal, 1)) + + val nextEnv = LocalReference(environmentType, freshName("environment")) + emit(Call(nextEnv.name, Ccc(), environmentType, objectEnvironment, List(nextObj))) + currentPtr = nextEnv + } + } + case _: effekt.generator.llvm.AliasInfo => + val `type` = environmentType(environment) + environment.zipWithIndex.foreach { + case (machine.Variable(name, tpe), i) => + val field = LocalReference(PointerType(), freshName(name + "_pointer")); + emit(GetElementPtr(field.name, `type`, pointer, List(0, i))); + emit(Load(name, transform(tpe), field, alias)) + } } } @@ -816,4 +848,98 @@ object Transformer { case None => acc += c } }.toString() + + /** + * if we can fit our object in one single saving block -> Easiest case + * Else we have to split our environment into multiple saving slots and reference in a linked list fashion + */ + private def fitsInOneSavingSlot(environment: machine.Environment): Boolean = { + environmentSize(environment) > 0 && environmentSize(environment) <= 48 + } + + /** + * Splits the environment into multiple environment such that you can store one object in multiple saving slots. + * Is required to do fixed-sized-allocation. + */ + private def splitEnvironment(environment: machine.Environment): List[machine.Environment] = { + val slotSize = 64 // we want to use 64 bytes for each saving slot + var headerSize = 16 // we use 16 byte for the last saving block only, the others are 32 bytes + var currentEnvironment = List[machine.Variable]() + var result = List[machine.Environment]() + var isLast = true + for (variable <- environment.reverse) { + val variableSize = typeSize(variable.tpe) + + if (headerSize + (environmentSize(currentEnvironment) + variableSize) <= slotSize) { + currentEnvironment = currentEnvironment :+ variable + } + else { + result = result :+ currentEnvironment.reverse + currentEnvironment = List(variable) + isLast = false + headerSize = 32 // normal header size + size of the positive type which is the reference to the next object + } + } + result = result :+ currentEnvironment.reverse + + result.reverse + } + + /** + * When you have a big object to produce, you shard it into multiple slots instead of one single big slot. + * The end of each slot references to the next one. + * Each slot is 64 byte long + */ + private def produceShardedObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { + // Split into multiple 64-byte blocks and chain them via a boxed link (%Pos with tag 0 pointing to next %Object) + val shardedEnvironments = splitEnvironment(environment) + + // Allocate the last block first. + val lastEnvironment = shardedEnvironments.last + val lastObjectReference = LocalReference(objectType, freshName(role)) + val lastEnvPtr = LocalReference(environmentType, freshName("environment")) + val lastSize = ConstantInt(environmentSize(lastEnvironment)) + val lastEraser = getEraser(lastEnvironment, ObjectEraser) + + emit(Call(lastObjectReference.name, Ccc(), objectType, newObject, List(lastEraser, lastSize))) + emit(Call(lastEnvPtr.name, Ccc(), environmentType, objectEnvironment, List(lastObjectReference))) + shareValues(lastEnvironment, freeInBody) + storeEnvironment(lastEnvPtr, lastEnvironment, Object) + + // Chain preceding blocks, each storing a boxed link to the next object + var headObject: LocalReference = lastObjectReference + + // loop through all shardedEnvironments except the last one in reversed order + shardedEnvironments.init.reverse.foreach { envChunk => + val temporaryName = freshName("link_temporary") + val linkValName = freshName("link") + + // we create a new Positive Object with tag 0... + emit(InsertValue(temporaryName, ConstantAggregateZero(positiveType), ConstantInt(0), 0)) // we create a fake Link Tag 0 that we just use for store a tag in the Pos + emit(InsertValue(linkValName, LocalReference(positiveType, temporaryName), headObject, 1)) + + // ... and inject it into our environment... + val extendedEnv = envChunk :+ machine.Variable(linkValName, machine.Positive()) + + // ...finally, we do the usual behavior when we produce an object + headObject = produceSingleObject(role, extendedEnv, freeInBody).asInstanceOf[LocalReference] + } + headObject + } + + /** + * Creates a new object and stores its environment in it + */ + private def produceSingleObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { + val objectReference = LocalReference(objectType, freshName(role)) + val environmentReference = LocalReference(environmentType, freshName("environment")) + val size = ConstantInt(environmentSize(environment)); + val eraser = getEraser(environment, ObjectEraser) + + emit(Call(objectReference.name, Ccc(), objectType, newObject, List(eraser, size))); + emit(Call(environmentReference.name, Ccc(), environmentType, objectEnvironment, List(objectReference))); + shareValues(environment, freeInBody); + storeEnvironment(environmentReference, environment, Object); + objectReference + } } diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index c42296186b..834bd54e8a 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -19,7 +19,7 @@ static const bool DEBUG = false; static Block* freeList = NULL; // Head of the Freelist static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage -static const int blockSize = 128; // The size of each block (128B) +static const int blockSize = 64; // The size of each block (128B) // How much storage do we allocate at the beginning of a program? =4GB static const size_t chunkSize = (size_t)4294967296ULL; From 536cd3c9a22fd09af77b80f9674f2bf78814d254 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 31 Oct 2025 13:44:45 +0100 Subject: [PATCH 35/57] Big objects are now splitted into a multiple objects with links to the next slot. Slot size are now 64 bytes --- .../effekt/generator/llvm/Transformer.scala | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index ca7fa31adb..07669812b7 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -2,10 +2,10 @@ package effekt package generator package llvm -import effekt.generator.llvm.{Type => LLVMType} +import effekt.generator.llvm.Type as LLVMType import effekt.generator.llvm.Instruction.Call import effekt.machine -import effekt.machine.Variable +import effekt.machine.{Environment, Variable} import effekt.machine.analysis.* import effekt.util.intercalate import effekt.util.messages.ErrorReporter @@ -659,39 +659,30 @@ object Transformer { } } - def loadEnvironmentAt(pointer: Operand, environment: machine.Environment, alias: AliasInfo)(using ModuleContext, FunctionContext, BlockContext): Unit = { + def loadEnvironmentAt(elementPointer: Operand, environment: machine.Environment, alias: AliasInfo)(using ModuleContext, FunctionContext, BlockContext): Unit = { alias match { // if we have a sharded object case Object if !fitsInOneSavingSlot(environment) => // Follow chained 64-byte blocks created in produceObject val chunkedEnvironments = splitEnvironment(environment) - var currentPtr: Operand = pointer + var currentEnvironmentPtr: Operand = elementPointer + // here we loop over all environments *in* order chunkedEnvironments.zipWithIndex.foreach { case (chunk, idx) => val isLast = idx == chunkedEnvironments.length - 1 if (isLast) { val tpe = environmentType(chunk) - chunk.zipWithIndex.foreach { - case (machine.Variable(name, tpe0), i) => - val field = LocalReference(PointerType(), freshName(name + "_pointer")) - emit(GetElementPtr(field.name, tpe, currentPtr, List(0, i))) - emit(Load(name, transform(tpe0), field, alias)) - } + loadAllVariables(currentEnvironmentPtr, chunk, alias, tpe) } else { + // Here we do the same as for the normal slot plus some extra stuff + // For non-last chunkedEnvironments, layout is chunk ++ [Pos link] - val tpe = StructureType(chunk.map { case machine.Variable(_, t) => transform(t) } :+ positiveType) - - // Load actual chunk fields - chunk.zipWithIndex.foreach { - case (machine.Variable(name, tpe0), i) => - val field = LocalReference(PointerType(), freshName(name + "_pointer")) - emit(GetElementPtr(field.name, tpe, currentPtr, List(0, i))) - emit(Load(name, transform(tpe0), field, alias)) - } - - // Follow link to next block + val tpe = StructureType(chunk.map { case machine.Variable(_, t) => transform(t) } :+ positiveType) // the same as for the normal case + positive Type for the link + loadAllVariables(currentEnvironmentPtr, chunk, alias, tpe) + + // Follow link to next block. The Link pointer is the last element of the chunk val linkPtr = LocalReference(PointerType(), freshName("link_pointer")) - emit(GetElementPtr(linkPtr.name, tpe, currentPtr, List(0, chunk.length))) + emit(GetElementPtr(linkPtr.name, tpe, currentEnvironmentPtr, List(0, chunk.length))) val linkVal = LocalReference(positiveType, freshName("link")) emit(Load(linkVal.name, positiveType, linkPtr, alias)) @@ -701,17 +692,12 @@ object Transformer { val nextEnv = LocalReference(environmentType, freshName("environment")) emit(Call(nextEnv.name, Ccc(), environmentType, objectEnvironment, List(nextObj))) - currentPtr = nextEnv + currentEnvironmentPtr = nextEnv } } - case _: effekt.generator.llvm.AliasInfo => - val `type` = environmentType(environment) - environment.zipWithIndex.foreach { - case (machine.Variable(name, tpe), i) => - val field = LocalReference(PointerType(), freshName(name + "_pointer")); - emit(GetElementPtr(field.name, `type`, pointer, List(0, i))); - emit(Load(name, transform(tpe), field, alias)) - } + case _: AliasInfo => + val tpe = environmentType(environment) + loadAllVariables(elementPointer, environment, alias, tpe) } } @@ -942,4 +928,13 @@ object Transformer { storeEnvironment(environmentReference, environment, Object); objectReference } + + private def loadAllVariables(pointer: Operand, environment: machine.Environment, alias: AliasInfo, typ: Type)(using ModuleContext, FunctionContext, BlockContext): Unit = { + environment.zipWithIndex.foreach { + case (machine.Variable(name, tpe), i) => + val field = LocalReference(PointerType(), freshName(name + "_pointer")) + emit(GetElementPtr(field.name, typ, pointer, List(0, i))) + emit(Load(name, transform(tpe), field, alias)) + } + } } From 679e9de9ed84dfa84c7b82d109cdb03e5e81fab6 Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 6 Nov 2025 08:24:05 +0100 Subject: [PATCH 36/57] two more test programs two_xxl_objects.effekt and two_xxxl_objects.effekt --- examples/flat/two_xxl_objects.check | 2 + examples/flat/two_xxl_objects.effekt | 312 +++ examples/flat/two_xxxl_objects.check | 2 + examples/flat/two_xxxl_objects.effekt | 3012 +++++++++++++++++++++++++ 4 files changed, 3328 insertions(+) create mode 100644 examples/flat/two_xxl_objects.check create mode 100644 examples/flat/two_xxl_objects.effekt create mode 100644 examples/flat/two_xxxl_objects.check create mode 100644 examples/flat/two_xxxl_objects.effekt diff --git a/examples/flat/two_xxl_objects.check b/examples/flat/two_xxl_objects.check new file mode 100644 index 0000000000..2a817d1436 --- /dev/null +++ b/examples/flat/two_xxl_objects.check @@ -0,0 +1,2 @@ +100 +151 \ No newline at end of file diff --git a/examples/flat/two_xxl_objects.effekt b/examples/flat/two_xxl_objects.effekt new file mode 100644 index 0000000000..7258d179af --- /dev/null +++ b/examples/flat/two_xxl_objects.effekt @@ -0,0 +1,312 @@ +// xxl: Person has 100 fields -> need x slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int, + field14: Int, + field15: Int, + field16: Int, + field17: Int, + field18: Int, + field19: Int, + field20: Int, + field21: Int, + field22: Int, + field23: Int, + field24: Int, + field25: Int, + field26: Int, + field27: Int, + field28: Int, + field29: Int, + field30: Int, + field31: Int, + field32: Int, + field33: Int, + field34: Int, + field35: Int, + field36: Int, + field37: Int, + field38: Int, + field39: Int, + field40: Int, + field41: Int, + field42: Int, + field43: Int, + field44: Int, + field45: Int, + field46: Int, + field47: Int, + field48: Int, + field49: Int, + field50: Int, + field51: Int, + field52: Int, + field53: Int, + field54: Int, + field55: Int, + field56: Int, + field57: Int, + field58: Int, + field59: Int, + field60: Int, + field61: Int, + field62: Int, + field63: Int, + field64: Int, + field65: Int, + field66: Int, + field67: Int, + field68: Int, + field69: Int, + field70: Int, + field71: Int, + field72: Int, + field73: Int, + field74: Int, + field75: Int, + field76: Int, + field77: Int, + field78: Int, + field79: Int, + field80: Int, + field81: Int, + field82: Int, + field83: Int, + field84: Int, + field85: Int, + field86: Int, + field87: Int, + field88: Int, + field89: Int, + field90: Int, + field91: Int, + field92: Int, + field93: Int, + field94: Int, + field95: Int, + field96: Int, + field97: Int, + field98: Int, + field99: Int, + field100: Int +) + +def main() = { + val p1 = Person( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100 +) + val p2 = Person( + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200 +) + println(p1.field100) + println(p2.field51) +} \ No newline at end of file diff --git a/examples/flat/two_xxxl_objects.check b/examples/flat/two_xxxl_objects.check new file mode 100644 index 0000000000..e3cf26f115 --- /dev/null +++ b/examples/flat/two_xxxl_objects.check @@ -0,0 +1,2 @@ +1000 +1999 \ No newline at end of file diff --git a/examples/flat/two_xxxl_objects.effekt b/examples/flat/two_xxxl_objects.effekt new file mode 100644 index 0000000000..5d4002bf11 --- /dev/null +++ b/examples/flat/two_xxxl_objects.effekt @@ -0,0 +1,3012 @@ +// xxxl: Person has 1000 fields -> need x slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int, + field14: Int, + field15: Int, + field16: Int, + field17: Int, + field18: Int, + field19: Int, + field20: Int, + field21: Int, + field22: Int, + field23: Int, + field24: Int, + field25: Int, + field26: Int, + field27: Int, + field28: Int, + field29: Int, + field30: Int, + field31: Int, + field32: Int, + field33: Int, + field34: Int, + field35: Int, + field36: Int, + field37: Int, + field38: Int, + field39: Int, + field40: Int, + field41: Int, + field42: Int, + field43: Int, + field44: Int, + field45: Int, + field46: Int, + field47: Int, + field48: Int, + field49: Int, + field50: Int, + field51: Int, + field52: Int, + field53: Int, + field54: Int, + field55: Int, + field56: Int, + field57: Int, + field58: Int, + field59: Int, + field60: Int, + field61: Int, + field62: Int, + field63: Int, + field64: Int, + field65: Int, + field66: Int, + field67: Int, + field68: Int, + field69: Int, + field70: Int, + field71: Int, + field72: Int, + field73: Int, + field74: Int, + field75: Int, + field76: Int, + field77: Int, + field78: Int, + field79: Int, + field80: Int, + field81: Int, + field82: Int, + field83: Int, + field84: Int, + field85: Int, + field86: Int, + field87: Int, + field88: Int, + field89: Int, + field90: Int, + field91: Int, + field92: Int, + field93: Int, + field94: Int, + field95: Int, + field96: Int, + field97: Int, + field98: Int, + field99: Int, + field100: Int, + field101: Int, + field102: Int, + field103: Int, + field104: Int, + field105: Int, + field106: Int, + field107: Int, + field108: Int, + field109: Int, + field110: Int, + field111: Int, + field112: Int, + field113: Int, + field114: Int, + field115: Int, + field116: Int, + field117: Int, + field118: Int, + field119: Int, + field120: Int, + field121: Int, + field122: Int, + field123: Int, + field124: Int, + field125: Int, + field126: Int, + field127: Int, + field128: Int, + field129: Int, + field130: Int, + field131: Int, + field132: Int, + field133: Int, + field134: Int, + field135: Int, + field136: Int, + field137: Int, + field138: Int, + field139: Int, + field140: Int, + field141: Int, + field142: Int, + field143: Int, + field144: Int, + field145: Int, + field146: Int, + field147: Int, + field148: Int, + field149: Int, + field150: Int, + field151: Int, + field152: Int, + field153: Int, + field154: Int, + field155: Int, + field156: Int, + field157: Int, + field158: Int, + field159: Int, + field160: Int, + field161: Int, + field162: Int, + field163: Int, + field164: Int, + field165: Int, + field166: Int, + field167: Int, + field168: Int, + field169: Int, + field170: Int, + field171: Int, + field172: Int, + field173: Int, + field174: Int, + field175: Int, + field176: Int, + field177: Int, + field178: Int, + field179: Int, + field180: Int, + field181: Int, + field182: Int, + field183: Int, + field184: Int, + field185: Int, + field186: Int, + field187: Int, + field188: Int, + field189: Int, + field190: Int, + field191: Int, + field192: Int, + field193: Int, + field194: Int, + field195: Int, + field196: Int, + field197: Int, + field198: Int, + field199: Int, + field200: Int, + field201: Int, + field202: Int, + field203: Int, + field204: Int, + field205: Int, + field206: Int, + field207: Int, + field208: Int, + field209: Int, + field210: Int, + field211: Int, + field212: Int, + field213: Int, + field214: Int, + field215: Int, + field216: Int, + field217: Int, + field218: Int, + field219: Int, + field220: Int, + field221: Int, + field222: Int, + field223: Int, + field224: Int, + field225: Int, + field226: Int, + field227: Int, + field228: Int, + field229: Int, + field230: Int, + field231: Int, + field232: Int, + field233: Int, + field234: Int, + field235: Int, + field236: Int, + field237: Int, + field238: Int, + field239: Int, + field240: Int, + field241: Int, + field242: Int, + field243: Int, + field244: Int, + field245: Int, + field246: Int, + field247: Int, + field248: Int, + field249: Int, + field250: Int, + field251: Int, + field252: Int, + field253: Int, + field254: Int, + field255: Int, + field256: Int, + field257: Int, + field258: Int, + field259: Int, + field260: Int, + field261: Int, + field262: Int, + field263: Int, + field264: Int, + field265: Int, + field266: Int, + field267: Int, + field268: Int, + field269: Int, + field270: Int, + field271: Int, + field272: Int, + field273: Int, + field274: Int, + field275: Int, + field276: Int, + field277: Int, + field278: Int, + field279: Int, + field280: Int, + field281: Int, + field282: Int, + field283: Int, + field284: Int, + field285: Int, + field286: Int, + field287: Int, + field288: Int, + field289: Int, + field290: Int, + field291: Int, + field292: Int, + field293: Int, + field294: Int, + field295: Int, + field296: Int, + field297: Int, + field298: Int, + field299: Int, + field300: Int, + field301: Int, + field302: Int, + field303: Int, + field304: Int, + field305: Int, + field306: Int, + field307: Int, + field308: Int, + field309: Int, + field310: Int, + field311: Int, + field312: Int, + field313: Int, + field314: Int, + field315: Int, + field316: Int, + field317: Int, + field318: Int, + field319: Int, + field320: Int, + field321: Int, + field322: Int, + field323: Int, + field324: Int, + field325: Int, + field326: Int, + field327: Int, + field328: Int, + field329: Int, + field330: Int, + field331: Int, + field332: Int, + field333: Int, + field334: Int, + field335: Int, + field336: Int, + field337: Int, + field338: Int, + field339: Int, + field340: Int, + field341: Int, + field342: Int, + field343: Int, + field344: Int, + field345: Int, + field346: Int, + field347: Int, + field348: Int, + field349: Int, + field350: Int, + field351: Int, + field352: Int, + field353: Int, + field354: Int, + field355: Int, + field356: Int, + field357: Int, + field358: Int, + field359: Int, + field360: Int, + field361: Int, + field362: Int, + field363: Int, + field364: Int, + field365: Int, + field366: Int, + field367: Int, + field368: Int, + field369: Int, + field370: Int, + field371: Int, + field372: Int, + field373: Int, + field374: Int, + field375: Int, + field376: Int, + field377: Int, + field378: Int, + field379: Int, + field380: Int, + field381: Int, + field382: Int, + field383: Int, + field384: Int, + field385: Int, + field386: Int, + field387: Int, + field388: Int, + field389: Int, + field390: Int, + field391: Int, + field392: Int, + field393: Int, + field394: Int, + field395: Int, + field396: Int, + field397: Int, + field398: Int, + field399: Int, + field400: Int, + field401: Int, + field402: Int, + field403: Int, + field404: Int, + field405: Int, + field406: Int, + field407: Int, + field408: Int, + field409: Int, + field410: Int, + field411: Int, + field412: Int, + field413: Int, + field414: Int, + field415: Int, + field416: Int, + field417: Int, + field418: Int, + field419: Int, + field420: Int, + field421: Int, + field422: Int, + field423: Int, + field424: Int, + field425: Int, + field426: Int, + field427: Int, + field428: Int, + field429: Int, + field430: Int, + field431: Int, + field432: Int, + field433: Int, + field434: Int, + field435: Int, + field436: Int, + field437: Int, + field438: Int, + field439: Int, + field440: Int, + field441: Int, + field442: Int, + field443: Int, + field444: Int, + field445: Int, + field446: Int, + field447: Int, + field448: Int, + field449: Int, + field450: Int, + field451: Int, + field452: Int, + field453: Int, + field454: Int, + field455: Int, + field456: Int, + field457: Int, + field458: Int, + field459: Int, + field460: Int, + field461: Int, + field462: Int, + field463: Int, + field464: Int, + field465: Int, + field466: Int, + field467: Int, + field468: Int, + field469: Int, + field470: Int, + field471: Int, + field472: Int, + field473: Int, + field474: Int, + field475: Int, + field476: Int, + field477: Int, + field478: Int, + field479: Int, + field480: Int, + field481: Int, + field482: Int, + field483: Int, + field484: Int, + field485: Int, + field486: Int, + field487: Int, + field488: Int, + field489: Int, + field490: Int, + field491: Int, + field492: Int, + field493: Int, + field494: Int, + field495: Int, + field496: Int, + field497: Int, + field498: Int, + field499: Int, + field500: Int, + field501: Int, + field502: Int, + field503: Int, + field504: Int, + field505: Int, + field506: Int, + field507: Int, + field508: Int, + field509: Int, + field510: Int, + field511: Int, + field512: Int, + field513: Int, + field514: Int, + field515: Int, + field516: Int, + field517: Int, + field518: Int, + field519: Int, + field520: Int, + field521: Int, + field522: Int, + field523: Int, + field524: Int, + field525: Int, + field526: Int, + field527: Int, + field528: Int, + field529: Int, + field530: Int, + field531: Int, + field532: Int, + field533: Int, + field534: Int, + field535: Int, + field536: Int, + field537: Int, + field538: Int, + field539: Int, + field540: Int, + field541: Int, + field542: Int, + field543: Int, + field544: Int, + field545: Int, + field546: Int, + field547: Int, + field548: Int, + field549: Int, + field550: Int, + field551: Int, + field552: Int, + field553: Int, + field554: Int, + field555: Int, + field556: Int, + field557: Int, + field558: Int, + field559: Int, + field560: Int, + field561: Int, + field562: Int, + field563: Int, + field564: Int, + field565: Int, + field566: Int, + field567: Int, + field568: Int, + field569: Int, + field570: Int, + field571: Int, + field572: Int, + field573: Int, + field574: Int, + field575: Int, + field576: Int, + field577: Int, + field578: Int, + field579: Int, + field580: Int, + field581: Int, + field582: Int, + field583: Int, + field584: Int, + field585: Int, + field586: Int, + field587: Int, + field588: Int, + field589: Int, + field590: Int, + field591: Int, + field592: Int, + field593: Int, + field594: Int, + field595: Int, + field596: Int, + field597: Int, + field598: Int, + field599: Int, + field600: Int, + field601: Int, + field602: Int, + field603: Int, + field604: Int, + field605: Int, + field606: Int, + field607: Int, + field608: Int, + field609: Int, + field610: Int, + field611: Int, + field612: Int, + field613: Int, + field614: Int, + field615: Int, + field616: Int, + field617: Int, + field618: Int, + field619: Int, + field620: Int, + field621: Int, + field622: Int, + field623: Int, + field624: Int, + field625: Int, + field626: Int, + field627: Int, + field628: Int, + field629: Int, + field630: Int, + field631: Int, + field632: Int, + field633: Int, + field634: Int, + field635: Int, + field636: Int, + field637: Int, + field638: Int, + field639: Int, + field640: Int, + field641: Int, + field642: Int, + field643: Int, + field644: Int, + field645: Int, + field646: Int, + field647: Int, + field648: Int, + field649: Int, + field650: Int, + field651: Int, + field652: Int, + field653: Int, + field654: Int, + field655: Int, + field656: Int, + field657: Int, + field658: Int, + field659: Int, + field660: Int, + field661: Int, + field662: Int, + field663: Int, + field664: Int, + field665: Int, + field666: Int, + field667: Int, + field668: Int, + field669: Int, + field670: Int, + field671: Int, + field672: Int, + field673: Int, + field674: Int, + field675: Int, + field676: Int, + field677: Int, + field678: Int, + field679: Int, + field680: Int, + field681: Int, + field682: Int, + field683: Int, + field684: Int, + field685: Int, + field686: Int, + field687: Int, + field688: Int, + field689: Int, + field690: Int, + field691: Int, + field692: Int, + field693: Int, + field694: Int, + field695: Int, + field696: Int, + field697: Int, + field698: Int, + field699: Int, + field700: Int, + field701: Int, + field702: Int, + field703: Int, + field704: Int, + field705: Int, + field706: Int, + field707: Int, + field708: Int, + field709: Int, + field710: Int, + field711: Int, + field712: Int, + field713: Int, + field714: Int, + field715: Int, + field716: Int, + field717: Int, + field718: Int, + field719: Int, + field720: Int, + field721: Int, + field722: Int, + field723: Int, + field724: Int, + field725: Int, + field726: Int, + field727: Int, + field728: Int, + field729: Int, + field730: Int, + field731: Int, + field732: Int, + field733: Int, + field734: Int, + field735: Int, + field736: Int, + field737: Int, + field738: Int, + field739: Int, + field740: Int, + field741: Int, + field742: Int, + field743: Int, + field744: Int, + field745: Int, + field746: Int, + field747: Int, + field748: Int, + field749: Int, + field750: Int, + field751: Int, + field752: Int, + field753: Int, + field754: Int, + field755: Int, + field756: Int, + field757: Int, + field758: Int, + field759: Int, + field760: Int, + field761: Int, + field762: Int, + field763: Int, + field764: Int, + field765: Int, + field766: Int, + field767: Int, + field768: Int, + field769: Int, + field770: Int, + field771: Int, + field772: Int, + field773: Int, + field774: Int, + field775: Int, + field776: Int, + field777: Int, + field778: Int, + field779: Int, + field780: Int, + field781: Int, + field782: Int, + field783: Int, + field784: Int, + field785: Int, + field786: Int, + field787: Int, + field788: Int, + field789: Int, + field790: Int, + field791: Int, + field792: Int, + field793: Int, + field794: Int, + field795: Int, + field796: Int, + field797: Int, + field798: Int, + field799: Int, + field800: Int, + field801: Int, + field802: Int, + field803: Int, + field804: Int, + field805: Int, + field806: Int, + field807: Int, + field808: Int, + field809: Int, + field810: Int, + field811: Int, + field812: Int, + field813: Int, + field814: Int, + field815: Int, + field816: Int, + field817: Int, + field818: Int, + field819: Int, + field820: Int, + field821: Int, + field822: Int, + field823: Int, + field824: Int, + field825: Int, + field826: Int, + field827: Int, + field828: Int, + field829: Int, + field830: Int, + field831: Int, + field832: Int, + field833: Int, + field834: Int, + field835: Int, + field836: Int, + field837: Int, + field838: Int, + field839: Int, + field840: Int, + field841: Int, + field842: Int, + field843: Int, + field844: Int, + field845: Int, + field846: Int, + field847: Int, + field848: Int, + field849: Int, + field850: Int, + field851: Int, + field852: Int, + field853: Int, + field854: Int, + field855: Int, + field856: Int, + field857: Int, + field858: Int, + field859: Int, + field860: Int, + field861: Int, + field862: Int, + field863: Int, + field864: Int, + field865: Int, + field866: Int, + field867: Int, + field868: Int, + field869: Int, + field870: Int, + field871: Int, + field872: Int, + field873: Int, + field874: Int, + field875: Int, + field876: Int, + field877: Int, + field878: Int, + field879: Int, + field880: Int, + field881: Int, + field882: Int, + field883: Int, + field884: Int, + field885: Int, + field886: Int, + field887: Int, + field888: Int, + field889: Int, + field890: Int, + field891: Int, + field892: Int, + field893: Int, + field894: Int, + field895: Int, + field896: Int, + field897: Int, + field898: Int, + field899: Int, + field900: Int, + field901: Int, + field902: Int, + field903: Int, + field904: Int, + field905: Int, + field906: Int, + field907: Int, + field908: Int, + field909: Int, + field910: Int, + field911: Int, + field912: Int, + field913: Int, + field914: Int, + field915: Int, + field916: Int, + field917: Int, + field918: Int, + field919: Int, + field920: Int, + field921: Int, + field922: Int, + field923: Int, + field924: Int, + field925: Int, + field926: Int, + field927: Int, + field928: Int, + field929: Int, + field930: Int, + field931: Int, + field932: Int, + field933: Int, + field934: Int, + field935: Int, + field936: Int, + field937: Int, + field938: Int, + field939: Int, + field940: Int, + field941: Int, + field942: Int, + field943: Int, + field944: Int, + field945: Int, + field946: Int, + field947: Int, + field948: Int, + field949: Int, + field950: Int, + field951: Int, + field952: Int, + field953: Int, + field954: Int, + field955: Int, + field956: Int, + field957: Int, + field958: Int, + field959: Int, + field960: Int, + field961: Int, + field962: Int, + field963: Int, + field964: Int, + field965: Int, + field966: Int, + field967: Int, + field968: Int, + field969: Int, + field970: Int, + field971: Int, + field972: Int, + field973: Int, + field974: Int, + field975: Int, + field976: Int, + field977: Int, + field978: Int, + field979: Int, + field980: Int, + field981: Int, + field982: Int, + field983: Int, + field984: Int, + field985: Int, + field986: Int, + field987: Int, + field988: Int, + field989: Int, + field990: Int, + field991: Int, + field992: Int, + field993: Int, + field994: Int, + field995: Int, + field996: Int, + field997: Int, + field998: Int, + field999: Int, + field1000: Int +) + +def main() = { + val p1 = Person( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500, + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 534, + 535, + 536, + 537, + 538, + 539, + 540, + 541, + 542, + 543, + 544, + 545, + 546, + 547, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 556, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 574, + 575, + 576, + 577, + 578, + 579, + 580, + 581, + 582, + 583, + 584, + 585, + 586, + 587, + 588, + 589, + 590, + 591, + 592, + 593, + 594, + 595, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 659, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 675, + 676, + 677, + 678, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 694, + 695, + 696, + 697, + 698, + 699, + 700, + 701, + 702, + 703, + 704, + 705, + 706, + 707, + 708, + 709, + 710, + 711, + 712, + 713, + 714, + 715, + 716, + 717, + 718, + 719, + 720, + 721, + 722, + 723, + 724, + 725, + 726, + 727, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 755, + 756, + 757, + 758, + 759, + 760, + 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, + 773, + 774, + 775, + 776, + 777, + 778, + 779, + 780, + 781, + 782, + 783, + 784, + 785, + 786, + 787, + 788, + 789, + 790, + 791, + 792, + 793, + 794, + 795, + 796, + 797, + 798, + 799, + 800, + 801, + 802, + 803, + 804, + 805, + 806, + 807, + 808, + 809, + 810, + 811, + 812, + 813, + 814, + 815, + 816, + 817, + 818, + 819, + 820, + 821, + 822, + 823, + 824, + 825, + 826, + 827, + 828, + 829, + 830, + 831, + 832, + 833, + 834, + 835, + 836, + 837, + 838, + 839, + 840, + 841, + 842, + 843, + 844, + 845, + 846, + 847, + 848, + 849, + 850, + 851, + 852, + 853, + 854, + 855, + 856, + 857, + 858, + 859, + 860, + 861, + 862, + 863, + 864, + 865, + 866, + 867, + 868, + 869, + 870, + 871, + 872, + 873, + 874, + 875, + 876, + 877, + 878, + 879, + 880, + 881, + 882, + 883, + 884, + 885, + 886, + 887, + 888, + 889, + 890, + 891, + 892, + 893, + 894, + 895, + 896, + 897, + 898, + 899, + 900, + 901, + 902, + 903, + 904, + 905, + 906, + 907, + 908, + 909, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 918, + 919, + 920, + 921, + 922, + 923, + 924, + 925, + 926, + 927, + 928, + 929, + 930, + 931, + 932, + 933, + 934, + 935, + 936, + 937, + 938, + 939, + 940, + 941, + 942, + 943, + 944, + 945, + 946, + 947, + 948, + 949, + 950, + 951, + 952, + 953, + 954, + 955, + 956, + 957, + 958, + 959, + 960, + 961, + 962, + 963, + 964, + 965, + 966, + 967, + 968, + 969, + 970, + 971, + 972, + 973, + 974, + 975, + 976, + 977, + 978, + 979, + 980, + 981, + 982, + 983, + 984, + 985, + 986, + 987, + 988, + 989, + 990, + 991, + 992, + 993, + 994, + 995, + 996, + 997, + 998, + 999, + 1000 +) + val p2 = Person( + 1001, + 1002, + 1003, + 1004, + 1005, + 1006, + 1007, + 1008, + 1009, + 1010, + 1011, + 1012, + 1013, + 1014, + 1015, + 1016, + 1017, + 1018, + 1019, + 1020, + 1021, + 1022, + 1023, + 1024, + 1025, + 1026, + 1027, + 1028, + 1029, + 1030, + 1031, + 1032, + 1033, + 1034, + 1035, + 1036, + 1037, + 1038, + 1039, + 1040, + 1041, + 1042, + 1043, + 1044, + 1045, + 1046, + 1047, + 1048, + 1049, + 1050, + 1051, + 1052, + 1053, + 1054, + 1055, + 1056, + 1057, + 1058, + 1059, + 1060, + 1061, + 1062, + 1063, + 1064, + 1065, + 1066, + 1067, + 1068, + 1069, + 1070, + 1071, + 1072, + 1073, + 1074, + 1075, + 1076, + 1077, + 1078, + 1079, + 1080, + 1081, + 1082, + 1083, + 1084, + 1085, + 1086, + 1087, + 1088, + 1089, + 1090, + 1091, + 1092, + 1093, + 1094, + 1095, + 1096, + 1097, + 1098, + 1099, + 1100, + 1101, + 1102, + 1103, + 1104, + 1105, + 1106, + 1107, + 1108, + 1109, + 1110, + 1111, + 1112, + 1113, + 1114, + 1115, + 1116, + 1117, + 1118, + 1119, + 1120, + 1121, + 1122, + 1123, + 1124, + 1125, + 1126, + 1127, + 1128, + 1129, + 1130, + 1131, + 1132, + 1133, + 1134, + 1135, + 1136, + 1137, + 1138, + 1139, + 1140, + 1141, + 1142, + 1143, + 1144, + 1145, + 1146, + 1147, + 1148, + 1149, + 1150, + 1151, + 1152, + 1153, + 1154, + 1155, + 1156, + 1157, + 1158, + 1159, + 1160, + 1161, + 1162, + 1163, + 1164, + 1165, + 1166, + 1167, + 1168, + 1169, + 1170, + 1171, + 1172, + 1173, + 1174, + 1175, + 1176, + 1177, + 1178, + 1179, + 1180, + 1181, + 1182, + 1183, + 1184, + 1185, + 1186, + 1187, + 1188, + 1189, + 1190, + 1191, + 1192, + 1193, + 1194, + 1195, + 1196, + 1197, + 1198, + 1199, + 1200, + 1201, + 1202, + 1203, + 1204, + 1205, + 1206, + 1207, + 1208, + 1209, + 1210, + 1211, + 1212, + 1213, + 1214, + 1215, + 1216, + 1217, + 1218, + 1219, + 1220, + 1221, + 1222, + 1223, + 1224, + 1225, + 1226, + 1227, + 1228, + 1229, + 1230, + 1231, + 1232, + 1233, + 1234, + 1235, + 1236, + 1237, + 1238, + 1239, + 1240, + 1241, + 1242, + 1243, + 1244, + 1245, + 1246, + 1247, + 1248, + 1249, + 1250, + 1251, + 1252, + 1253, + 1254, + 1255, + 1256, + 1257, + 1258, + 1259, + 1260, + 1261, + 1262, + 1263, + 1264, + 1265, + 1266, + 1267, + 1268, + 1269, + 1270, + 1271, + 1272, + 1273, + 1274, + 1275, + 1276, + 1277, + 1278, + 1279, + 1280, + 1281, + 1282, + 1283, + 1284, + 1285, + 1286, + 1287, + 1288, + 1289, + 1290, + 1291, + 1292, + 1293, + 1294, + 1295, + 1296, + 1297, + 1298, + 1299, + 1300, + 1301, + 1302, + 1303, + 1304, + 1305, + 1306, + 1307, + 1308, + 1309, + 1310, + 1311, + 1312, + 1313, + 1314, + 1315, + 1316, + 1317, + 1318, + 1319, + 1320, + 1321, + 1322, + 1323, + 1324, + 1325, + 1326, + 1327, + 1328, + 1329, + 1330, + 1331, + 1332, + 1333, + 1334, + 1335, + 1336, + 1337, + 1338, + 1339, + 1340, + 1341, + 1342, + 1343, + 1344, + 1345, + 1346, + 1347, + 1348, + 1349, + 1350, + 1351, + 1352, + 1353, + 1354, + 1355, + 1356, + 1357, + 1358, + 1359, + 1360, + 1361, + 1362, + 1363, + 1364, + 1365, + 1366, + 1367, + 1368, + 1369, + 1370, + 1371, + 1372, + 1373, + 1374, + 1375, + 1376, + 1377, + 1378, + 1379, + 1380, + 1381, + 1382, + 1383, + 1384, + 1385, + 1386, + 1387, + 1388, + 1389, + 1390, + 1391, + 1392, + 1393, + 1394, + 1395, + 1396, + 1397, + 1398, + 1399, + 1400, + 1401, + 1402, + 1403, + 1404, + 1405, + 1406, + 1407, + 1408, + 1409, + 1410, + 1411, + 1412, + 1413, + 1414, + 1415, + 1416, + 1417, + 1418, + 1419, + 1420, + 1421, + 1422, + 1423, + 1424, + 1425, + 1426, + 1427, + 1428, + 1429, + 1430, + 1431, + 1432, + 1433, + 1434, + 1435, + 1436, + 1437, + 1438, + 1439, + 1440, + 1441, + 1442, + 1443, + 1444, + 1445, + 1446, + 1447, + 1448, + 1449, + 1450, + 1451, + 1452, + 1453, + 1454, + 1455, + 1456, + 1457, + 1458, + 1459, + 1460, + 1461, + 1462, + 1463, + 1464, + 1465, + 1466, + 1467, + 1468, + 1469, + 1470, + 1471, + 1472, + 1473, + 1474, + 1475, + 1476, + 1477, + 1478, + 1479, + 1480, + 1481, + 1482, + 1483, + 1484, + 1485, + 1486, + 1487, + 1488, + 1489, + 1490, + 1491, + 1492, + 1493, + 1494, + 1495, + 1496, + 1497, + 1498, + 1499, + 1500, + 1501, + 1502, + 1503, + 1504, + 1505, + 1506, + 1507, + 1508, + 1509, + 1510, + 1511, + 1512, + 1513, + 1514, + 1515, + 1516, + 1517, + 1518, + 1519, + 1520, + 1521, + 1522, + 1523, + 1524, + 1525, + 1526, + 1527, + 1528, + 1529, + 1530, + 1531, + 1532, + 1533, + 1534, + 1535, + 1536, + 1537, + 1538, + 1539, + 1540, + 1541, + 1542, + 1543, + 1544, + 1545, + 1546, + 1547, + 1548, + 1549, + 1550, + 1551, + 1552, + 1553, + 1554, + 1555, + 1556, + 1557, + 1558, + 1559, + 1560, + 1561, + 1562, + 1563, + 1564, + 1565, + 1566, + 1567, + 1568, + 1569, + 1570, + 1571, + 1572, + 1573, + 1574, + 1575, + 1576, + 1577, + 1578, + 1579, + 1580, + 1581, + 1582, + 1583, + 1584, + 1585, + 1586, + 1587, + 1588, + 1589, + 1590, + 1591, + 1592, + 1593, + 1594, + 1595, + 1596, + 1597, + 1598, + 1599, + 1600, + 1601, + 1602, + 1603, + 1604, + 1605, + 1606, + 1607, + 1608, + 1609, + 1610, + 1611, + 1612, + 1613, + 1614, + 1615, + 1616, + 1617, + 1618, + 1619, + 1620, + 1621, + 1622, + 1623, + 1624, + 1625, + 1626, + 1627, + 1628, + 1629, + 1630, + 1631, + 1632, + 1633, + 1634, + 1635, + 1636, + 1637, + 1638, + 1639, + 1640, + 1641, + 1642, + 1643, + 1644, + 1645, + 1646, + 1647, + 1648, + 1649, + 1650, + 1651, + 1652, + 1653, + 1654, + 1655, + 1656, + 1657, + 1658, + 1659, + 1660, + 1661, + 1662, + 1663, + 1664, + 1665, + 1666, + 1667, + 1668, + 1669, + 1670, + 1671, + 1672, + 1673, + 1674, + 1675, + 1676, + 1677, + 1678, + 1679, + 1680, + 1681, + 1682, + 1683, + 1684, + 1685, + 1686, + 1687, + 1688, + 1689, + 1690, + 1691, + 1692, + 1693, + 1694, + 1695, + 1696, + 1697, + 1698, + 1699, + 1700, + 1701, + 1702, + 1703, + 1704, + 1705, + 1706, + 1707, + 1708, + 1709, + 1710, + 1711, + 1712, + 1713, + 1714, + 1715, + 1716, + 1717, + 1718, + 1719, + 1720, + 1721, + 1722, + 1723, + 1724, + 1725, + 1726, + 1727, + 1728, + 1729, + 1730, + 1731, + 1732, + 1733, + 1734, + 1735, + 1736, + 1737, + 1738, + 1739, + 1740, + 1741, + 1742, + 1743, + 1744, + 1745, + 1746, + 1747, + 1748, + 1749, + 1750, + 1751, + 1752, + 1753, + 1754, + 1755, + 1756, + 1757, + 1758, + 1759, + 1760, + 1761, + 1762, + 1763, + 1764, + 1765, + 1766, + 1767, + 1768, + 1769, + 1770, + 1771, + 1772, + 1773, + 1774, + 1775, + 1776, + 1777, + 1778, + 1779, + 1780, + 1781, + 1782, + 1783, + 1784, + 1785, + 1786, + 1787, + 1788, + 1789, + 1790, + 1791, + 1792, + 1793, + 1794, + 1795, + 1796, + 1797, + 1798, + 1799, + 1800, + 1801, + 1802, + 1803, + 1804, + 1805, + 1806, + 1807, + 1808, + 1809, + 1810, + 1811, + 1812, + 1813, + 1814, + 1815, + 1816, + 1817, + 1818, + 1819, + 1820, + 1821, + 1822, + 1823, + 1824, + 1825, + 1826, + 1827, + 1828, + 1829, + 1830, + 1831, + 1832, + 1833, + 1834, + 1835, + 1836, + 1837, + 1838, + 1839, + 1840, + 1841, + 1842, + 1843, + 1844, + 1845, + 1846, + 1847, + 1848, + 1849, + 1850, + 1851, + 1852, + 1853, + 1854, + 1855, + 1856, + 1857, + 1858, + 1859, + 1860, + 1861, + 1862, + 1863, + 1864, + 1865, + 1866, + 1867, + 1868, + 1869, + 1870, + 1871, + 1872, + 1873, + 1874, + 1875, + 1876, + 1877, + 1878, + 1879, + 1880, + 1881, + 1882, + 1883, + 1884, + 1885, + 1886, + 1887, + 1888, + 1889, + 1890, + 1891, + 1892, + 1893, + 1894, + 1895, + 1896, + 1897, + 1898, + 1899, + 1900, + 1901, + 1902, + 1903, + 1904, + 1905, + 1906, + 1907, + 1908, + 1909, + 1910, + 1911, + 1912, + 1913, + 1914, + 1915, + 1916, + 1917, + 1918, + 1919, + 1920, + 1921, + 1922, + 1923, + 1924, + 1925, + 1926, + 1927, + 1928, + 1929, + 1930, + 1931, + 1932, + 1933, + 1934, + 1935, + 1936, + 1937, + 1938, + 1939, + 1940, + 1941, + 1942, + 1943, + 1944, + 1945, + 1946, + 1947, + 1948, + 1949, + 1950, + 1951, + 1952, + 1953, + 1954, + 1955, + 1956, + 1957, + 1958, + 1959, + 1960, + 1961, + 1962, + 1963, + 1964, + 1965, + 1966, + 1967, + 1968, + 1969, + 1970, + 1971, + 1972, + 1973, + 1974, + 1975, + 1976, + 1977, + 1978, + 1979, + 1980, + 1981, + 1982, + 1983, + 1984, + 1985, + 1986, + 1987, + 1988, + 1989, + 1990, + 1991, + 1992, + 1993, + 1994, + 1995, + 1996, + 1997, + 1998, + 1999, + 2000 +) + println(p1.field1000) + println(p2.field999) +} \ No newline at end of file From 29f27b84d84b677c7765fcac0cacc5bdd84dd644 Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 6 Nov 2025 08:24:37 +0100 Subject: [PATCH 37/57] short review with Philipp on 05.11.2025 of splitted allocation approach --- .../src/main/scala/effekt/generator/llvm/Transformer.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 07669812b7..d63070c465 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -908,7 +908,7 @@ object Transformer { val extendedEnv = envChunk :+ machine.Variable(linkValName, machine.Positive()) // ...finally, we do the usual behavior when we produce an object - headObject = produceSingleObject(role, extendedEnv, freeInBody).asInstanceOf[LocalReference] + headObject = produceSingleObject(role, extendedEnv, freeInBody) } headObject } @@ -916,7 +916,7 @@ object Transformer { /** * Creates a new object and stores its environment in it */ - private def produceSingleObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { + private def produceSingleObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): LocalReference = { val objectReference = LocalReference(objectType, freshName(role)) val environmentReference = LocalReference(environmentType, freshName("environment")) val size = ConstantInt(environmentSize(environment)); From 329cffaca1b25beee987cf028c132840309fd8f0 Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 6 Nov 2025 08:24:57 +0100 Subject: [PATCH 38/57] comment adjusted --- libraries/llvm/cMalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 834bd54e8a..11699526d2 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -19,7 +19,7 @@ static const bool DEBUG = false; static Block* freeList = NULL; // Head of the Freelist static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage -static const int blockSize = 64; // The size of each block (128B) +static const int blockSize = 64; // The size of each block (64B) // How much storage do we allocate at the beginning of a program? =4GB static const size_t chunkSize = (size_t)4294967296ULL; From 2cd235f000abf689cc203a9882100ab724e3cf4b Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 6 Nov 2025 15:00:08 +0100 Subject: [PATCH 39/57] more test programs --- .../flat/{ => nooptimized}/array_record.check | 0 .../{ => nooptimized}/array_record.effekt | 0 .../{ => nooptimized}/nested_objects.check | 0 .../{ => nooptimized}/nested_objects.effekt | 0 .../{ => nooptimized}/one_lg_object.check | 0 .../{ => nooptimized}/one_lg_object.effekt | 0 .../{ => nooptimized}/one_md_object.check | 0 .../{ => nooptimized}/one_md_object.effekt | 0 .../{ => nooptimized}/one_sm_object.check | 0 .../{ => nooptimized}/one_sm_object.effekt | 0 .../{ => nooptimized}/one_xl_object.check | 0 .../{ => nooptimized}/one_xl_object.effekt | 0 .../{ => nooptimized}/one_xs_object.check | 0 .../{ => nooptimized}/one_xs_object.effekt | 0 .../one_xxl_object_all_fields.check | 100 + .../one_xxl_object_all_fields.effekt | 308 ++ .../flat/nooptimized/one_xxxl_object.check | 1 + .../flat/nooptimized/one_xxxl_object.effekt | 1008 ++++++ .../{ => nooptimized}/two_lg_objects.check | 0 .../{ => nooptimized}/two_lg_objects.effekt | 0 .../{ => nooptimized}/two_md_objects.check | 0 .../{ => nooptimized}/two_md_objects.effekt | 0 .../{ => nooptimized}/two_sm_objects.check | 0 .../{ => nooptimized}/two_sm_objects.effekt | 0 .../{ => nooptimized}/two_xl_objects.check | 0 .../{ => nooptimized}/two_xl_objects.effekt | 0 .../{ => nooptimized}/two_xs_objects.check | 0 .../{ => nooptimized}/two_xs_objects.effekt | 0 .../two_xxl_object_all_fields.check | 100 + .../two_xxl_object_all_fields.effekt | 308 ++ .../{ => nooptimized}/two_xxl_objects.check | 0 .../{ => nooptimized}/two_xxl_objects.effekt | 0 .../flat/nooptimized/two_xxxl_objects.check | 2 + .../flat/nooptimized/two_xxxl_objects.effekt | 1512 +++++++++ examples/flat/two_xxxl_objects.check | 2 - examples/flat/two_xxxl_objects.effekt | 3012 ----------------- 36 files changed, 3339 insertions(+), 3014 deletions(-) rename examples/flat/{ => nooptimized}/array_record.check (100%) rename examples/flat/{ => nooptimized}/array_record.effekt (100%) rename examples/flat/{ => nooptimized}/nested_objects.check (100%) rename examples/flat/{ => nooptimized}/nested_objects.effekt (100%) rename examples/flat/{ => nooptimized}/one_lg_object.check (100%) rename examples/flat/{ => nooptimized}/one_lg_object.effekt (100%) rename examples/flat/{ => nooptimized}/one_md_object.check (100%) rename examples/flat/{ => nooptimized}/one_md_object.effekt (100%) rename examples/flat/{ => nooptimized}/one_sm_object.check (100%) rename examples/flat/{ => nooptimized}/one_sm_object.effekt (100%) rename examples/flat/{ => nooptimized}/one_xl_object.check (100%) rename examples/flat/{ => nooptimized}/one_xl_object.effekt (100%) rename examples/flat/{ => nooptimized}/one_xs_object.check (100%) rename examples/flat/{ => nooptimized}/one_xs_object.effekt (100%) create mode 100644 examples/flat/nooptimized/one_xxl_object_all_fields.check create mode 100644 examples/flat/nooptimized/one_xxl_object_all_fields.effekt create mode 100644 examples/flat/nooptimized/one_xxxl_object.check create mode 100644 examples/flat/nooptimized/one_xxxl_object.effekt rename examples/flat/{ => nooptimized}/two_lg_objects.check (100%) rename examples/flat/{ => nooptimized}/two_lg_objects.effekt (100%) rename examples/flat/{ => nooptimized}/two_md_objects.check (100%) rename examples/flat/{ => nooptimized}/two_md_objects.effekt (100%) rename examples/flat/{ => nooptimized}/two_sm_objects.check (100%) rename examples/flat/{ => nooptimized}/two_sm_objects.effekt (100%) rename examples/flat/{ => nooptimized}/two_xl_objects.check (100%) rename examples/flat/{ => nooptimized}/two_xl_objects.effekt (100%) rename examples/flat/{ => nooptimized}/two_xs_objects.check (100%) rename examples/flat/{ => nooptimized}/two_xs_objects.effekt (100%) create mode 100644 examples/flat/nooptimized/two_xxl_object_all_fields.check create mode 100644 examples/flat/nooptimized/two_xxl_object_all_fields.effekt rename examples/flat/{ => nooptimized}/two_xxl_objects.check (100%) rename examples/flat/{ => nooptimized}/two_xxl_objects.effekt (100%) create mode 100644 examples/flat/nooptimized/two_xxxl_objects.check create mode 100644 examples/flat/nooptimized/two_xxxl_objects.effekt delete mode 100644 examples/flat/two_xxxl_objects.check delete mode 100644 examples/flat/two_xxxl_objects.effekt diff --git a/examples/flat/array_record.check b/examples/flat/nooptimized/array_record.check similarity index 100% rename from examples/flat/array_record.check rename to examples/flat/nooptimized/array_record.check diff --git a/examples/flat/array_record.effekt b/examples/flat/nooptimized/array_record.effekt similarity index 100% rename from examples/flat/array_record.effekt rename to examples/flat/nooptimized/array_record.effekt diff --git a/examples/flat/nested_objects.check b/examples/flat/nooptimized/nested_objects.check similarity index 100% rename from examples/flat/nested_objects.check rename to examples/flat/nooptimized/nested_objects.check diff --git a/examples/flat/nested_objects.effekt b/examples/flat/nooptimized/nested_objects.effekt similarity index 100% rename from examples/flat/nested_objects.effekt rename to examples/flat/nooptimized/nested_objects.effekt diff --git a/examples/flat/one_lg_object.check b/examples/flat/nooptimized/one_lg_object.check similarity index 100% rename from examples/flat/one_lg_object.check rename to examples/flat/nooptimized/one_lg_object.check diff --git a/examples/flat/one_lg_object.effekt b/examples/flat/nooptimized/one_lg_object.effekt similarity index 100% rename from examples/flat/one_lg_object.effekt rename to examples/flat/nooptimized/one_lg_object.effekt diff --git a/examples/flat/one_md_object.check b/examples/flat/nooptimized/one_md_object.check similarity index 100% rename from examples/flat/one_md_object.check rename to examples/flat/nooptimized/one_md_object.check diff --git a/examples/flat/one_md_object.effekt b/examples/flat/nooptimized/one_md_object.effekt similarity index 100% rename from examples/flat/one_md_object.effekt rename to examples/flat/nooptimized/one_md_object.effekt diff --git a/examples/flat/one_sm_object.check b/examples/flat/nooptimized/one_sm_object.check similarity index 100% rename from examples/flat/one_sm_object.check rename to examples/flat/nooptimized/one_sm_object.check diff --git a/examples/flat/one_sm_object.effekt b/examples/flat/nooptimized/one_sm_object.effekt similarity index 100% rename from examples/flat/one_sm_object.effekt rename to examples/flat/nooptimized/one_sm_object.effekt diff --git a/examples/flat/one_xl_object.check b/examples/flat/nooptimized/one_xl_object.check similarity index 100% rename from examples/flat/one_xl_object.check rename to examples/flat/nooptimized/one_xl_object.check diff --git a/examples/flat/one_xl_object.effekt b/examples/flat/nooptimized/one_xl_object.effekt similarity index 100% rename from examples/flat/one_xl_object.effekt rename to examples/flat/nooptimized/one_xl_object.effekt diff --git a/examples/flat/one_xs_object.check b/examples/flat/nooptimized/one_xs_object.check similarity index 100% rename from examples/flat/one_xs_object.check rename to examples/flat/nooptimized/one_xs_object.check diff --git a/examples/flat/one_xs_object.effekt b/examples/flat/nooptimized/one_xs_object.effekt similarity index 100% rename from examples/flat/one_xs_object.effekt rename to examples/flat/nooptimized/one_xs_object.effekt diff --git a/examples/flat/nooptimized/one_xxl_object_all_fields.check b/examples/flat/nooptimized/one_xxl_object_all_fields.check new file mode 100644 index 0000000000..6a0bcb8711 --- /dev/null +++ b/examples/flat/nooptimized/one_xxl_object_all_fields.check @@ -0,0 +1,100 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 \ No newline at end of file diff --git a/examples/flat/nooptimized/one_xxl_object_all_fields.effekt b/examples/flat/nooptimized/one_xxl_object_all_fields.effekt new file mode 100644 index 0000000000..824a4b6038 --- /dev/null +++ b/examples/flat/nooptimized/one_xxl_object_all_fields.effekt @@ -0,0 +1,308 @@ +// xxl: Person has 100 fields -> need x slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int, + field14: Int, + field15: Int, + field16: Int, + field17: Int, + field18: Int, + field19: Int, + field20: Int, + field21: Int, + field22: Int, + field23: Int, + field24: Int, + field25: Int, + field26: Int, + field27: Int, + field28: Int, + field29: Int, + field30: Int, + field31: Int, + field32: Int, + field33: Int, + field34: Int, + field35: Int, + field36: Int, + field37: Int, + field38: Int, + field39: Int, + field40: Int, + field41: Int, + field42: Int, + field43: Int, + field44: Int, + field45: Int, + field46: Int, + field47: Int, + field48: Int, + field49: Int, + field50: Int, + field51: Int, + field52: Int, + field53: Int, + field54: Int, + field55: Int, + field56: Int, + field57: Int, + field58: Int, + field59: Int, + field60: Int, + field61: Int, + field62: Int, + field63: Int, + field64: Int, + field65: Int, + field66: Int, + field67: Int, + field68: Int, + field69: Int, + field70: Int, + field71: Int, + field72: Int, + field73: Int, + field74: Int, + field75: Int, + field76: Int, + field77: Int, + field78: Int, + field79: Int, + field80: Int, + field81: Int, + field82: Int, + field83: Int, + field84: Int, + field85: Int, + field86: Int, + field87: Int, + field88: Int, + field89: Int, + field90: Int, + field91: Int, + field92: Int, + field93: Int, + field94: Int, + field95: Int, + field96: Int, + field97: Int, + field98: Int, + field99: Int, + field100: Int +) + +def main() = { + val p1 = Person( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100 +) + println(p1.field1) + println(p1.field2) + println(p1.field3) + println(p1.field4) + println(p1.field5) + println(p1.field6) + println(p1.field7) + println(p1.field8) + println(p1.field9) + println(p1.field10) + println(p1.field11) + println(p1.field12) + println(p1.field13) + println(p1.field14) + println(p1.field15) + println(p1.field16) + println(p1.field17) + println(p1.field18) + println(p1.field19) + println(p1.field20) + println(p1.field21) + println(p1.field22) + println(p1.field23) + println(p1.field24) + println(p1.field25) + println(p1.field26) + println(p1.field27) + println(p1.field28) + println(p1.field29) + println(p1.field30) + println(p1.field31) + println(p1.field32) + println(p1.field33) + println(p1.field34) + println(p1.field35) + println(p1.field36) + println(p1.field37) + println(p1.field38) + println(p1.field39) + println(p1.field40) + println(p1.field41) + println(p1.field42) + println(p1.field43) + println(p1.field44) + println(p1.field45) + println(p1.field46) + println(p1.field47) + println(p1.field48) + println(p1.field49) + println(p1.field50) + println(p1.field51) + println(p1.field52) + println(p1.field53) + println(p1.field54) + println(p1.field55) + println(p1.field56) + println(p1.field57) + println(p1.field58) + println(p1.field59) + println(p1.field60) + println(p1.field61) + println(p1.field62) + println(p1.field63) + println(p1.field64) + println(p1.field65) + println(p1.field66) + println(p1.field67) + println(p1.field68) + println(p1.field69) + println(p1.field70) + println(p1.field71) + println(p1.field72) + println(p1.field73) + println(p1.field74) + println(p1.field75) + println(p1.field76) + println(p1.field77) + println(p1.field78) + println(p1.field79) + println(p1.field80) + println(p1.field81) + println(p1.field82) + println(p1.field83) + println(p1.field84) + println(p1.field85) + println(p1.field86) + println(p1.field87) + println(p1.field88) + println(p1.field89) + println(p1.field90) + println(p1.field91) + println(p1.field92) + println(p1.field93) + println(p1.field94) + println(p1.field95) + println(p1.field96) + println(p1.field97) + println(p1.field98) + println(p1.field99) + println(p1.field100) +} \ No newline at end of file diff --git a/examples/flat/nooptimized/one_xxxl_object.check b/examples/flat/nooptimized/one_xxxl_object.check new file mode 100644 index 0000000000..eb1f49486a --- /dev/null +++ b/examples/flat/nooptimized/one_xxxl_object.check @@ -0,0 +1 @@ +500 \ No newline at end of file diff --git a/examples/flat/nooptimized/one_xxxl_object.effekt b/examples/flat/nooptimized/one_xxxl_object.effekt new file mode 100644 index 0000000000..74211407a9 --- /dev/null +++ b/examples/flat/nooptimized/one_xxxl_object.effekt @@ -0,0 +1,1008 @@ +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int, + field14: Int, + field15: Int, + field16: Int, + field17: Int, + field18: Int, + field19: Int, + field20: Int, + field21: Int, + field22: Int, + field23: Int, + field24: Int, + field25: Int, + field26: Int, + field27: Int, + field28: Int, + field29: Int, + field30: Int, + field31: Int, + field32: Int, + field33: Int, + field34: Int, + field35: Int, + field36: Int, + field37: Int, + field38: Int, + field39: Int, + field40: Int, + field41: Int, + field42: Int, + field43: Int, + field44: Int, + field45: Int, + field46: Int, + field47: Int, + field48: Int, + field49: Int, + field50: Int, + field51: Int, + field52: Int, + field53: Int, + field54: Int, + field55: Int, + field56: Int, + field57: Int, + field58: Int, + field59: Int, + field60: Int, + field61: Int, + field62: Int, + field63: Int, + field64: Int, + field65: Int, + field66: Int, + field67: Int, + field68: Int, + field69: Int, + field70: Int, + field71: Int, + field72: Int, + field73: Int, + field74: Int, + field75: Int, + field76: Int, + field77: Int, + field78: Int, + field79: Int, + field80: Int, + field81: Int, + field82: Int, + field83: Int, + field84: Int, + field85: Int, + field86: Int, + field87: Int, + field88: Int, + field89: Int, + field90: Int, + field91: Int, + field92: Int, + field93: Int, + field94: Int, + field95: Int, + field96: Int, + field97: Int, + field98: Int, + field99: Int, + field100: Int, + field101: Int, + field102: Int, + field103: Int, + field104: Int, + field105: Int, + field106: Int, + field107: Int, + field108: Int, + field109: Int, + field110: Int, + field111: Int, + field112: Int, + field113: Int, + field114: Int, + field115: Int, + field116: Int, + field117: Int, + field118: Int, + field119: Int, + field120: Int, + field121: Int, + field122: Int, + field123: Int, + field124: Int, + field125: Int, + field126: Int, + field127: Int, + field128: Int, + field129: Int, + field130: Int, + field131: Int, + field132: Int, + field133: Int, + field134: Int, + field135: Int, + field136: Int, + field137: Int, + field138: Int, + field139: Int, + field140: Int, + field141: Int, + field142: Int, + field143: Int, + field144: Int, + field145: Int, + field146: Int, + field147: Int, + field148: Int, + field149: Int, + field150: Int, + field151: Int, + field152: Int, + field153: Int, + field154: Int, + field155: Int, + field156: Int, + field157: Int, + field158: Int, + field159: Int, + field160: Int, + field161: Int, + field162: Int, + field163: Int, + field164: Int, + field165: Int, + field166: Int, + field167: Int, + field168: Int, + field169: Int, + field170: Int, + field171: Int, + field172: Int, + field173: Int, + field174: Int, + field175: Int, + field176: Int, + field177: Int, + field178: Int, + field179: Int, + field180: Int, + field181: Int, + field182: Int, + field183: Int, + field184: Int, + field185: Int, + field186: Int, + field187: Int, + field188: Int, + field189: Int, + field190: Int, + field191: Int, + field192: Int, + field193: Int, + field194: Int, + field195: Int, + field196: Int, + field197: Int, + field198: Int, + field199: Int, + field200: Int, + field201: Int, + field202: Int, + field203: Int, + field204: Int, + field205: Int, + field206: Int, + field207: Int, + field208: Int, + field209: Int, + field210: Int, + field211: Int, + field212: Int, + field213: Int, + field214: Int, + field215: Int, + field216: Int, + field217: Int, + field218: Int, + field219: Int, + field220: Int, + field221: Int, + field222: Int, + field223: Int, + field224: Int, + field225: Int, + field226: Int, + field227: Int, + field228: Int, + field229: Int, + field230: Int, + field231: Int, + field232: Int, + field233: Int, + field234: Int, + field235: Int, + field236: Int, + field237: Int, + field238: Int, + field239: Int, + field240: Int, + field241: Int, + field242: Int, + field243: Int, + field244: Int, + field245: Int, + field246: Int, + field247: Int, + field248: Int, + field249: Int, + field250: Int, + field251: Int, + field252: Int, + field253: Int, + field254: Int, + field255: Int, + field256: Int, + field257: Int, + field258: Int, + field259: Int, + field260: Int, + field261: Int, + field262: Int, + field263: Int, + field264: Int, + field265: Int, + field266: Int, + field267: Int, + field268: Int, + field269: Int, + field270: Int, + field271: Int, + field272: Int, + field273: Int, + field274: Int, + field275: Int, + field276: Int, + field277: Int, + field278: Int, + field279: Int, + field280: Int, + field281: Int, + field282: Int, + field283: Int, + field284: Int, + field285: Int, + field286: Int, + field287: Int, + field288: Int, + field289: Int, + field290: Int, + field291: Int, + field292: Int, + field293: Int, + field294: Int, + field295: Int, + field296: Int, + field297: Int, + field298: Int, + field299: Int, + field300: Int, + field301: Int, + field302: Int, + field303: Int, + field304: Int, + field305: Int, + field306: Int, + field307: Int, + field308: Int, + field309: Int, + field310: Int, + field311: Int, + field312: Int, + field313: Int, + field314: Int, + field315: Int, + field316: Int, + field317: Int, + field318: Int, + field319: Int, + field320: Int, + field321: Int, + field322: Int, + field323: Int, + field324: Int, + field325: Int, + field326: Int, + field327: Int, + field328: Int, + field329: Int, + field330: Int, + field331: Int, + field332: Int, + field333: Int, + field334: Int, + field335: Int, + field336: Int, + field337: Int, + field338: Int, + field339: Int, + field340: Int, + field341: Int, + field342: Int, + field343: Int, + field344: Int, + field345: Int, + field346: Int, + field347: Int, + field348: Int, + field349: Int, + field350: Int, + field351: Int, + field352: Int, + field353: Int, + field354: Int, + field355: Int, + field356: Int, + field357: Int, + field358: Int, + field359: Int, + field360: Int, + field361: Int, + field362: Int, + field363: Int, + field364: Int, + field365: Int, + field366: Int, + field367: Int, + field368: Int, + field369: Int, + field370: Int, + field371: Int, + field372: Int, + field373: Int, + field374: Int, + field375: Int, + field376: Int, + field377: Int, + field378: Int, + field379: Int, + field380: Int, + field381: Int, + field382: Int, + field383: Int, + field384: Int, + field385: Int, + field386: Int, + field387: Int, + field388: Int, + field389: Int, + field390: Int, + field391: Int, + field392: Int, + field393: Int, + field394: Int, + field395: Int, + field396: Int, + field397: Int, + field398: Int, + field399: Int, + field400: Int, + field401: Int, + field402: Int, + field403: Int, + field404: Int, + field405: Int, + field406: Int, + field407: Int, + field408: Int, + field409: Int, + field410: Int, + field411: Int, + field412: Int, + field413: Int, + field414: Int, + field415: Int, + field416: Int, + field417: Int, + field418: Int, + field419: Int, + field420: Int, + field421: Int, + field422: Int, + field423: Int, + field424: Int, + field425: Int, + field426: Int, + field427: Int, + field428: Int, + field429: Int, + field430: Int, + field431: Int, + field432: Int, + field433: Int, + field434: Int, + field435: Int, + field436: Int, + field437: Int, + field438: Int, + field439: Int, + field440: Int, + field441: Int, + field442: Int, + field443: Int, + field444: Int, + field445: Int, + field446: Int, + field447: Int, + field448: Int, + field449: Int, + field450: Int, + field451: Int, + field452: Int, + field453: Int, + field454: Int, + field455: Int, + field456: Int, + field457: Int, + field458: Int, + field459: Int, + field460: Int, + field461: Int, + field462: Int, + field463: Int, + field464: Int, + field465: Int, + field466: Int, + field467: Int, + field468: Int, + field469: Int, + field470: Int, + field471: Int, + field472: Int, + field473: Int, + field474: Int, + field475: Int, + field476: Int, + field477: Int, + field478: Int, + field479: Int, + field480: Int, + field481: Int, + field482: Int, + field483: Int, + field484: Int, + field485: Int, + field486: Int, + field487: Int, + field488: Int, + field489: Int, + field490: Int, + field491: Int, + field492: Int, + field493: Int, + field494: Int, + field495: Int, + field496: Int, + field497: Int, + field498: Int, + field499: Int, + field500: Int +) + +def main() = { + val p1 = Person( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500 +) + println(p1.field500) +} \ No newline at end of file diff --git a/examples/flat/two_lg_objects.check b/examples/flat/nooptimized/two_lg_objects.check similarity index 100% rename from examples/flat/two_lg_objects.check rename to examples/flat/nooptimized/two_lg_objects.check diff --git a/examples/flat/two_lg_objects.effekt b/examples/flat/nooptimized/two_lg_objects.effekt similarity index 100% rename from examples/flat/two_lg_objects.effekt rename to examples/flat/nooptimized/two_lg_objects.effekt diff --git a/examples/flat/two_md_objects.check b/examples/flat/nooptimized/two_md_objects.check similarity index 100% rename from examples/flat/two_md_objects.check rename to examples/flat/nooptimized/two_md_objects.check diff --git a/examples/flat/two_md_objects.effekt b/examples/flat/nooptimized/two_md_objects.effekt similarity index 100% rename from examples/flat/two_md_objects.effekt rename to examples/flat/nooptimized/two_md_objects.effekt diff --git a/examples/flat/two_sm_objects.check b/examples/flat/nooptimized/two_sm_objects.check similarity index 100% rename from examples/flat/two_sm_objects.check rename to examples/flat/nooptimized/two_sm_objects.check diff --git a/examples/flat/two_sm_objects.effekt b/examples/flat/nooptimized/two_sm_objects.effekt similarity index 100% rename from examples/flat/two_sm_objects.effekt rename to examples/flat/nooptimized/two_sm_objects.effekt diff --git a/examples/flat/two_xl_objects.check b/examples/flat/nooptimized/two_xl_objects.check similarity index 100% rename from examples/flat/two_xl_objects.check rename to examples/flat/nooptimized/two_xl_objects.check diff --git a/examples/flat/two_xl_objects.effekt b/examples/flat/nooptimized/two_xl_objects.effekt similarity index 100% rename from examples/flat/two_xl_objects.effekt rename to examples/flat/nooptimized/two_xl_objects.effekt diff --git a/examples/flat/two_xs_objects.check b/examples/flat/nooptimized/two_xs_objects.check similarity index 100% rename from examples/flat/two_xs_objects.check rename to examples/flat/nooptimized/two_xs_objects.check diff --git a/examples/flat/two_xs_objects.effekt b/examples/flat/nooptimized/two_xs_objects.effekt similarity index 100% rename from examples/flat/two_xs_objects.effekt rename to examples/flat/nooptimized/two_xs_objects.effekt diff --git a/examples/flat/nooptimized/two_xxl_object_all_fields.check b/examples/flat/nooptimized/two_xxl_object_all_fields.check new file mode 100644 index 0000000000..6a0bcb8711 --- /dev/null +++ b/examples/flat/nooptimized/two_xxl_object_all_fields.check @@ -0,0 +1,100 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 \ No newline at end of file diff --git a/examples/flat/nooptimized/two_xxl_object_all_fields.effekt b/examples/flat/nooptimized/two_xxl_object_all_fields.effekt new file mode 100644 index 0000000000..824a4b6038 --- /dev/null +++ b/examples/flat/nooptimized/two_xxl_object_all_fields.effekt @@ -0,0 +1,308 @@ +// xxl: Person has 100 fields -> need x slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int, + field14: Int, + field15: Int, + field16: Int, + field17: Int, + field18: Int, + field19: Int, + field20: Int, + field21: Int, + field22: Int, + field23: Int, + field24: Int, + field25: Int, + field26: Int, + field27: Int, + field28: Int, + field29: Int, + field30: Int, + field31: Int, + field32: Int, + field33: Int, + field34: Int, + field35: Int, + field36: Int, + field37: Int, + field38: Int, + field39: Int, + field40: Int, + field41: Int, + field42: Int, + field43: Int, + field44: Int, + field45: Int, + field46: Int, + field47: Int, + field48: Int, + field49: Int, + field50: Int, + field51: Int, + field52: Int, + field53: Int, + field54: Int, + field55: Int, + field56: Int, + field57: Int, + field58: Int, + field59: Int, + field60: Int, + field61: Int, + field62: Int, + field63: Int, + field64: Int, + field65: Int, + field66: Int, + field67: Int, + field68: Int, + field69: Int, + field70: Int, + field71: Int, + field72: Int, + field73: Int, + field74: Int, + field75: Int, + field76: Int, + field77: Int, + field78: Int, + field79: Int, + field80: Int, + field81: Int, + field82: Int, + field83: Int, + field84: Int, + field85: Int, + field86: Int, + field87: Int, + field88: Int, + field89: Int, + field90: Int, + field91: Int, + field92: Int, + field93: Int, + field94: Int, + field95: Int, + field96: Int, + field97: Int, + field98: Int, + field99: Int, + field100: Int +) + +def main() = { + val p1 = Person( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100 +) + println(p1.field1) + println(p1.field2) + println(p1.field3) + println(p1.field4) + println(p1.field5) + println(p1.field6) + println(p1.field7) + println(p1.field8) + println(p1.field9) + println(p1.field10) + println(p1.field11) + println(p1.field12) + println(p1.field13) + println(p1.field14) + println(p1.field15) + println(p1.field16) + println(p1.field17) + println(p1.field18) + println(p1.field19) + println(p1.field20) + println(p1.field21) + println(p1.field22) + println(p1.field23) + println(p1.field24) + println(p1.field25) + println(p1.field26) + println(p1.field27) + println(p1.field28) + println(p1.field29) + println(p1.field30) + println(p1.field31) + println(p1.field32) + println(p1.field33) + println(p1.field34) + println(p1.field35) + println(p1.field36) + println(p1.field37) + println(p1.field38) + println(p1.field39) + println(p1.field40) + println(p1.field41) + println(p1.field42) + println(p1.field43) + println(p1.field44) + println(p1.field45) + println(p1.field46) + println(p1.field47) + println(p1.field48) + println(p1.field49) + println(p1.field50) + println(p1.field51) + println(p1.field52) + println(p1.field53) + println(p1.field54) + println(p1.field55) + println(p1.field56) + println(p1.field57) + println(p1.field58) + println(p1.field59) + println(p1.field60) + println(p1.field61) + println(p1.field62) + println(p1.field63) + println(p1.field64) + println(p1.field65) + println(p1.field66) + println(p1.field67) + println(p1.field68) + println(p1.field69) + println(p1.field70) + println(p1.field71) + println(p1.field72) + println(p1.field73) + println(p1.field74) + println(p1.field75) + println(p1.field76) + println(p1.field77) + println(p1.field78) + println(p1.field79) + println(p1.field80) + println(p1.field81) + println(p1.field82) + println(p1.field83) + println(p1.field84) + println(p1.field85) + println(p1.field86) + println(p1.field87) + println(p1.field88) + println(p1.field89) + println(p1.field90) + println(p1.field91) + println(p1.field92) + println(p1.field93) + println(p1.field94) + println(p1.field95) + println(p1.field96) + println(p1.field97) + println(p1.field98) + println(p1.field99) + println(p1.field100) +} \ No newline at end of file diff --git a/examples/flat/two_xxl_objects.check b/examples/flat/nooptimized/two_xxl_objects.check similarity index 100% rename from examples/flat/two_xxl_objects.check rename to examples/flat/nooptimized/two_xxl_objects.check diff --git a/examples/flat/two_xxl_objects.effekt b/examples/flat/nooptimized/two_xxl_objects.effekt similarity index 100% rename from examples/flat/two_xxl_objects.effekt rename to examples/flat/nooptimized/two_xxl_objects.effekt diff --git a/examples/flat/nooptimized/two_xxxl_objects.check b/examples/flat/nooptimized/two_xxxl_objects.check new file mode 100644 index 0000000000..f10f03bb2e --- /dev/null +++ b/examples/flat/nooptimized/two_xxxl_objects.check @@ -0,0 +1,2 @@ +500 +999 \ No newline at end of file diff --git a/examples/flat/nooptimized/two_xxxl_objects.effekt b/examples/flat/nooptimized/two_xxxl_objects.effekt new file mode 100644 index 0000000000..0e3ee0511e --- /dev/null +++ b/examples/flat/nooptimized/two_xxxl_objects.effekt @@ -0,0 +1,1512 @@ +// xxxl: Person has 500 fields -> need x slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int, + field14: Int, + field15: Int, + field16: Int, + field17: Int, + field18: Int, + field19: Int, + field20: Int, + field21: Int, + field22: Int, + field23: Int, + field24: Int, + field25: Int, + field26: Int, + field27: Int, + field28: Int, + field29: Int, + field30: Int, + field31: Int, + field32: Int, + field33: Int, + field34: Int, + field35: Int, + field36: Int, + field37: Int, + field38: Int, + field39: Int, + field40: Int, + field41: Int, + field42: Int, + field43: Int, + field44: Int, + field45: Int, + field46: Int, + field47: Int, + field48: Int, + field49: Int, + field50: Int, + field51: Int, + field52: Int, + field53: Int, + field54: Int, + field55: Int, + field56: Int, + field57: Int, + field58: Int, + field59: Int, + field60: Int, + field61: Int, + field62: Int, + field63: Int, + field64: Int, + field65: Int, + field66: Int, + field67: Int, + field68: Int, + field69: Int, + field70: Int, + field71: Int, + field72: Int, + field73: Int, + field74: Int, + field75: Int, + field76: Int, + field77: Int, + field78: Int, + field79: Int, + field80: Int, + field81: Int, + field82: Int, + field83: Int, + field84: Int, + field85: Int, + field86: Int, + field87: Int, + field88: Int, + field89: Int, + field90: Int, + field91: Int, + field92: Int, + field93: Int, + field94: Int, + field95: Int, + field96: Int, + field97: Int, + field98: Int, + field99: Int, + field100: Int, + field101: Int, + field102: Int, + field103: Int, + field104: Int, + field105: Int, + field106: Int, + field107: Int, + field108: Int, + field109: Int, + field110: Int, + field111: Int, + field112: Int, + field113: Int, + field114: Int, + field115: Int, + field116: Int, + field117: Int, + field118: Int, + field119: Int, + field120: Int, + field121: Int, + field122: Int, + field123: Int, + field124: Int, + field125: Int, + field126: Int, + field127: Int, + field128: Int, + field129: Int, + field130: Int, + field131: Int, + field132: Int, + field133: Int, + field134: Int, + field135: Int, + field136: Int, + field137: Int, + field138: Int, + field139: Int, + field140: Int, + field141: Int, + field142: Int, + field143: Int, + field144: Int, + field145: Int, + field146: Int, + field147: Int, + field148: Int, + field149: Int, + field150: Int, + field151: Int, + field152: Int, + field153: Int, + field154: Int, + field155: Int, + field156: Int, + field157: Int, + field158: Int, + field159: Int, + field160: Int, + field161: Int, + field162: Int, + field163: Int, + field164: Int, + field165: Int, + field166: Int, + field167: Int, + field168: Int, + field169: Int, + field170: Int, + field171: Int, + field172: Int, + field173: Int, + field174: Int, + field175: Int, + field176: Int, + field177: Int, + field178: Int, + field179: Int, + field180: Int, + field181: Int, + field182: Int, + field183: Int, + field184: Int, + field185: Int, + field186: Int, + field187: Int, + field188: Int, + field189: Int, + field190: Int, + field191: Int, + field192: Int, + field193: Int, + field194: Int, + field195: Int, + field196: Int, + field197: Int, + field198: Int, + field199: Int, + field200: Int, + field201: Int, + field202: Int, + field203: Int, + field204: Int, + field205: Int, + field206: Int, + field207: Int, + field208: Int, + field209: Int, + field210: Int, + field211: Int, + field212: Int, + field213: Int, + field214: Int, + field215: Int, + field216: Int, + field217: Int, + field218: Int, + field219: Int, + field220: Int, + field221: Int, + field222: Int, + field223: Int, + field224: Int, + field225: Int, + field226: Int, + field227: Int, + field228: Int, + field229: Int, + field230: Int, + field231: Int, + field232: Int, + field233: Int, + field234: Int, + field235: Int, + field236: Int, + field237: Int, + field238: Int, + field239: Int, + field240: Int, + field241: Int, + field242: Int, + field243: Int, + field244: Int, + field245: Int, + field246: Int, + field247: Int, + field248: Int, + field249: Int, + field250: Int, + field251: Int, + field252: Int, + field253: Int, + field254: Int, + field255: Int, + field256: Int, + field257: Int, + field258: Int, + field259: Int, + field260: Int, + field261: Int, + field262: Int, + field263: Int, + field264: Int, + field265: Int, + field266: Int, + field267: Int, + field268: Int, + field269: Int, + field270: Int, + field271: Int, + field272: Int, + field273: Int, + field274: Int, + field275: Int, + field276: Int, + field277: Int, + field278: Int, + field279: Int, + field280: Int, + field281: Int, + field282: Int, + field283: Int, + field284: Int, + field285: Int, + field286: Int, + field287: Int, + field288: Int, + field289: Int, + field290: Int, + field291: Int, + field292: Int, + field293: Int, + field294: Int, + field295: Int, + field296: Int, + field297: Int, + field298: Int, + field299: Int, + field300: Int, + field301: Int, + field302: Int, + field303: Int, + field304: Int, + field305: Int, + field306: Int, + field307: Int, + field308: Int, + field309: Int, + field310: Int, + field311: Int, + field312: Int, + field313: Int, + field314: Int, + field315: Int, + field316: Int, + field317: Int, + field318: Int, + field319: Int, + field320: Int, + field321: Int, + field322: Int, + field323: Int, + field324: Int, + field325: Int, + field326: Int, + field327: Int, + field328: Int, + field329: Int, + field330: Int, + field331: Int, + field332: Int, + field333: Int, + field334: Int, + field335: Int, + field336: Int, + field337: Int, + field338: Int, + field339: Int, + field340: Int, + field341: Int, + field342: Int, + field343: Int, + field344: Int, + field345: Int, + field346: Int, + field347: Int, + field348: Int, + field349: Int, + field350: Int, + field351: Int, + field352: Int, + field353: Int, + field354: Int, + field355: Int, + field356: Int, + field357: Int, + field358: Int, + field359: Int, + field360: Int, + field361: Int, + field362: Int, + field363: Int, + field364: Int, + field365: Int, + field366: Int, + field367: Int, + field368: Int, + field369: Int, + field370: Int, + field371: Int, + field372: Int, + field373: Int, + field374: Int, + field375: Int, + field376: Int, + field377: Int, + field378: Int, + field379: Int, + field380: Int, + field381: Int, + field382: Int, + field383: Int, + field384: Int, + field385: Int, + field386: Int, + field387: Int, + field388: Int, + field389: Int, + field390: Int, + field391: Int, + field392: Int, + field393: Int, + field394: Int, + field395: Int, + field396: Int, + field397: Int, + field398: Int, + field399: Int, + field400: Int, + field401: Int, + field402: Int, + field403: Int, + field404: Int, + field405: Int, + field406: Int, + field407: Int, + field408: Int, + field409: Int, + field410: Int, + field411: Int, + field412: Int, + field413: Int, + field414: Int, + field415: Int, + field416: Int, + field417: Int, + field418: Int, + field419: Int, + field420: Int, + field421: Int, + field422: Int, + field423: Int, + field424: Int, + field425: Int, + field426: Int, + field427: Int, + field428: Int, + field429: Int, + field430: Int, + field431: Int, + field432: Int, + field433: Int, + field434: Int, + field435: Int, + field436: Int, + field437: Int, + field438: Int, + field439: Int, + field440: Int, + field441: Int, + field442: Int, + field443: Int, + field444: Int, + field445: Int, + field446: Int, + field447: Int, + field448: Int, + field449: Int, + field450: Int, + field451: Int, + field452: Int, + field453: Int, + field454: Int, + field455: Int, + field456: Int, + field457: Int, + field458: Int, + field459: Int, + field460: Int, + field461: Int, + field462: Int, + field463: Int, + field464: Int, + field465: Int, + field466: Int, + field467: Int, + field468: Int, + field469: Int, + field470: Int, + field471: Int, + field472: Int, + field473: Int, + field474: Int, + field475: Int, + field476: Int, + field477: Int, + field478: Int, + field479: Int, + field480: Int, + field481: Int, + field482: Int, + field483: Int, + field484: Int, + field485: Int, + field486: Int, + field487: Int, + field488: Int, + field489: Int, + field490: Int, + field491: Int, + field492: Int, + field493: Int, + field494: Int, + field495: Int, + field496: Int, + field497: Int, + field498: Int, + field499: Int, + field500: Int +) + +def main() = { + val p1 = Person( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400, + 401, + 402, + 403, + 404, + 405, + 406, + 407, + 408, + 409, + 410, + 411, + 412, + 413, + 414, + 415, + 416, + 417, + 418, + 419, + 420, + 421, + 422, + 423, + 424, + 425, + 426, + 427, + 428, + 429, + 430, + 431, + 432, + 433, + 434, + 435, + 436, + 437, + 438, + 439, + 440, + 441, + 442, + 443, + 444, + 445, + 446, + 447, + 448, + 449, + 450, + 451, + 452, + 453, + 454, + 455, + 456, + 457, + 458, + 459, + 460, + 461, + 462, + 463, + 464, + 465, + 466, + 467, + 468, + 469, + 470, + 471, + 472, + 473, + 474, + 475, + 476, + 477, + 478, + 479, + 480, + 481, + 482, + 483, + 484, + 485, + 486, + 487, + 488, + 489, + 490, + 491, + 492, + 493, + 494, + 495, + 496, + 497, + 498, + 499, + 500 +) + val p2 = Person( + 501, + 502, + 503, + 504, + 505, + 506, + 507, + 508, + 509, + 510, + 511, + 512, + 513, + 514, + 515, + 516, + 517, + 518, + 519, + 520, + 521, + 522, + 523, + 524, + 525, + 526, + 527, + 528, + 529, + 530, + 531, + 532, + 533, + 534, + 535, + 536, + 537, + 538, + 539, + 540, + 541, + 542, + 543, + 544, + 545, + 546, + 547, + 548, + 549, + 550, + 551, + 552, + 553, + 554, + 555, + 556, + 557, + 558, + 559, + 560, + 561, + 562, + 563, + 564, + 565, + 566, + 567, + 568, + 569, + 570, + 571, + 572, + 573, + 574, + 575, + 576, + 577, + 578, + 579, + 580, + 581, + 582, + 583, + 584, + 585, + 586, + 587, + 588, + 589, + 590, + 591, + 592, + 593, + 594, + 595, + 596, + 597, + 598, + 599, + 600, + 601, + 602, + 603, + 604, + 605, + 606, + 607, + 608, + 609, + 610, + 611, + 612, + 613, + 614, + 615, + 616, + 617, + 618, + 619, + 620, + 621, + 622, + 623, + 624, + 625, + 626, + 627, + 628, + 629, + 630, + 631, + 632, + 633, + 634, + 635, + 636, + 637, + 638, + 639, + 640, + 641, + 642, + 643, + 644, + 645, + 646, + 647, + 648, + 649, + 650, + 651, + 652, + 653, + 654, + 655, + 656, + 657, + 658, + 659, + 660, + 661, + 662, + 663, + 664, + 665, + 666, + 667, + 668, + 669, + 670, + 671, + 672, + 673, + 674, + 675, + 676, + 677, + 678, + 679, + 680, + 681, + 682, + 683, + 684, + 685, + 686, + 687, + 688, + 689, + 690, + 691, + 692, + 693, + 694, + 695, + 696, + 697, + 698, + 699, + 700, + 701, + 702, + 703, + 704, + 705, + 706, + 707, + 708, + 709, + 710, + 711, + 712, + 713, + 714, + 715, + 716, + 717, + 718, + 719, + 720, + 721, + 722, + 723, + 724, + 725, + 726, + 727, + 728, + 729, + 730, + 731, + 732, + 733, + 734, + 735, + 736, + 737, + 738, + 739, + 740, + 741, + 742, + 743, + 744, + 745, + 746, + 747, + 748, + 749, + 750, + 751, + 752, + 753, + 754, + 755, + 756, + 757, + 758, + 759, + 760, + 761, + 762, + 763, + 764, + 765, + 766, + 767, + 768, + 769, + 770, + 771, + 772, + 773, + 774, + 775, + 776, + 777, + 778, + 779, + 780, + 781, + 782, + 783, + 784, + 785, + 786, + 787, + 788, + 789, + 790, + 791, + 792, + 793, + 794, + 795, + 796, + 797, + 798, + 799, + 800, + 801, + 802, + 803, + 804, + 805, + 806, + 807, + 808, + 809, + 810, + 811, + 812, + 813, + 814, + 815, + 816, + 817, + 818, + 819, + 820, + 821, + 822, + 823, + 824, + 825, + 826, + 827, + 828, + 829, + 830, + 831, + 832, + 833, + 834, + 835, + 836, + 837, + 838, + 839, + 840, + 841, + 842, + 843, + 844, + 845, + 846, + 847, + 848, + 849, + 850, + 851, + 852, + 853, + 854, + 855, + 856, + 857, + 858, + 859, + 860, + 861, + 862, + 863, + 864, + 865, + 866, + 867, + 868, + 869, + 870, + 871, + 872, + 873, + 874, + 875, + 876, + 877, + 878, + 879, + 880, + 881, + 882, + 883, + 884, + 885, + 886, + 887, + 888, + 889, + 890, + 891, + 892, + 893, + 894, + 895, + 896, + 897, + 898, + 899, + 900, + 901, + 902, + 903, + 904, + 905, + 906, + 907, + 908, + 909, + 910, + 911, + 912, + 913, + 914, + 915, + 916, + 917, + 918, + 919, + 920, + 921, + 922, + 923, + 924, + 925, + 926, + 927, + 928, + 929, + 930, + 931, + 932, + 933, + 934, + 935, + 936, + 937, + 938, + 939, + 940, + 941, + 942, + 943, + 944, + 945, + 946, + 947, + 948, + 949, + 950, + 951, + 952, + 953, + 954, + 955, + 956, + 957, + 958, + 959, + 960, + 961, + 962, + 963, + 964, + 965, + 966, + 967, + 968, + 969, + 970, + 971, + 972, + 973, + 974, + 975, + 976, + 977, + 978, + 979, + 980, + 981, + 982, + 983, + 984, + 985, + 986, + 987, + 988, + 989, + 990, + 991, + 992, + 993, + 994, + 995, + 996, + 997, + 998, + 999, + 1000 +) + println(p1.field500) + println(p2.field499) +} \ No newline at end of file diff --git a/examples/flat/two_xxxl_objects.check b/examples/flat/two_xxxl_objects.check deleted file mode 100644 index e3cf26f115..0000000000 --- a/examples/flat/two_xxxl_objects.check +++ /dev/null @@ -1,2 +0,0 @@ -1000 -1999 \ No newline at end of file diff --git a/examples/flat/two_xxxl_objects.effekt b/examples/flat/two_xxxl_objects.effekt deleted file mode 100644 index 5d4002bf11..0000000000 --- a/examples/flat/two_xxxl_objects.effekt +++ /dev/null @@ -1,3012 +0,0 @@ -// xxxl: Person has 1000 fields -> need x slots -record Person( - field1: Int, - field2: Int, - field3: Int, - field4: Int, - field5: Int, - field6: Int, - field7: Int, - field8: Int, - field9: Int, - field10: Int, - field11: Int, - field12: Int, - field13: Int, - field14: Int, - field15: Int, - field16: Int, - field17: Int, - field18: Int, - field19: Int, - field20: Int, - field21: Int, - field22: Int, - field23: Int, - field24: Int, - field25: Int, - field26: Int, - field27: Int, - field28: Int, - field29: Int, - field30: Int, - field31: Int, - field32: Int, - field33: Int, - field34: Int, - field35: Int, - field36: Int, - field37: Int, - field38: Int, - field39: Int, - field40: Int, - field41: Int, - field42: Int, - field43: Int, - field44: Int, - field45: Int, - field46: Int, - field47: Int, - field48: Int, - field49: Int, - field50: Int, - field51: Int, - field52: Int, - field53: Int, - field54: Int, - field55: Int, - field56: Int, - field57: Int, - field58: Int, - field59: Int, - field60: Int, - field61: Int, - field62: Int, - field63: Int, - field64: Int, - field65: Int, - field66: Int, - field67: Int, - field68: Int, - field69: Int, - field70: Int, - field71: Int, - field72: Int, - field73: Int, - field74: Int, - field75: Int, - field76: Int, - field77: Int, - field78: Int, - field79: Int, - field80: Int, - field81: Int, - field82: Int, - field83: Int, - field84: Int, - field85: Int, - field86: Int, - field87: Int, - field88: Int, - field89: Int, - field90: Int, - field91: Int, - field92: Int, - field93: Int, - field94: Int, - field95: Int, - field96: Int, - field97: Int, - field98: Int, - field99: Int, - field100: Int, - field101: Int, - field102: Int, - field103: Int, - field104: Int, - field105: Int, - field106: Int, - field107: Int, - field108: Int, - field109: Int, - field110: Int, - field111: Int, - field112: Int, - field113: Int, - field114: Int, - field115: Int, - field116: Int, - field117: Int, - field118: Int, - field119: Int, - field120: Int, - field121: Int, - field122: Int, - field123: Int, - field124: Int, - field125: Int, - field126: Int, - field127: Int, - field128: Int, - field129: Int, - field130: Int, - field131: Int, - field132: Int, - field133: Int, - field134: Int, - field135: Int, - field136: Int, - field137: Int, - field138: Int, - field139: Int, - field140: Int, - field141: Int, - field142: Int, - field143: Int, - field144: Int, - field145: Int, - field146: Int, - field147: Int, - field148: Int, - field149: Int, - field150: Int, - field151: Int, - field152: Int, - field153: Int, - field154: Int, - field155: Int, - field156: Int, - field157: Int, - field158: Int, - field159: Int, - field160: Int, - field161: Int, - field162: Int, - field163: Int, - field164: Int, - field165: Int, - field166: Int, - field167: Int, - field168: Int, - field169: Int, - field170: Int, - field171: Int, - field172: Int, - field173: Int, - field174: Int, - field175: Int, - field176: Int, - field177: Int, - field178: Int, - field179: Int, - field180: Int, - field181: Int, - field182: Int, - field183: Int, - field184: Int, - field185: Int, - field186: Int, - field187: Int, - field188: Int, - field189: Int, - field190: Int, - field191: Int, - field192: Int, - field193: Int, - field194: Int, - field195: Int, - field196: Int, - field197: Int, - field198: Int, - field199: Int, - field200: Int, - field201: Int, - field202: Int, - field203: Int, - field204: Int, - field205: Int, - field206: Int, - field207: Int, - field208: Int, - field209: Int, - field210: Int, - field211: Int, - field212: Int, - field213: Int, - field214: Int, - field215: Int, - field216: Int, - field217: Int, - field218: Int, - field219: Int, - field220: Int, - field221: Int, - field222: Int, - field223: Int, - field224: Int, - field225: Int, - field226: Int, - field227: Int, - field228: Int, - field229: Int, - field230: Int, - field231: Int, - field232: Int, - field233: Int, - field234: Int, - field235: Int, - field236: Int, - field237: Int, - field238: Int, - field239: Int, - field240: Int, - field241: Int, - field242: Int, - field243: Int, - field244: Int, - field245: Int, - field246: Int, - field247: Int, - field248: Int, - field249: Int, - field250: Int, - field251: Int, - field252: Int, - field253: Int, - field254: Int, - field255: Int, - field256: Int, - field257: Int, - field258: Int, - field259: Int, - field260: Int, - field261: Int, - field262: Int, - field263: Int, - field264: Int, - field265: Int, - field266: Int, - field267: Int, - field268: Int, - field269: Int, - field270: Int, - field271: Int, - field272: Int, - field273: Int, - field274: Int, - field275: Int, - field276: Int, - field277: Int, - field278: Int, - field279: Int, - field280: Int, - field281: Int, - field282: Int, - field283: Int, - field284: Int, - field285: Int, - field286: Int, - field287: Int, - field288: Int, - field289: Int, - field290: Int, - field291: Int, - field292: Int, - field293: Int, - field294: Int, - field295: Int, - field296: Int, - field297: Int, - field298: Int, - field299: Int, - field300: Int, - field301: Int, - field302: Int, - field303: Int, - field304: Int, - field305: Int, - field306: Int, - field307: Int, - field308: Int, - field309: Int, - field310: Int, - field311: Int, - field312: Int, - field313: Int, - field314: Int, - field315: Int, - field316: Int, - field317: Int, - field318: Int, - field319: Int, - field320: Int, - field321: Int, - field322: Int, - field323: Int, - field324: Int, - field325: Int, - field326: Int, - field327: Int, - field328: Int, - field329: Int, - field330: Int, - field331: Int, - field332: Int, - field333: Int, - field334: Int, - field335: Int, - field336: Int, - field337: Int, - field338: Int, - field339: Int, - field340: Int, - field341: Int, - field342: Int, - field343: Int, - field344: Int, - field345: Int, - field346: Int, - field347: Int, - field348: Int, - field349: Int, - field350: Int, - field351: Int, - field352: Int, - field353: Int, - field354: Int, - field355: Int, - field356: Int, - field357: Int, - field358: Int, - field359: Int, - field360: Int, - field361: Int, - field362: Int, - field363: Int, - field364: Int, - field365: Int, - field366: Int, - field367: Int, - field368: Int, - field369: Int, - field370: Int, - field371: Int, - field372: Int, - field373: Int, - field374: Int, - field375: Int, - field376: Int, - field377: Int, - field378: Int, - field379: Int, - field380: Int, - field381: Int, - field382: Int, - field383: Int, - field384: Int, - field385: Int, - field386: Int, - field387: Int, - field388: Int, - field389: Int, - field390: Int, - field391: Int, - field392: Int, - field393: Int, - field394: Int, - field395: Int, - field396: Int, - field397: Int, - field398: Int, - field399: Int, - field400: Int, - field401: Int, - field402: Int, - field403: Int, - field404: Int, - field405: Int, - field406: Int, - field407: Int, - field408: Int, - field409: Int, - field410: Int, - field411: Int, - field412: Int, - field413: Int, - field414: Int, - field415: Int, - field416: Int, - field417: Int, - field418: Int, - field419: Int, - field420: Int, - field421: Int, - field422: Int, - field423: Int, - field424: Int, - field425: Int, - field426: Int, - field427: Int, - field428: Int, - field429: Int, - field430: Int, - field431: Int, - field432: Int, - field433: Int, - field434: Int, - field435: Int, - field436: Int, - field437: Int, - field438: Int, - field439: Int, - field440: Int, - field441: Int, - field442: Int, - field443: Int, - field444: Int, - field445: Int, - field446: Int, - field447: Int, - field448: Int, - field449: Int, - field450: Int, - field451: Int, - field452: Int, - field453: Int, - field454: Int, - field455: Int, - field456: Int, - field457: Int, - field458: Int, - field459: Int, - field460: Int, - field461: Int, - field462: Int, - field463: Int, - field464: Int, - field465: Int, - field466: Int, - field467: Int, - field468: Int, - field469: Int, - field470: Int, - field471: Int, - field472: Int, - field473: Int, - field474: Int, - field475: Int, - field476: Int, - field477: Int, - field478: Int, - field479: Int, - field480: Int, - field481: Int, - field482: Int, - field483: Int, - field484: Int, - field485: Int, - field486: Int, - field487: Int, - field488: Int, - field489: Int, - field490: Int, - field491: Int, - field492: Int, - field493: Int, - field494: Int, - field495: Int, - field496: Int, - field497: Int, - field498: Int, - field499: Int, - field500: Int, - field501: Int, - field502: Int, - field503: Int, - field504: Int, - field505: Int, - field506: Int, - field507: Int, - field508: Int, - field509: Int, - field510: Int, - field511: Int, - field512: Int, - field513: Int, - field514: Int, - field515: Int, - field516: Int, - field517: Int, - field518: Int, - field519: Int, - field520: Int, - field521: Int, - field522: Int, - field523: Int, - field524: Int, - field525: Int, - field526: Int, - field527: Int, - field528: Int, - field529: Int, - field530: Int, - field531: Int, - field532: Int, - field533: Int, - field534: Int, - field535: Int, - field536: Int, - field537: Int, - field538: Int, - field539: Int, - field540: Int, - field541: Int, - field542: Int, - field543: Int, - field544: Int, - field545: Int, - field546: Int, - field547: Int, - field548: Int, - field549: Int, - field550: Int, - field551: Int, - field552: Int, - field553: Int, - field554: Int, - field555: Int, - field556: Int, - field557: Int, - field558: Int, - field559: Int, - field560: Int, - field561: Int, - field562: Int, - field563: Int, - field564: Int, - field565: Int, - field566: Int, - field567: Int, - field568: Int, - field569: Int, - field570: Int, - field571: Int, - field572: Int, - field573: Int, - field574: Int, - field575: Int, - field576: Int, - field577: Int, - field578: Int, - field579: Int, - field580: Int, - field581: Int, - field582: Int, - field583: Int, - field584: Int, - field585: Int, - field586: Int, - field587: Int, - field588: Int, - field589: Int, - field590: Int, - field591: Int, - field592: Int, - field593: Int, - field594: Int, - field595: Int, - field596: Int, - field597: Int, - field598: Int, - field599: Int, - field600: Int, - field601: Int, - field602: Int, - field603: Int, - field604: Int, - field605: Int, - field606: Int, - field607: Int, - field608: Int, - field609: Int, - field610: Int, - field611: Int, - field612: Int, - field613: Int, - field614: Int, - field615: Int, - field616: Int, - field617: Int, - field618: Int, - field619: Int, - field620: Int, - field621: Int, - field622: Int, - field623: Int, - field624: Int, - field625: Int, - field626: Int, - field627: Int, - field628: Int, - field629: Int, - field630: Int, - field631: Int, - field632: Int, - field633: Int, - field634: Int, - field635: Int, - field636: Int, - field637: Int, - field638: Int, - field639: Int, - field640: Int, - field641: Int, - field642: Int, - field643: Int, - field644: Int, - field645: Int, - field646: Int, - field647: Int, - field648: Int, - field649: Int, - field650: Int, - field651: Int, - field652: Int, - field653: Int, - field654: Int, - field655: Int, - field656: Int, - field657: Int, - field658: Int, - field659: Int, - field660: Int, - field661: Int, - field662: Int, - field663: Int, - field664: Int, - field665: Int, - field666: Int, - field667: Int, - field668: Int, - field669: Int, - field670: Int, - field671: Int, - field672: Int, - field673: Int, - field674: Int, - field675: Int, - field676: Int, - field677: Int, - field678: Int, - field679: Int, - field680: Int, - field681: Int, - field682: Int, - field683: Int, - field684: Int, - field685: Int, - field686: Int, - field687: Int, - field688: Int, - field689: Int, - field690: Int, - field691: Int, - field692: Int, - field693: Int, - field694: Int, - field695: Int, - field696: Int, - field697: Int, - field698: Int, - field699: Int, - field700: Int, - field701: Int, - field702: Int, - field703: Int, - field704: Int, - field705: Int, - field706: Int, - field707: Int, - field708: Int, - field709: Int, - field710: Int, - field711: Int, - field712: Int, - field713: Int, - field714: Int, - field715: Int, - field716: Int, - field717: Int, - field718: Int, - field719: Int, - field720: Int, - field721: Int, - field722: Int, - field723: Int, - field724: Int, - field725: Int, - field726: Int, - field727: Int, - field728: Int, - field729: Int, - field730: Int, - field731: Int, - field732: Int, - field733: Int, - field734: Int, - field735: Int, - field736: Int, - field737: Int, - field738: Int, - field739: Int, - field740: Int, - field741: Int, - field742: Int, - field743: Int, - field744: Int, - field745: Int, - field746: Int, - field747: Int, - field748: Int, - field749: Int, - field750: Int, - field751: Int, - field752: Int, - field753: Int, - field754: Int, - field755: Int, - field756: Int, - field757: Int, - field758: Int, - field759: Int, - field760: Int, - field761: Int, - field762: Int, - field763: Int, - field764: Int, - field765: Int, - field766: Int, - field767: Int, - field768: Int, - field769: Int, - field770: Int, - field771: Int, - field772: Int, - field773: Int, - field774: Int, - field775: Int, - field776: Int, - field777: Int, - field778: Int, - field779: Int, - field780: Int, - field781: Int, - field782: Int, - field783: Int, - field784: Int, - field785: Int, - field786: Int, - field787: Int, - field788: Int, - field789: Int, - field790: Int, - field791: Int, - field792: Int, - field793: Int, - field794: Int, - field795: Int, - field796: Int, - field797: Int, - field798: Int, - field799: Int, - field800: Int, - field801: Int, - field802: Int, - field803: Int, - field804: Int, - field805: Int, - field806: Int, - field807: Int, - field808: Int, - field809: Int, - field810: Int, - field811: Int, - field812: Int, - field813: Int, - field814: Int, - field815: Int, - field816: Int, - field817: Int, - field818: Int, - field819: Int, - field820: Int, - field821: Int, - field822: Int, - field823: Int, - field824: Int, - field825: Int, - field826: Int, - field827: Int, - field828: Int, - field829: Int, - field830: Int, - field831: Int, - field832: Int, - field833: Int, - field834: Int, - field835: Int, - field836: Int, - field837: Int, - field838: Int, - field839: Int, - field840: Int, - field841: Int, - field842: Int, - field843: Int, - field844: Int, - field845: Int, - field846: Int, - field847: Int, - field848: Int, - field849: Int, - field850: Int, - field851: Int, - field852: Int, - field853: Int, - field854: Int, - field855: Int, - field856: Int, - field857: Int, - field858: Int, - field859: Int, - field860: Int, - field861: Int, - field862: Int, - field863: Int, - field864: Int, - field865: Int, - field866: Int, - field867: Int, - field868: Int, - field869: Int, - field870: Int, - field871: Int, - field872: Int, - field873: Int, - field874: Int, - field875: Int, - field876: Int, - field877: Int, - field878: Int, - field879: Int, - field880: Int, - field881: Int, - field882: Int, - field883: Int, - field884: Int, - field885: Int, - field886: Int, - field887: Int, - field888: Int, - field889: Int, - field890: Int, - field891: Int, - field892: Int, - field893: Int, - field894: Int, - field895: Int, - field896: Int, - field897: Int, - field898: Int, - field899: Int, - field900: Int, - field901: Int, - field902: Int, - field903: Int, - field904: Int, - field905: Int, - field906: Int, - field907: Int, - field908: Int, - field909: Int, - field910: Int, - field911: Int, - field912: Int, - field913: Int, - field914: Int, - field915: Int, - field916: Int, - field917: Int, - field918: Int, - field919: Int, - field920: Int, - field921: Int, - field922: Int, - field923: Int, - field924: Int, - field925: Int, - field926: Int, - field927: Int, - field928: Int, - field929: Int, - field930: Int, - field931: Int, - field932: Int, - field933: Int, - field934: Int, - field935: Int, - field936: Int, - field937: Int, - field938: Int, - field939: Int, - field940: Int, - field941: Int, - field942: Int, - field943: Int, - field944: Int, - field945: Int, - field946: Int, - field947: Int, - field948: Int, - field949: Int, - field950: Int, - field951: Int, - field952: Int, - field953: Int, - field954: Int, - field955: Int, - field956: Int, - field957: Int, - field958: Int, - field959: Int, - field960: Int, - field961: Int, - field962: Int, - field963: Int, - field964: Int, - field965: Int, - field966: Int, - field967: Int, - field968: Int, - field969: Int, - field970: Int, - field971: Int, - field972: Int, - field973: Int, - field974: Int, - field975: Int, - field976: Int, - field977: Int, - field978: Int, - field979: Int, - field980: Int, - field981: Int, - field982: Int, - field983: Int, - field984: Int, - field985: Int, - field986: Int, - field987: Int, - field988: Int, - field989: Int, - field990: Int, - field991: Int, - field992: Int, - field993: Int, - field994: Int, - field995: Int, - field996: Int, - field997: Int, - field998: Int, - field999: Int, - field1000: Int -) - -def main() = { - val p1 = Person( - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - 62, - 63, - 64, - 65, - 66, - 67, - 68, - 69, - 70, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 80, - 81, - 82, - 83, - 84, - 85, - 86, - 87, - 88, - 89, - 90, - 91, - 92, - 93, - 94, - 95, - 96, - 97, - 98, - 99, - 100, - 101, - 102, - 103, - 104, - 105, - 106, - 107, - 108, - 109, - 110, - 111, - 112, - 113, - 114, - 115, - 116, - 117, - 118, - 119, - 120, - 121, - 122, - 123, - 124, - 125, - 126, - 127, - 128, - 129, - 130, - 131, - 132, - 133, - 134, - 135, - 136, - 137, - 138, - 139, - 140, - 141, - 142, - 143, - 144, - 145, - 146, - 147, - 148, - 149, - 150, - 151, - 152, - 153, - 154, - 155, - 156, - 157, - 158, - 159, - 160, - 161, - 162, - 163, - 164, - 165, - 166, - 167, - 168, - 169, - 170, - 171, - 172, - 173, - 174, - 175, - 176, - 177, - 178, - 179, - 180, - 181, - 182, - 183, - 184, - 185, - 186, - 187, - 188, - 189, - 190, - 191, - 192, - 193, - 194, - 195, - 196, - 197, - 198, - 199, - 200, - 201, - 202, - 203, - 204, - 205, - 206, - 207, - 208, - 209, - 210, - 211, - 212, - 213, - 214, - 215, - 216, - 217, - 218, - 219, - 220, - 221, - 222, - 223, - 224, - 225, - 226, - 227, - 228, - 229, - 230, - 231, - 232, - 233, - 234, - 235, - 236, - 237, - 238, - 239, - 240, - 241, - 242, - 243, - 244, - 245, - 246, - 247, - 248, - 249, - 250, - 251, - 252, - 253, - 254, - 255, - 256, - 257, - 258, - 259, - 260, - 261, - 262, - 263, - 264, - 265, - 266, - 267, - 268, - 269, - 270, - 271, - 272, - 273, - 274, - 275, - 276, - 277, - 278, - 279, - 280, - 281, - 282, - 283, - 284, - 285, - 286, - 287, - 288, - 289, - 290, - 291, - 292, - 293, - 294, - 295, - 296, - 297, - 298, - 299, - 300, - 301, - 302, - 303, - 304, - 305, - 306, - 307, - 308, - 309, - 310, - 311, - 312, - 313, - 314, - 315, - 316, - 317, - 318, - 319, - 320, - 321, - 322, - 323, - 324, - 325, - 326, - 327, - 328, - 329, - 330, - 331, - 332, - 333, - 334, - 335, - 336, - 337, - 338, - 339, - 340, - 341, - 342, - 343, - 344, - 345, - 346, - 347, - 348, - 349, - 350, - 351, - 352, - 353, - 354, - 355, - 356, - 357, - 358, - 359, - 360, - 361, - 362, - 363, - 364, - 365, - 366, - 367, - 368, - 369, - 370, - 371, - 372, - 373, - 374, - 375, - 376, - 377, - 378, - 379, - 380, - 381, - 382, - 383, - 384, - 385, - 386, - 387, - 388, - 389, - 390, - 391, - 392, - 393, - 394, - 395, - 396, - 397, - 398, - 399, - 400, - 401, - 402, - 403, - 404, - 405, - 406, - 407, - 408, - 409, - 410, - 411, - 412, - 413, - 414, - 415, - 416, - 417, - 418, - 419, - 420, - 421, - 422, - 423, - 424, - 425, - 426, - 427, - 428, - 429, - 430, - 431, - 432, - 433, - 434, - 435, - 436, - 437, - 438, - 439, - 440, - 441, - 442, - 443, - 444, - 445, - 446, - 447, - 448, - 449, - 450, - 451, - 452, - 453, - 454, - 455, - 456, - 457, - 458, - 459, - 460, - 461, - 462, - 463, - 464, - 465, - 466, - 467, - 468, - 469, - 470, - 471, - 472, - 473, - 474, - 475, - 476, - 477, - 478, - 479, - 480, - 481, - 482, - 483, - 484, - 485, - 486, - 487, - 488, - 489, - 490, - 491, - 492, - 493, - 494, - 495, - 496, - 497, - 498, - 499, - 500, - 501, - 502, - 503, - 504, - 505, - 506, - 507, - 508, - 509, - 510, - 511, - 512, - 513, - 514, - 515, - 516, - 517, - 518, - 519, - 520, - 521, - 522, - 523, - 524, - 525, - 526, - 527, - 528, - 529, - 530, - 531, - 532, - 533, - 534, - 535, - 536, - 537, - 538, - 539, - 540, - 541, - 542, - 543, - 544, - 545, - 546, - 547, - 548, - 549, - 550, - 551, - 552, - 553, - 554, - 555, - 556, - 557, - 558, - 559, - 560, - 561, - 562, - 563, - 564, - 565, - 566, - 567, - 568, - 569, - 570, - 571, - 572, - 573, - 574, - 575, - 576, - 577, - 578, - 579, - 580, - 581, - 582, - 583, - 584, - 585, - 586, - 587, - 588, - 589, - 590, - 591, - 592, - 593, - 594, - 595, - 596, - 597, - 598, - 599, - 600, - 601, - 602, - 603, - 604, - 605, - 606, - 607, - 608, - 609, - 610, - 611, - 612, - 613, - 614, - 615, - 616, - 617, - 618, - 619, - 620, - 621, - 622, - 623, - 624, - 625, - 626, - 627, - 628, - 629, - 630, - 631, - 632, - 633, - 634, - 635, - 636, - 637, - 638, - 639, - 640, - 641, - 642, - 643, - 644, - 645, - 646, - 647, - 648, - 649, - 650, - 651, - 652, - 653, - 654, - 655, - 656, - 657, - 658, - 659, - 660, - 661, - 662, - 663, - 664, - 665, - 666, - 667, - 668, - 669, - 670, - 671, - 672, - 673, - 674, - 675, - 676, - 677, - 678, - 679, - 680, - 681, - 682, - 683, - 684, - 685, - 686, - 687, - 688, - 689, - 690, - 691, - 692, - 693, - 694, - 695, - 696, - 697, - 698, - 699, - 700, - 701, - 702, - 703, - 704, - 705, - 706, - 707, - 708, - 709, - 710, - 711, - 712, - 713, - 714, - 715, - 716, - 717, - 718, - 719, - 720, - 721, - 722, - 723, - 724, - 725, - 726, - 727, - 728, - 729, - 730, - 731, - 732, - 733, - 734, - 735, - 736, - 737, - 738, - 739, - 740, - 741, - 742, - 743, - 744, - 745, - 746, - 747, - 748, - 749, - 750, - 751, - 752, - 753, - 754, - 755, - 756, - 757, - 758, - 759, - 760, - 761, - 762, - 763, - 764, - 765, - 766, - 767, - 768, - 769, - 770, - 771, - 772, - 773, - 774, - 775, - 776, - 777, - 778, - 779, - 780, - 781, - 782, - 783, - 784, - 785, - 786, - 787, - 788, - 789, - 790, - 791, - 792, - 793, - 794, - 795, - 796, - 797, - 798, - 799, - 800, - 801, - 802, - 803, - 804, - 805, - 806, - 807, - 808, - 809, - 810, - 811, - 812, - 813, - 814, - 815, - 816, - 817, - 818, - 819, - 820, - 821, - 822, - 823, - 824, - 825, - 826, - 827, - 828, - 829, - 830, - 831, - 832, - 833, - 834, - 835, - 836, - 837, - 838, - 839, - 840, - 841, - 842, - 843, - 844, - 845, - 846, - 847, - 848, - 849, - 850, - 851, - 852, - 853, - 854, - 855, - 856, - 857, - 858, - 859, - 860, - 861, - 862, - 863, - 864, - 865, - 866, - 867, - 868, - 869, - 870, - 871, - 872, - 873, - 874, - 875, - 876, - 877, - 878, - 879, - 880, - 881, - 882, - 883, - 884, - 885, - 886, - 887, - 888, - 889, - 890, - 891, - 892, - 893, - 894, - 895, - 896, - 897, - 898, - 899, - 900, - 901, - 902, - 903, - 904, - 905, - 906, - 907, - 908, - 909, - 910, - 911, - 912, - 913, - 914, - 915, - 916, - 917, - 918, - 919, - 920, - 921, - 922, - 923, - 924, - 925, - 926, - 927, - 928, - 929, - 930, - 931, - 932, - 933, - 934, - 935, - 936, - 937, - 938, - 939, - 940, - 941, - 942, - 943, - 944, - 945, - 946, - 947, - 948, - 949, - 950, - 951, - 952, - 953, - 954, - 955, - 956, - 957, - 958, - 959, - 960, - 961, - 962, - 963, - 964, - 965, - 966, - 967, - 968, - 969, - 970, - 971, - 972, - 973, - 974, - 975, - 976, - 977, - 978, - 979, - 980, - 981, - 982, - 983, - 984, - 985, - 986, - 987, - 988, - 989, - 990, - 991, - 992, - 993, - 994, - 995, - 996, - 997, - 998, - 999, - 1000 -) - val p2 = Person( - 1001, - 1002, - 1003, - 1004, - 1005, - 1006, - 1007, - 1008, - 1009, - 1010, - 1011, - 1012, - 1013, - 1014, - 1015, - 1016, - 1017, - 1018, - 1019, - 1020, - 1021, - 1022, - 1023, - 1024, - 1025, - 1026, - 1027, - 1028, - 1029, - 1030, - 1031, - 1032, - 1033, - 1034, - 1035, - 1036, - 1037, - 1038, - 1039, - 1040, - 1041, - 1042, - 1043, - 1044, - 1045, - 1046, - 1047, - 1048, - 1049, - 1050, - 1051, - 1052, - 1053, - 1054, - 1055, - 1056, - 1057, - 1058, - 1059, - 1060, - 1061, - 1062, - 1063, - 1064, - 1065, - 1066, - 1067, - 1068, - 1069, - 1070, - 1071, - 1072, - 1073, - 1074, - 1075, - 1076, - 1077, - 1078, - 1079, - 1080, - 1081, - 1082, - 1083, - 1084, - 1085, - 1086, - 1087, - 1088, - 1089, - 1090, - 1091, - 1092, - 1093, - 1094, - 1095, - 1096, - 1097, - 1098, - 1099, - 1100, - 1101, - 1102, - 1103, - 1104, - 1105, - 1106, - 1107, - 1108, - 1109, - 1110, - 1111, - 1112, - 1113, - 1114, - 1115, - 1116, - 1117, - 1118, - 1119, - 1120, - 1121, - 1122, - 1123, - 1124, - 1125, - 1126, - 1127, - 1128, - 1129, - 1130, - 1131, - 1132, - 1133, - 1134, - 1135, - 1136, - 1137, - 1138, - 1139, - 1140, - 1141, - 1142, - 1143, - 1144, - 1145, - 1146, - 1147, - 1148, - 1149, - 1150, - 1151, - 1152, - 1153, - 1154, - 1155, - 1156, - 1157, - 1158, - 1159, - 1160, - 1161, - 1162, - 1163, - 1164, - 1165, - 1166, - 1167, - 1168, - 1169, - 1170, - 1171, - 1172, - 1173, - 1174, - 1175, - 1176, - 1177, - 1178, - 1179, - 1180, - 1181, - 1182, - 1183, - 1184, - 1185, - 1186, - 1187, - 1188, - 1189, - 1190, - 1191, - 1192, - 1193, - 1194, - 1195, - 1196, - 1197, - 1198, - 1199, - 1200, - 1201, - 1202, - 1203, - 1204, - 1205, - 1206, - 1207, - 1208, - 1209, - 1210, - 1211, - 1212, - 1213, - 1214, - 1215, - 1216, - 1217, - 1218, - 1219, - 1220, - 1221, - 1222, - 1223, - 1224, - 1225, - 1226, - 1227, - 1228, - 1229, - 1230, - 1231, - 1232, - 1233, - 1234, - 1235, - 1236, - 1237, - 1238, - 1239, - 1240, - 1241, - 1242, - 1243, - 1244, - 1245, - 1246, - 1247, - 1248, - 1249, - 1250, - 1251, - 1252, - 1253, - 1254, - 1255, - 1256, - 1257, - 1258, - 1259, - 1260, - 1261, - 1262, - 1263, - 1264, - 1265, - 1266, - 1267, - 1268, - 1269, - 1270, - 1271, - 1272, - 1273, - 1274, - 1275, - 1276, - 1277, - 1278, - 1279, - 1280, - 1281, - 1282, - 1283, - 1284, - 1285, - 1286, - 1287, - 1288, - 1289, - 1290, - 1291, - 1292, - 1293, - 1294, - 1295, - 1296, - 1297, - 1298, - 1299, - 1300, - 1301, - 1302, - 1303, - 1304, - 1305, - 1306, - 1307, - 1308, - 1309, - 1310, - 1311, - 1312, - 1313, - 1314, - 1315, - 1316, - 1317, - 1318, - 1319, - 1320, - 1321, - 1322, - 1323, - 1324, - 1325, - 1326, - 1327, - 1328, - 1329, - 1330, - 1331, - 1332, - 1333, - 1334, - 1335, - 1336, - 1337, - 1338, - 1339, - 1340, - 1341, - 1342, - 1343, - 1344, - 1345, - 1346, - 1347, - 1348, - 1349, - 1350, - 1351, - 1352, - 1353, - 1354, - 1355, - 1356, - 1357, - 1358, - 1359, - 1360, - 1361, - 1362, - 1363, - 1364, - 1365, - 1366, - 1367, - 1368, - 1369, - 1370, - 1371, - 1372, - 1373, - 1374, - 1375, - 1376, - 1377, - 1378, - 1379, - 1380, - 1381, - 1382, - 1383, - 1384, - 1385, - 1386, - 1387, - 1388, - 1389, - 1390, - 1391, - 1392, - 1393, - 1394, - 1395, - 1396, - 1397, - 1398, - 1399, - 1400, - 1401, - 1402, - 1403, - 1404, - 1405, - 1406, - 1407, - 1408, - 1409, - 1410, - 1411, - 1412, - 1413, - 1414, - 1415, - 1416, - 1417, - 1418, - 1419, - 1420, - 1421, - 1422, - 1423, - 1424, - 1425, - 1426, - 1427, - 1428, - 1429, - 1430, - 1431, - 1432, - 1433, - 1434, - 1435, - 1436, - 1437, - 1438, - 1439, - 1440, - 1441, - 1442, - 1443, - 1444, - 1445, - 1446, - 1447, - 1448, - 1449, - 1450, - 1451, - 1452, - 1453, - 1454, - 1455, - 1456, - 1457, - 1458, - 1459, - 1460, - 1461, - 1462, - 1463, - 1464, - 1465, - 1466, - 1467, - 1468, - 1469, - 1470, - 1471, - 1472, - 1473, - 1474, - 1475, - 1476, - 1477, - 1478, - 1479, - 1480, - 1481, - 1482, - 1483, - 1484, - 1485, - 1486, - 1487, - 1488, - 1489, - 1490, - 1491, - 1492, - 1493, - 1494, - 1495, - 1496, - 1497, - 1498, - 1499, - 1500, - 1501, - 1502, - 1503, - 1504, - 1505, - 1506, - 1507, - 1508, - 1509, - 1510, - 1511, - 1512, - 1513, - 1514, - 1515, - 1516, - 1517, - 1518, - 1519, - 1520, - 1521, - 1522, - 1523, - 1524, - 1525, - 1526, - 1527, - 1528, - 1529, - 1530, - 1531, - 1532, - 1533, - 1534, - 1535, - 1536, - 1537, - 1538, - 1539, - 1540, - 1541, - 1542, - 1543, - 1544, - 1545, - 1546, - 1547, - 1548, - 1549, - 1550, - 1551, - 1552, - 1553, - 1554, - 1555, - 1556, - 1557, - 1558, - 1559, - 1560, - 1561, - 1562, - 1563, - 1564, - 1565, - 1566, - 1567, - 1568, - 1569, - 1570, - 1571, - 1572, - 1573, - 1574, - 1575, - 1576, - 1577, - 1578, - 1579, - 1580, - 1581, - 1582, - 1583, - 1584, - 1585, - 1586, - 1587, - 1588, - 1589, - 1590, - 1591, - 1592, - 1593, - 1594, - 1595, - 1596, - 1597, - 1598, - 1599, - 1600, - 1601, - 1602, - 1603, - 1604, - 1605, - 1606, - 1607, - 1608, - 1609, - 1610, - 1611, - 1612, - 1613, - 1614, - 1615, - 1616, - 1617, - 1618, - 1619, - 1620, - 1621, - 1622, - 1623, - 1624, - 1625, - 1626, - 1627, - 1628, - 1629, - 1630, - 1631, - 1632, - 1633, - 1634, - 1635, - 1636, - 1637, - 1638, - 1639, - 1640, - 1641, - 1642, - 1643, - 1644, - 1645, - 1646, - 1647, - 1648, - 1649, - 1650, - 1651, - 1652, - 1653, - 1654, - 1655, - 1656, - 1657, - 1658, - 1659, - 1660, - 1661, - 1662, - 1663, - 1664, - 1665, - 1666, - 1667, - 1668, - 1669, - 1670, - 1671, - 1672, - 1673, - 1674, - 1675, - 1676, - 1677, - 1678, - 1679, - 1680, - 1681, - 1682, - 1683, - 1684, - 1685, - 1686, - 1687, - 1688, - 1689, - 1690, - 1691, - 1692, - 1693, - 1694, - 1695, - 1696, - 1697, - 1698, - 1699, - 1700, - 1701, - 1702, - 1703, - 1704, - 1705, - 1706, - 1707, - 1708, - 1709, - 1710, - 1711, - 1712, - 1713, - 1714, - 1715, - 1716, - 1717, - 1718, - 1719, - 1720, - 1721, - 1722, - 1723, - 1724, - 1725, - 1726, - 1727, - 1728, - 1729, - 1730, - 1731, - 1732, - 1733, - 1734, - 1735, - 1736, - 1737, - 1738, - 1739, - 1740, - 1741, - 1742, - 1743, - 1744, - 1745, - 1746, - 1747, - 1748, - 1749, - 1750, - 1751, - 1752, - 1753, - 1754, - 1755, - 1756, - 1757, - 1758, - 1759, - 1760, - 1761, - 1762, - 1763, - 1764, - 1765, - 1766, - 1767, - 1768, - 1769, - 1770, - 1771, - 1772, - 1773, - 1774, - 1775, - 1776, - 1777, - 1778, - 1779, - 1780, - 1781, - 1782, - 1783, - 1784, - 1785, - 1786, - 1787, - 1788, - 1789, - 1790, - 1791, - 1792, - 1793, - 1794, - 1795, - 1796, - 1797, - 1798, - 1799, - 1800, - 1801, - 1802, - 1803, - 1804, - 1805, - 1806, - 1807, - 1808, - 1809, - 1810, - 1811, - 1812, - 1813, - 1814, - 1815, - 1816, - 1817, - 1818, - 1819, - 1820, - 1821, - 1822, - 1823, - 1824, - 1825, - 1826, - 1827, - 1828, - 1829, - 1830, - 1831, - 1832, - 1833, - 1834, - 1835, - 1836, - 1837, - 1838, - 1839, - 1840, - 1841, - 1842, - 1843, - 1844, - 1845, - 1846, - 1847, - 1848, - 1849, - 1850, - 1851, - 1852, - 1853, - 1854, - 1855, - 1856, - 1857, - 1858, - 1859, - 1860, - 1861, - 1862, - 1863, - 1864, - 1865, - 1866, - 1867, - 1868, - 1869, - 1870, - 1871, - 1872, - 1873, - 1874, - 1875, - 1876, - 1877, - 1878, - 1879, - 1880, - 1881, - 1882, - 1883, - 1884, - 1885, - 1886, - 1887, - 1888, - 1889, - 1890, - 1891, - 1892, - 1893, - 1894, - 1895, - 1896, - 1897, - 1898, - 1899, - 1900, - 1901, - 1902, - 1903, - 1904, - 1905, - 1906, - 1907, - 1908, - 1909, - 1910, - 1911, - 1912, - 1913, - 1914, - 1915, - 1916, - 1917, - 1918, - 1919, - 1920, - 1921, - 1922, - 1923, - 1924, - 1925, - 1926, - 1927, - 1928, - 1929, - 1930, - 1931, - 1932, - 1933, - 1934, - 1935, - 1936, - 1937, - 1938, - 1939, - 1940, - 1941, - 1942, - 1943, - 1944, - 1945, - 1946, - 1947, - 1948, - 1949, - 1950, - 1951, - 1952, - 1953, - 1954, - 1955, - 1956, - 1957, - 1958, - 1959, - 1960, - 1961, - 1962, - 1963, - 1964, - 1965, - 1966, - 1967, - 1968, - 1969, - 1970, - 1971, - 1972, - 1973, - 1974, - 1975, - 1976, - 1977, - 1978, - 1979, - 1980, - 1981, - 1982, - 1983, - 1984, - 1985, - 1986, - 1987, - 1988, - 1989, - 1990, - 1991, - 1992, - 1993, - 1994, - 1995, - 1996, - 1997, - 1998, - 1999, - 2000 -) - println(p1.field1000) - println(p2.field999) -} \ No newline at end of file From f929949a3e579613fc2ff5e4f090ca10c63df799 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 7 Nov 2025 09:06:14 +0100 Subject: [PATCH 40/57] implemented a custom valgrind implementation by substituting all invokes of malloc, calloc and free by the memtrack tracker and print a result if there are + we do not have memory leaks anymore --- .../jvm/src/test/scala/effekt/FlatTests.scala | 8 +- .../effekt/generator/llvm/Transformer.scala | 2 +- libraries/common/array.effekt | 18 ++- libraries/llvm/bytearray.c | 8 +- libraries/llvm/cMalloc.c | 61 +++++++- libraries/llvm/cMalloc.h | 13 ++ libraries/llvm/io.c | 141 +++++++++--------- libraries/llvm/memtrack.c | 141 ++++++++++++++++++ libraries/llvm/rts.ll | 9 +- 9 files changed, 311 insertions(+), 90 deletions(-) create mode 100644 libraries/llvm/cMalloc.h create mode 100644 libraries/llvm/memtrack.c diff --git a/effekt/jvm/src/test/scala/effekt/FlatTests.scala b/effekt/jvm/src/test/scala/effekt/FlatTests.scala index 909e825c94..c0c0f6e5c8 100644 --- a/effekt/jvm/src/test/scala/effekt/FlatTests.scala +++ b/effekt/jvm/src/test/scala/effekt/FlatTests.scala @@ -12,9 +12,13 @@ class FlatTests extends EffektTests { def backendName = "llvm" - override lazy val positives: Set[File] = Set() + override def valgrind = false // works on linux only + + override lazy val positives: Set[File] = Set( + examplesDir / "flat" / "optimized" + ) override lazy val withoutOptimizations: Set[File] = Set( - examplesDir / "flat", + examplesDir / "flat" / "nooptimized", ) } diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index d63070c465..7c6c44a155 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -919,7 +919,7 @@ object Transformer { private def produceSingleObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): LocalReference = { val objectReference = LocalReference(objectType, freshName(role)) val environmentReference = LocalReference(environmentType, freshName("environment")) - val size = ConstantInt(environmentSize(environment)); + val size = ConstantInt(environmentSize(environment)) val eraser = getEraser(environment, ObjectEraser) emit(Call(objectReference.name, Ccc(), objectType, newObject, List(eraser, size))); diff --git a/libraries/common/array.effekt b/libraries/common/array.effekt index 89c85ffd86..11557af7b5 100644 --- a/libraries/common/array.effekt +++ b/libraries/common/array.effekt @@ -18,12 +18,17 @@ extern type Array[T] */ extern llvm """ -declare noalias ptr @calloc(i64, i64) +declare noalias ptr @cCalloc(i64, i64) define void @array_erase(%Object %object) { entry: - %data_pointer = getelementptr inbounds i64, ptr %object, i64 1 - %size = load i64, ptr %object, align 8 + ; header of arrays looks like that: rc (0), eraser (1), size (2), data (3) + %size_pointer = getelementptr inbounds i64, ptr %object, i64 2 + %size = load i64, ptr %size_pointer, align 8 + + ; data starts after 3 entries in our object + %data_pointer = getelementptr inbounds ptr, ptr %object, i64 3 + %size_eq_0 = icmp eq i64 %size, 0 br i1 %size_eq_0, label %exit, label %loop loop: @@ -32,12 +37,13 @@ loop: %element_pointer = getelementptr inbounds %Pos, ptr %data_pointer, i64 %loop_phi %element = load %Pos, ptr %element_pointer - call void @erasePositive(%Pos %element) + call void @erasePositive(%Pos %element) ; we erase each child of the array + %inc = add nuw i64 %loop_phi, 1 %cmp = icmp ult i64 %inc, %size br i1 %cmp, label %loop, label %exit exit: - call void @free(%Object %object) + call void @cFree(%Object %object) ; we delete the array ret void } """ @@ -50,7 +56,7 @@ extern def allocate[T](size: Int) at global: Array[T] = llvm """ %size0 = shl i64 ${size}, 4 %size1 = add i64 %size0, 24 - %calloc = tail call noalias ptr @calloc(i64 %size1, i64 1) + %calloc = tail call noalias ptr @cCalloc(i64 %size1, i64 1) %eraser_pointer = getelementptr ptr, ptr %calloc, i64 1 %size_pointer = getelementptr ptr, ptr %calloc, i64 2 store i64 0, ptr %calloc diff --git a/libraries/llvm/bytearray.c b/libraries/llvm/bytearray.c index 3dce3a6d92..f44663e53e 100644 --- a/libraries/llvm/bytearray.c +++ b/libraries/llvm/bytearray.c @@ -4,6 +4,8 @@ #include // For memcopy #include +#include "memtrack.c" + /** We represent bytearrays like positive types. * * - The field `tag` contains the size @@ -17,11 +19,13 @@ */ -void c_bytearray_erase_noop(void* object) { free(object); } +void c_bytearray_erase_noop(void* object) { + cFree(object); +} struct Pos c_bytearray_new(const Int size) { int object_size = sizeof(struct Header) + size; - void *objPtr = malloc(object_size); + void *objPtr = cMalloc(object_size); struct Header *headerPtr = objPtr; *headerPtr = (struct Header) { .rc = 0, .eraser = c_bytearray_erase_noop, }; return (struct Pos) { diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 11699526d2..944fdd0d91 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -1,7 +1,7 @@ #include #include #include - +#include /** * @brief Block-Struktur für die Freelist. @@ -15,7 +15,7 @@ typedef struct Block // Globale Variablen -static const bool DEBUG = false; +const bool DEBUG = false; static Block* freeList = NULL; // Head of the Freelist static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage @@ -24,15 +24,27 @@ static const int blockSize = 64; // The size of each block (64B) // How much storage do we allocate at the beginning of a program? =4GB static const size_t chunkSize = (size_t)4294967296ULL; +void printFreeList(const Block *freeList) { + printf("All Elements in Free List:\n"); + + const Block *b = freeList; + while (b != NULL) { + printf(" Block at %p\n", (void *)b); + b = b->next; + } + + printf("(end of list)\n"); +} + /** * Initialisiert den großen Speicherbereich (4GB). */ -void cInitializeMemory(void) +void cInitializeMemory() { uint8_t* mem = (uint8_t*)malloc(chunkSize); if (!mem) { - fprintf(stderr, "malloc() failed!\n"); + printf("Error: malloc() failed!\n"); exit(1); } @@ -60,8 +72,8 @@ void* acquire(uint8_t size) { if (nextUnusedBlock + blockSize > endOfChunk) { - fprintf(stderr, "Out of memory!\n"); - return NULL; + printf("Error: Out of memory!\n"); + exit(1); } void* block = nextUnusedBlock; @@ -123,18 +135,53 @@ void assertNumberLeakedBlocks(int expected) { printf("firstBlock: %p\n", (void*)firstBlock); printf("nextUnusedBlock: %p\n", (void*)nextUnusedBlock); + printFreeList(freeList); + + // we print all remaining block addresses // TODO df: Is commented out because it creates warning now +// for (uint8_t* p = firstBlock; p < nextUnusedBlock; p += blockSize) { +// bool inFreeList = false; +// for (const Block* b = freeList; b != NULL; b = b->next) { +// if ((void*)b == (void*)p) { +// inFreeList = true; +// break; +// } +// } +// if (!inFreeList) { +// printf(" still used: %p\n", p); +// } +// } + printf("Test failed!, Total Allocated: %zu, Elements in Free List: %zu \n", totalAllocated, numberOfElementsInFreeList); exit(1); } } +// If there are any open libuv handles or requests, this function will block until they are closed. +// This might happen if the program has asynchronous operations (e.g. read, write file) that are not finished. +// This is necessary to ensure that all memory is freed. +void assertThatAllAsynchronousOperationsAreFinished() { + // 1. Complete all open libuv operations. This blocks until there are no more active handles or requests open. + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + // 2. Now the loop can be closed. + int close_result = uv_loop_close(uv_default_loop()); + if (close_result != 0) { + printf("Error: uv_loop_close still detected some active handles\n"); + exit(1); + } +} + + /** * Works as a Unit-Test, if all used blocks are freed at the end of the program. * If not, we print an error message, making test fail. */ void testIfAllBlocksAreFreed() { -// assertNumberLeakedBlocks(0); + assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles + assertLeakFree(); // testing malloc & calloc & free + assertNumberLeakedBlocks(0); // testing aquire & release + } // small testprogram to test the allocator diff --git a/libraries/llvm/cMalloc.h b/libraries/llvm/cMalloc.h new file mode 100644 index 0000000000..bad50feb9e --- /dev/null +++ b/libraries/llvm/cMalloc.h @@ -0,0 +1,13 @@ +#ifndef CMALLOC_H +#define CMALLOC_H + +extern const bool DEBUG; + +void cInitializeMemory(); +void* acquire(uint8_t size); +void release(void* ptr); +void assertNumberLeakedBlocks(int expected); +void testIfAllBlocksAreFreed(); + + +#endif \ No newline at end of file diff --git a/libraries/llvm/io.c b/libraries/llvm/io.c index 5396915dd5..710adf6452 100644 --- a/libraries/llvm/io.c +++ b/libraries/llvm/io.c @@ -3,6 +3,7 @@ #include #include // to compare flag names +#include "cMalloc.h" // Println and Readln and Random // ----------------------------- @@ -18,7 +19,7 @@ void c_io_println(String text) { String c_io_readln() { uint64_t capacity = 64; uint64_t length = 0; - char* buffer = malloc(capacity * sizeof(char)); + char* buffer = cMalloc(capacity * sizeof(char)); for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) { if (length >= capacity) { @@ -30,7 +31,7 @@ String c_io_readln() { } String result = c_bytearray_construct(length, (const uint8_t*)buffer); - free(buffer); + cFree(buffer); return result; } @@ -73,7 +74,7 @@ void c_resume_int_fs(uv_fs_t* request) { Stack stack = (Stack)request->data; uv_fs_req_cleanup(request); - free(request); + cFree(request); resume_Int(stack, result); } @@ -84,7 +85,7 @@ void c_fs_open(String path, int flags, Stack stack) { char* path_str = c_bytearray_into_nullterminated_string(path); erasePositive((struct Pos) path); - uv_fs_t* request = malloc(sizeof(uv_fs_t)); + uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); request->data = stack; int result = uv_fs_open(uv_default_loop(), request, path_str, flags, 0777, c_resume_int_fs); @@ -92,12 +93,12 @@ void c_fs_open(String path, int flags, Stack stack) { if (result < 0) { // TODO free path_str? uv_fs_req_cleanup(request); - free(request); + cFree(request); resume_Int(stack, result); } // Free the string since libuv copies it into the request - free(path_str); + cFree(path_str); return; } @@ -120,9 +121,9 @@ void c_fs_cb(uv_fs_t* request) { int64_t result = (int64_t)request->result; uv_fs_req_cleanup(request); - free(request); + cFree(request); erasePositive(closure->buffer); - free(closure); + cFree(closure); resume_Int(stack, result); } @@ -130,21 +131,21 @@ void c_fs_read(Int file, struct Pos buffer, Int offset, Int size, Int position, uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer) + offset), size); - io_closure_t* read_closure = malloc(sizeof(io_closure_t)); + io_closure_t* read_closure = cMalloc(sizeof(io_closure_t)); read_closure->stack = stack; read_closure->buffer = buffer; read_closure->offset = offset; - uv_fs_t* request = malloc(sizeof(uv_fs_t)); + uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); request->data = read_closure; int result = uv_fs_read(uv_default_loop(), request, file, &buf, 1, position, c_fs_cb); if (result < 0) { uv_fs_req_cleanup(request); - free(request); + cFree(request); resume_Int(stack, result); - free(read_closure); + cFree(read_closure); } } @@ -153,34 +154,34 @@ void c_fs_write(Int file, struct Pos buffer, Int offset, Int size, Int position, uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer) + offset), size); - io_closure_t* write_closure = malloc(sizeof(io_closure_t)); + io_closure_t* write_closure = cMalloc(sizeof(io_closure_t)); write_closure->stack = stack; write_closure->buffer = buffer; write_closure->offset = offset; - uv_fs_t* request = malloc(sizeof(uv_fs_t)); + uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); request->data = write_closure; int result = uv_fs_write(uv_default_loop(), request, file, &buf, 1, position, c_fs_cb); if (result < 0) { uv_fs_req_cleanup(request); - free(request); + cFree(request); resume_Int(stack, result); - free(write_closure); + cFree(write_closure); } } void c_fs_close(Int file, Stack stack) { - uv_fs_t* request = malloc(sizeof(uv_fs_t)); + uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); request->data = stack; int result = uv_fs_close(uv_default_loop(), request, file, c_resume_int_fs); if (result < 0) { uv_fs_req_cleanup(request); - free(request); + cFree(request); resume_Int(stack, result); } } @@ -191,7 +192,7 @@ void c_fs_mkdir(String path, Stack stack) { char* path_str = c_bytearray_into_nullterminated_string(path); erasePositive((struct Pos) path); - uv_fs_t* request = malloc(sizeof(uv_fs_t)); + uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); request->data = stack; // Perform the mkdir operation @@ -199,12 +200,12 @@ void c_fs_mkdir(String path, Stack stack) { if (result < 0) { uv_fs_req_cleanup(request); - free(request); + cFree(request); resume_Int(stack, result); } // Free the string since libuv copies it into the request - free(path_str); + cFree(path_str); return; } @@ -216,12 +217,12 @@ void c_tcp_connect_cb(uv_connect_t* request, int status) { Stack stack = (Stack)request->data; if (status < 0) { - uv_close((uv_handle_t*)request->handle, (uv_close_cb)free); - free(request); + uv_close((uv_handle_t*)request->handle, (uv_close_cb)cFree); + cFree(request); resume_Int(stack, status); } else { int64_t handle = (int64_t)request->handle; - free(request); + cFree(request); resume_Int(stack, handle); } } @@ -230,26 +231,26 @@ void c_tcp_connect(String host, Int port, Stack stack) { char* host_str = c_bytearray_into_nullterminated_string(host); erasePositive(host); - uv_tcp_t* tcp_handle = malloc(sizeof(uv_tcp_t)); + uv_tcp_t* tcp_handle = cMalloc(sizeof(uv_tcp_t)); int result = uv_tcp_init(uv_default_loop(), tcp_handle); if (result < 0) { - free(tcp_handle); - free(host_str); + cFree(tcp_handle); + cFree(host_str); resume_Int(stack, result); return; } - uv_connect_t* connect_req = malloc(sizeof(uv_connect_t)); + uv_connect_t* connect_req = cMalloc(sizeof(uv_connect_t)); connect_req->data = stack; struct sockaddr_in addr; result = uv_ip4_addr(host_str, port, &addr); - free(host_str); + cFree(host_str); if (result < 0) { - free(tcp_handle); - free(connect_req); + cFree(tcp_handle); + cFree(connect_req); resume_Int(stack, result); return; } @@ -257,8 +258,8 @@ void c_tcp_connect(String host, Int port, Stack stack) { result = uv_tcp_connect(connect_req, tcp_handle, (const struct sockaddr*)&addr, c_tcp_connect_cb); if (result < 0) { - free(tcp_handle); - free(connect_req); + cFree(tcp_handle); + cFree(connect_req); resume_Int(stack, result); return; } @@ -271,7 +272,7 @@ void c_tcp_read_cb(uv_stream_t* stream, ssize_t bytes_read, const uv_buf_t* buf) uv_read_stop(stream); erasePositive(read_closure->buffer); - free(read_closure); + cFree(read_closure); resume_Int(stack, (int64_t)bytes_read); } @@ -288,7 +289,7 @@ void c_tcp_read(Int handle, struct Pos buffer, Int offset, Int size, Stack stack (void)size; uv_stream_t* stream = (uv_stream_t*)handle; - io_closure_t* read_closure = malloc(sizeof(io_closure_t)); + io_closure_t* read_closure = cMalloc(sizeof(io_closure_t)); read_closure->stack = stack; read_closure->buffer = buffer; read_closure->offset = offset; @@ -297,7 +298,7 @@ void c_tcp_read(Int handle, struct Pos buffer, Int offset, Int size, Stack stack int result = uv_read_start(stream, c_tcp_read_alloc_cb, c_tcp_read_cb); if (result < 0) { - free(read_closure); + cFree(read_closure); stream->data = NULL; resume_Int(stack, result); } @@ -305,10 +306,10 @@ void c_tcp_read(Int handle, struct Pos buffer, Int offset, Int size, Stack stack void c_tcp_write_cb(uv_write_t* request, int status) { io_closure_t* write_closure = (io_closure_t*)request->data; - free(request); + cFree(request); erasePositive(write_closure->buffer); Stack stack = write_closure->stack; - free(write_closure); + cFree(write_closure); resume_Int(stack, (int64_t)status); } @@ -317,26 +318,26 @@ void c_tcp_write(Int handle, struct Pos buffer, Int offset, Int size, Stack stac uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer) + offset), size); - io_closure_t* write_closure = malloc(sizeof(io_closure_t)); + io_closure_t* write_closure = cMalloc(sizeof(io_closure_t)); write_closure->stack = stack; write_closure->buffer = buffer; write_closure->offset = offset; - uv_write_t* request = malloc(sizeof(uv_write_t)); + uv_write_t* request = cMalloc(sizeof(uv_write_t)); request->data = write_closure; int result = uv_write(request, stream, &buf, 1, c_tcp_write_cb); if (result < 0) { - free(request); + cFree(request); resume_Int(stack, result); - free(write_closure); + cFree(write_closure); } } void c_tcp_close_cb(uv_handle_t* handle) { Stack stack = (Stack)handle->data; - free(handle); + cFree(handle); resume_Pos(stack, Unit); } @@ -350,27 +351,27 @@ Int c_tcp_bind(String host, Int port) { char* host_str = c_bytearray_into_nullterminated_string(host); erasePositive(host); - uv_tcp_t* tcp_handle = malloc(sizeof(uv_tcp_t)); + uv_tcp_t* tcp_handle = cMalloc(sizeof(uv_tcp_t)); int result = uv_tcp_init(uv_default_loop(), tcp_handle); if (result < 0) { - free(tcp_handle); - free(host_str); + cFree(tcp_handle); + cFree(host_str); return result; } struct sockaddr_in addr; result = uv_ip4_addr(host_str, port, &addr); - free(host_str); + cFree(host_str); if (result < 0) { - uv_close((uv_handle_t*)tcp_handle, (uv_close_cb)free); + uv_close((uv_handle_t*)tcp_handle, (uv_close_cb)cFree); return result; } result = uv_tcp_bind(tcp_handle, (const struct sockaddr*)&addr, 0); if (result < 0) { - uv_close((uv_handle_t*)tcp_handle, (uv_close_cb)free); + uv_close((uv_handle_t*)tcp_handle, (uv_close_cb)cFree); return result; } @@ -389,19 +390,19 @@ void c_tcp_listen_cb(uv_stream_t* server, int status) { if (status < 0) { server->data = NULL; - free(listen_closure); + cFree(listen_closure); erasePositive(closure_handler); resume_Int(closure_stack, status); return; } - uv_tcp_t* client = malloc(sizeof(uv_tcp_t)); + uv_tcp_t* client = cMalloc(sizeof(uv_tcp_t)); int result = uv_tcp_init(uv_default_loop(), client); if (result < 0) { - free(client); + cFree(client); server->data = NULL; - free(listen_closure); + cFree(listen_closure); erasePositive(closure_handler); resume_Int(closure_stack, result); return; @@ -409,9 +410,9 @@ void c_tcp_listen_cb(uv_stream_t* server, int status) { result = uv_accept(server, (uv_stream_t*)client); if (result < 0) { - uv_close((uv_handle_t*)client, (uv_close_cb)free); + uv_close((uv_handle_t*)client, (uv_close_cb)cFree); server->data = NULL; - free(listen_closure); + cFree(listen_closure); erasePositive(closure_handler); resume_Int(closure_stack, result); return; @@ -424,14 +425,14 @@ void c_tcp_listen_cb(uv_stream_t* server, int status) { void c_tcp_listen(Int listener, struct Pos handler, Stack stack) { uv_stream_t* server = (uv_stream_t*)listener; - tcp_listen_closure_t* listen_closure = malloc(sizeof(tcp_listen_closure_t)); + tcp_listen_closure_t* listen_closure = cMalloc(sizeof(tcp_listen_closure_t)); listen_closure->stack = stack; listen_closure->handler = handler; server->data = listen_closure; int result = uv_listen(server, SOMAXCONN, c_tcp_listen_cb); if (result < 0) { - free(listen_closure); + cFree(listen_closure); erasePositive(handler); resume_Int(stack, result); return; @@ -448,7 +449,7 @@ void c_tcp_shutdown(Int handle, Stack stack) { if (listen_closure) { Stack closure_stack = listen_closure->stack; struct Pos closure_handler = listen_closure->handler; - free(listen_closure); + cFree(listen_closure); erasePositive(closure_handler); resume_Int(closure_stack, 0); } @@ -554,13 +555,13 @@ void c_resume_unit_timer(uv_timer_t* handle) { Stack stack = handle->data; uv_timer_stop(handle); - uv_close((uv_handle_t*)handle, (uv_close_cb)free); + uv_close((uv_handle_t*)handle, (uv_close_cb)cFree); resume_Pos(stack, Unit); } void c_timer_start(Int millis, Stack stack) { - uv_timer_t* timer = malloc(sizeof(uv_timer_t)); + uv_timer_t* timer = cMalloc(sizeof(uv_timer_t)); timer->data = stack; uv_timer_init(uv_default_loop(), timer); @@ -597,9 +598,10 @@ typedef struct { } payload; } Promise; -void c_promise_erase_listeners(void *envPtr) { - // envPtr points to a Promise _after_ the eraser, so let's adjust it to point to the promise. - Promise *promise = (Promise*) (envPtr - offsetof(Promise, state)); +void c_promise_erase_listeners(void *object) { + // object points to the Promise object (at rc) + // The environment starts at state (after rc and eraser) + Promise *promise = (Promise*) object; promise_state_t state = promise->state; Stack head; @@ -618,7 +620,7 @@ void c_promise_erase_listeners(void *envPtr) { while (current != NULL) { head = current->head; tail = current->tail; - free(current); + cFree(current); eraseStack(head); current = tail; }; @@ -628,13 +630,14 @@ void c_promise_erase_listeners(void *envPtr) { erasePositive(promise->payload.value); break; } + cFree(promise); // Free the promise object itself } void c_promise_resume_listeners(Listeners* listeners, struct Pos value) { if (listeners != NULL) { Stack head = listeners->head; Listeners* tail = listeners->tail; - free(listeners); + cFree(listeners); c_promise_resume_listeners(tail, value); sharePositive(value); resume_Pos(head, value); @@ -674,7 +677,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) { @@ -690,7 +693,7 @@ void c_promise_await(struct Pos promise, Stack stack) { head = p->payload.listeners.head; tail = p->payload.listeners.tail; if (head != NULL) { - node = (Listeners*)malloc(sizeof(Listeners)); + node = (Listeners*)cMalloc(sizeof(Listeners)); node->head = head; node->tail = tail; p->payload.listeners.head = stack; @@ -706,11 +709,11 @@ void c_promise_await(struct Pos promise, Stack stack) { break; }; // TODO hmm, stack overflow? - erasePositive(promise); // df: Otherwise, interleave_promises.effekt fails. + erasePositive(promise); } struct Pos c_promise_make() { - Promise* promise = (Promise*)malloc(sizeof(Promise)); + Promise* promise = (Promise*)cMalloc(sizeof(Promise)); promise->rc = 0; promise->eraser = c_promise_erase_listeners; diff --git a/libraries/llvm/memtrack.c b/libraries/llvm/memtrack.c new file mode 100644 index 0000000000..7aab17fb9c --- /dev/null +++ b/libraries/llvm/memtrack.c @@ -0,0 +1,141 @@ +/** + * df: Simple memory tracker in C + * + * Tracks all invocations of malloc, calloc and free and prints a report an effekt program if it contains any memory leak. + */ + +#include +#include +#include "cMalloc.h" + +const bool debug = false; // if we should print debug messages + +// Here we track all allocations done by cMalloc, cCalloc and cFree +typedef struct Allocation +{ + void* ptr; // pointer returned by malloc + size_t size; // allocated size (for debugging only) + struct Allocation* next; // next entry +} Allocation; + +static Allocation* allocList = NULL; // Head of the list + +// Track total allocations for summary +static size_t totalAllocated = 0; +static size_t totalFreed = 0; + +// Internal helper: add allocation to alloclist +static void addAllocation(void* ptr, size_t size) +{ + Allocation* entry = malloc(sizeof(Allocation)); + entry->ptr = ptr; + entry->size = size; + entry->next = allocList; + allocList = entry; + totalAllocated += size; +} + +// Internal helper: remove allocation from alloclist +void removeAllocation(void* ptr) +{ + Allocation** curr = &allocList; + while (*curr) + { + if ((*curr)->ptr == ptr) + { + Allocation* toFree = *curr; + totalFreed += toFree->size; + *curr = toFree->next; + free(toFree); + return; + } + curr = &((*curr)->next); + } + if (debug) { + // Pointer not found — double free or invalid free was called + printf("Warning: cFree() was called on untracked pointer %p\n", ptr); + } +} + +// call normal malloc() of the C library and track the allocation +void* cMalloc(size_t size) +{ + void* ptr = malloc(size); + if (!ptr) + { + printf("Allocation failed with %p\n", &size); + exit(1); + } + addAllocation(ptr, size); + if (debug) { + printf("[cMalloc] New block: %p\n", ptr); + } + return ptr; +} + +// call normal calloc() of the C library and track the allocation +void* cCalloc(size_t mmemb, size_t size) +{ + void* ptr = calloc(mmemb, size); + if (!ptr) + { + printf("Allocation failed with %p\n", &size); + exit(1); + } + addAllocation(ptr, size); + if (debug) { + printf("[cCalloc] New block: %p\n", ptr); + } + return ptr; +} + +// call normal free() of the C library and track the allocation +void cFree(void* ptr) +{ + if (ptr == NULL) + return; + removeAllocation(ptr); + free(ptr); + if (debug) { + printf("[cFree] Freed block: %p\n", ptr); + } +} + +// If there are any remaining allocations (potential leaks), we report all of them and terminate the program +void assertLeakFree(void) +{ + if (allocList != NULL) + { + printf("\n----- MEMORY LEAK REPORT -----\n"); + Allocation* curr = allocList; + while (curr) + { + printf("LEAK: %p (%zu bytes) allocated\n", + curr->ptr, curr->size); + curr = curr->next; + } + printf("------------------------------\n"); + printf("Total allocated: %zu bytes\n", totalAllocated); + printf("Total freed: %zu bytes\n", totalFreed); + printf("Still allocated: %zu bytes\n", totalAllocated - totalFreed); + printf("------------------------------\n"); + } +} + +// Simple demo + // int main(void) + // { + // printf("Start memtrack demo\n"); + // + // char* a = cMalloc(10); + // int* b = cMalloc(sizeof(int) * 5); + // double* c = cMalloc(sizeof(double) * 3); + // + // cFree(b); + // //cFree(a); // Intentionally leaked + // //cFree(c); // Intentionally leaked + // + // assertLeakFree(); // Show leaks at end + // + // return 0; + // } diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index a19bbd81bd..8fdb94b73f 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -99,6 +99,9 @@ declare ptr @acquire(i64) declare void @release(ptr) declare void @testIfAllBlocksAreFreed() +declare ptr @cMalloc(i64) +declare void @cFree(ptr) + declare ptr @malloc(i64) declare void @free(ptr) declare ptr @realloc(ptr, i64) @@ -531,7 +534,7 @@ decrement: ret void free: - call void @free(%Prompt %prompt) + call void @cFree(%Prompt %prompt) ;df: wird gecalled, obwohl es nie allocated wurde -> gibt warning ret void } @@ -554,7 +557,7 @@ define private %Stack @underflowStack(%Stack %stack) { store %Stack null, ptr %promptStack_pointer, !alias.scope !13, !noalias !23 call void @erasePrompt(%Prompt %prompt) - call void @free(%Stack %stack) + call void @cFree(%Stack %stack) ;df: wird gecalled, obwohl es nie allocated wurde -> gibt warning ret %Stack %rest } @@ -724,7 +727,7 @@ define private void @eraseFrames(%StackPointer %stackPointer) alwaysinline { define private void @freeStack(%StackPointer %stackPointer) alwaysinline { %base = getelementptr %StackValue, %StackPointer %stackPointer, i64 -1 - call void @free(%Base %base) + call void @cFree(%Base %base) ret void } From 46f4c121aace5e4acffe4c782653b192901f605c Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 7 Nov 2025 11:19:29 +0100 Subject: [PATCH 41/57] edited + added more test programs --- ...different_person_sizes_biggest_first.check | 3 ++ ...ifferent_person_sizes_biggest_first.effekt | 46 +++++++++++++++++++ ...ifferent_person_sizes_smallest_first.check | 3 ++ ...fferent_person_sizes_smallest_first.effekt | 46 +++++++++++++++++++ .../nooptimized/one_big_string_object.check | 1 + .../nooptimized/one_big_string_object.effekt | 9 ++++ examples/flat/nooptimized/one_lg_object.check | 2 +- .../flat/nooptimized/one_lg_object.effekt | 8 ++-- .../nooptimized/one_small_string_object.check | 1 + .../one_small_string_object.effekt | 8 ++++ examples/flat/nooptimized/one_xl_object.check | 2 +- .../flat/nooptimized/one_xl_object.effekt | 8 ++-- .../nooptimized/three_md_objects_direct.check | 3 ++ .../three_md_objects_direct.effekt | 20 ++++++++ .../flat/nooptimized/two_lg_objects.check | 4 +- .../flat/nooptimized/two_lg_objects.effekt | 12 ++--- .../nooptimized/two_md_mixed_object.check | 2 + .../nooptimized/two_md_mixed_object.effekt | 17 +++++++ .../nooptimized/two_sm_mixed_object.check | 2 + .../nooptimized/two_sm_mixed_object.effekt | 15 ++++++ .../flat/nooptimized/two_xl_objects.check | 4 +- .../flat/nooptimized/two_xl_objects.effekt | 10 ++-- libraries/llvm/memtrack.c | 4 +- 23 files changed, 199 insertions(+), 31 deletions(-) create mode 100644 examples/flat/nooptimized/different_person_sizes_biggest_first.check create mode 100644 examples/flat/nooptimized/different_person_sizes_biggest_first.effekt create mode 100644 examples/flat/nooptimized/different_person_sizes_smallest_first.check create mode 100644 examples/flat/nooptimized/different_person_sizes_smallest_first.effekt create mode 100644 examples/flat/nooptimized/one_big_string_object.check create mode 100644 examples/flat/nooptimized/one_big_string_object.effekt create mode 100644 examples/flat/nooptimized/one_small_string_object.check create mode 100644 examples/flat/nooptimized/one_small_string_object.effekt create mode 100644 examples/flat/nooptimized/three_md_objects_direct.check create mode 100644 examples/flat/nooptimized/three_md_objects_direct.effekt create mode 100644 examples/flat/nooptimized/two_md_mixed_object.check create mode 100644 examples/flat/nooptimized/two_md_mixed_object.effekt create mode 100644 examples/flat/nooptimized/two_sm_mixed_object.check create mode 100644 examples/flat/nooptimized/two_sm_mixed_object.effekt diff --git a/examples/flat/nooptimized/different_person_sizes_biggest_first.check b/examples/flat/nooptimized/different_person_sizes_biggest_first.check new file mode 100644 index 0000000000..63ea17a09e --- /dev/null +++ b/examples/flat/nooptimized/different_person_sizes_biggest_first.check @@ -0,0 +1,3 @@ +24 +13 +6 \ No newline at end of file diff --git a/examples/flat/nooptimized/different_person_sizes_biggest_first.effekt b/examples/flat/nooptimized/different_person_sizes_biggest_first.effekt new file mode 100644 index 0000000000..6a963c745c --- /dev/null +++ b/examples/flat/nooptimized/different_person_sizes_biggest_first.effekt @@ -0,0 +1,46 @@ +// this test is for debugging of the to-do-list approach. Here, we define multiple records that require different +// number of heap-slots (aka. 64 bytes). + +// here we allocate the biggest object at first and go to the smallest. + +record OneSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int + ) + +record TwoSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int + ) + +record ThreeSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int + ) + +def main() = { + val p3 = ThreeSlotPerson(14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) + println(p3.field11) + val p2 = TwoSlotPerson(7, 8, 9, 10, 11, 12, 13) + println(p2.field7) + val p1 = OneSlotPerson(1, 2, 3, 4, 5, 6) + println(p1.field6) +} \ No newline at end of file diff --git a/examples/flat/nooptimized/different_person_sizes_smallest_first.check b/examples/flat/nooptimized/different_person_sizes_smallest_first.check new file mode 100644 index 0000000000..bdfbf007c5 --- /dev/null +++ b/examples/flat/nooptimized/different_person_sizes_smallest_first.check @@ -0,0 +1,3 @@ +6 +13 +26 \ No newline at end of file diff --git a/examples/flat/nooptimized/different_person_sizes_smallest_first.effekt b/examples/flat/nooptimized/different_person_sizes_smallest_first.effekt new file mode 100644 index 0000000000..2a61b272dc --- /dev/null +++ b/examples/flat/nooptimized/different_person_sizes_smallest_first.effekt @@ -0,0 +1,46 @@ +// this test is for debugging of the to-do-list approach. Here, we define multiple records that require different +// number of heap-slots (aka. 64 bytes). + +record OneSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int + ) + +record TwoSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int + ) + +record ThreeSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int + ) + +def main() = { + val p1 = OneSlotPerson(1, 2, 3, 4, 5, 6) + println(p1.field6) + val p2 = TwoSlotPerson(7, 8, 9, 10, 11, 12, 13) + println(p2.field7) + val p3 = ThreeSlotPerson(14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26) + println(p3.field13) +} \ No newline at end of file diff --git a/examples/flat/nooptimized/one_big_string_object.check b/examples/flat/nooptimized/one_big_string_object.check new file mode 100644 index 0000000000..41bfe81b82 --- /dev/null +++ b/examples/flat/nooptimized/one_big_string_object.check @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. \ No newline at end of file diff --git a/examples/flat/nooptimized/one_big_string_object.effekt b/examples/flat/nooptimized/one_big_string_object.effekt new file mode 100644 index 0000000000..492ee575c7 --- /dev/null +++ b/examples/flat/nooptimized/one_big_string_object.effekt @@ -0,0 +1,9 @@ +// contains one big lorem ipsum string with 100 words +record Person( + field1: String + ) + +def main() = { + val p = Person("Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.") + println(p.field1) +} \ No newline at end of file diff --git a/examples/flat/nooptimized/one_lg_object.check b/examples/flat/nooptimized/one_lg_object.check index 3cacc0b93c..9a037142aa 100644 --- a/examples/flat/nooptimized/one_lg_object.check +++ b/examples/flat/nooptimized/one_lg_object.check @@ -1 +1 @@ -12 \ No newline at end of file +10 \ No newline at end of file diff --git a/examples/flat/nooptimized/one_lg_object.effekt b/examples/flat/nooptimized/one_lg_object.effekt index 42691204c7..18eff9264f 100644 --- a/examples/flat/nooptimized/one_lg_object.effekt +++ b/examples/flat/nooptimized/one_lg_object.effekt @@ -9,12 +9,10 @@ record Person( field7: Int, field8: Int, field9: Int, - field10: Int, - field11: Int, - field12: Int + field10: Int ) def main() = { - val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) - println(p.field12) + val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + println(p.field10) } \ No newline at end of file diff --git a/examples/flat/nooptimized/one_small_string_object.check b/examples/flat/nooptimized/one_small_string_object.check new file mode 100644 index 0000000000..30d74d2584 --- /dev/null +++ b/examples/flat/nooptimized/one_small_string_object.check @@ -0,0 +1 @@ +test \ No newline at end of file diff --git a/examples/flat/nooptimized/one_small_string_object.effekt b/examples/flat/nooptimized/one_small_string_object.effekt new file mode 100644 index 0000000000..efe6e6fefb --- /dev/null +++ b/examples/flat/nooptimized/one_small_string_object.effekt @@ -0,0 +1,8 @@ +record Person( + field1: String + ) + +def main() = { + val p = Person("test") + println(p.field1) +} \ No newline at end of file diff --git a/examples/flat/nooptimized/one_xl_object.check b/examples/flat/nooptimized/one_xl_object.check index ca7bf83ac5..9d607966b7 100644 --- a/examples/flat/nooptimized/one_xl_object.check +++ b/examples/flat/nooptimized/one_xl_object.check @@ -1 +1 @@ -13 \ No newline at end of file +11 \ No newline at end of file diff --git a/examples/flat/nooptimized/one_xl_object.effekt b/examples/flat/nooptimized/one_xl_object.effekt index aff3ed9e29..bc45859caa 100644 --- a/examples/flat/nooptimized/one_xl_object.effekt +++ b/examples/flat/nooptimized/one_xl_object.effekt @@ -10,12 +10,10 @@ record Person( field8: Int, field9: Int, field10: Int, - field11: Int, - field12: Int, - field13: Int + field11: Int ) def main() = { - val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) - println(p.field13) + val p = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) + println(p.field11) } \ No newline at end of file diff --git a/examples/flat/nooptimized/three_md_objects_direct.check b/examples/flat/nooptimized/three_md_objects_direct.check new file mode 100644 index 0000000000..8fac222064 --- /dev/null +++ b/examples/flat/nooptimized/three_md_objects_direct.check @@ -0,0 +1,3 @@ +7 +10 +18 \ No newline at end of file diff --git a/examples/flat/nooptimized/three_md_objects_direct.effekt b/examples/flat/nooptimized/three_md_objects_direct.effekt new file mode 100644 index 0000000000..ec0561e122 --- /dev/null +++ b/examples/flat/nooptimized/three_md_objects_direct.effekt @@ -0,0 +1,20 @@ +// md = Person requires 2 saving slots +// direct = create person and print field directly +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6, 7) + println(p1.field7) + val p2 = Person(8, 9, 10, 11, 12, 13, 14) + val p3 = Person(15, 16, 17, 18, 19, 20, 21) + println(p2.field3) + println(p3.field4) +} \ No newline at end of file diff --git a/examples/flat/nooptimized/two_lg_objects.check b/examples/flat/nooptimized/two_lg_objects.check index 89917fb8fd..8f59f50a43 100644 --- a/examples/flat/nooptimized/two_lg_objects.check +++ b/examples/flat/nooptimized/two_lg_objects.check @@ -1,2 +1,2 @@ -12 -19 \ No newline at end of file +10 +16 \ No newline at end of file diff --git a/examples/flat/nooptimized/two_lg_objects.effekt b/examples/flat/nooptimized/two_lg_objects.effekt index 62c0e7a235..6393bd1716 100644 --- a/examples/flat/nooptimized/two_lg_objects.effekt +++ b/examples/flat/nooptimized/two_lg_objects.effekt @@ -9,14 +9,12 @@ record Person( field7: Int, field8: Int, field9: Int, - field10: Int, - field11: Int, - field12: Int + field10: Int ) def main() = { - val p1 = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) - val p2 = Person(13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24) - println(p1.field12) - println(p2.field7) + val p1 = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + val p2 = Person(11, 12, 13, 14, 15, 16, 17, 18, 19, 20) + println(p1.field10) + println(p2.field6) } \ No newline at end of file diff --git a/examples/flat/nooptimized/two_md_mixed_object.check b/examples/flat/nooptimized/two_md_mixed_object.check new file mode 100644 index 0000000000..f21c60d7ea --- /dev/null +++ b/examples/flat/nooptimized/two_md_mixed_object.check @@ -0,0 +1,2 @@ +P1Field2 +10 \ No newline at end of file diff --git a/examples/flat/nooptimized/two_md_mixed_object.effekt b/examples/flat/nooptimized/two_md_mixed_object.effekt new file mode 100644 index 0000000000..4d67068e4c --- /dev/null +++ b/examples/flat/nooptimized/two_md_mixed_object.effekt @@ -0,0 +1,17 @@ +// md = Person needs 2 slots +// contains strings and integers. +// First slot contains field1, second slot contains field2, field3, field4, field5. +record Person( + field1: Int, + field2: String, + field3: String, + field4: String, + field5: Int + ) + +def main() = { + val p1 = Person(1, "P1Field2", "P1Field3", "P1Field4", 5) + val p2 = Person(6, "P2Field2", "P2Field3", "P2Field4", 10) + println(p1.field2) + println(p2.field5) +} \ No newline at end of file diff --git a/examples/flat/nooptimized/two_sm_mixed_object.check b/examples/flat/nooptimized/two_sm_mixed_object.check new file mode 100644 index 0000000000..ca3d17d48c --- /dev/null +++ b/examples/flat/nooptimized/two_sm_mixed_object.check @@ -0,0 +1,2 @@ +P1Field1 +8 \ No newline at end of file diff --git a/examples/flat/nooptimized/two_sm_mixed_object.effekt b/examples/flat/nooptimized/two_sm_mixed_object.effekt new file mode 100644 index 0000000000..c07c2af927 --- /dev/null +++ b/examples/flat/nooptimized/two_sm_mixed_object.effekt @@ -0,0 +1,15 @@ +// sm = Person fills 1 saving slot completely +// contains strings and integers +record Person( + field1: String, + field2: Int, + field3: String, + field4: Int + ) + +def main() = { + val p1 = Person("P1Field1", 2, "P1Field3", 4) + val p2 = Person("P2Field1", 6, "P2Field3", 8) + println(p1.field1) + println(p2.field4) +} \ No newline at end of file diff --git a/examples/flat/nooptimized/two_xl_objects.check b/examples/flat/nooptimized/two_xl_objects.check index bda4b3e2f4..10afbfb7e2 100644 --- a/examples/flat/nooptimized/two_xl_objects.check +++ b/examples/flat/nooptimized/two_xl_objects.check @@ -1,2 +1,2 @@ -13 -20 \ No newline at end of file +11 +18 \ No newline at end of file diff --git a/examples/flat/nooptimized/two_xl_objects.effekt b/examples/flat/nooptimized/two_xl_objects.effekt index c1a445f31f..e5628b0b77 100644 --- a/examples/flat/nooptimized/two_xl_objects.effekt +++ b/examples/flat/nooptimized/two_xl_objects.effekt @@ -10,14 +10,12 @@ record Person( field8: Int, field9: Int, field10: Int, - field11: Int, - field12: Int, - field13: Int + field11: Int ) def main() = { - val p1 = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) - val p2 = Person(14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26) - println(p1.field13) + val p1 = Person(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) + val p2 = Person(12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22) + println(p1.field11) println(p2.field7) } \ No newline at end of file diff --git a/libraries/llvm/memtrack.c b/libraries/llvm/memtrack.c index 7aab17fb9c..b526cc5561 100644 --- a/libraries/llvm/memtrack.c +++ b/libraries/llvm/memtrack.c @@ -94,11 +94,11 @@ void cFree(void* ptr) { if (ptr == NULL) return; - removeAllocation(ptr); - free(ptr); if (debug) { printf("[cFree] Freed block: %p\n", ptr); } + removeAllocation(ptr); + free(ptr); } // If there are any remaining allocations (potential leaks), we report all of them and terminate the program From ba9dbe74bc03bddbf9993c4b4334bfc19c16ed89 Mon Sep 17 00:00:00 2001 From: Flat Date: Mon, 10 Nov 2025 17:58:36 +0100 Subject: [PATCH 42/57] First contribution of lazy reference counting. --- .../effekt/generator/llvm/Transformer.scala | 232 ++++++++++++------ libraries/llvm/cMalloc.c | 62 +++-- 2 files changed, 192 insertions(+), 102 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 7c6c44a155..3860770945 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -3,7 +3,7 @@ package generator package llvm import effekt.generator.llvm.Type as LLVMType -import effekt.generator.llvm.Instruction.Call +import effekt.generator.llvm.Instruction.{Call, Load} import effekt.machine import effekt.machine.{Environment, Variable} import effekt.machine.analysis.* @@ -12,6 +12,7 @@ import effekt.util.messages.ErrorReporter import scala.annotation.tailrec import scala.collection.mutable +import scala.collection.mutable.ListBuffer object Transformer { @@ -23,7 +24,8 @@ object Transformer { given MC: ModuleContext = ModuleContext(); definitions.foreach(transform); - val globals = MC.definitions; MC.definitions = null; + val globals = MC.definitions; + MC.definitions = null; val entryInstructions = List( Call("stack", Ccc(), stackType, withEmptyStack, List()), @@ -39,7 +41,9 @@ object Transformer { // context getters private def MC(using MC: ModuleContext): ModuleContext = MC + private def FC(using FC: FunctionContext): FunctionContext = FC + private def BC(using BC: BlockContext): BlockContext = BC def transform(declaration: machine.Declaration)(using ErrorReporter): Definition = @@ -63,26 +67,26 @@ object Transformer { """call void @hole() |unreachable |""".stripMargin - } + } def transform(template: Template[machine.Variable]): String = "; variable\n " ++ intercalate(template.strings, template.args.map { case machine.Variable(name, tpe) => PrettyPrinter.localName(name) }).mkString def transform(definition: machine.Definition)(using ModuleContext): Unit = definition match { - case machine.Definition(machine.Label(name, environment), body) => - val parameters = environment.map { case machine.Variable(name, tpe) => Parameter(transform(tpe), name) } - defineLabel(name, parameters) { - emit(Comment(s"definition $name, environment length ${environment.length}")) - eraseValues(environment, freeVariables(body)) - transform(body) - } + case machine.Definition(machine.Label(name, environment), body) => + val parameters = environment.map { case machine.Variable(name, tpe) => Parameter(transform(tpe), name) } + defineLabel(name, parameters) { + emit(Comment(s"definition $name, environment length ${environment.length}")) + eraseValues(environment, freeVariables(body)) + transform(body) + } } def transform(statement: machine.Statement)(using ModuleContext, FunctionContext, BlockContext): Terminator = statement match { - case machine.Jump(label, arguments) => + case machine.Jump(label, arguments) => emit(Comment(s"jump ${label.name}")) shareValues(arguments, Set()) emit(callLabel(transform(label), arguments.map(transform))) @@ -186,7 +190,7 @@ object Transformer { emit(callLabel(LocalReference(methodType, functionName), LocalReference(objectType, objectName) +: arguments)) RetVoid() - case machine.Var(ref @ machine.Variable(name, machine.Type.Reference(tpe)), init, retType, rest) => + case machine.Var(ref@machine.Variable(name, machine.Type.Reference(tpe)), init, retType, rest) => val environment = List(init) val returnAddressName = freshName("returnAddress") val returnType = transform(retType) @@ -394,17 +398,17 @@ object Transformer { } transform(rest) - case machine.Statement.Hole(span) => - val posfmt = span.range.from.format - emit(Comment(s"Hole @ $posfmt")) + case machine.Statement.Hole(span) => + val posfmt = span.range.from.format + emit(Comment(s"Hole @ $posfmt")) - // Reused from LiteralUTF8String - val utf8 = (posfmt + "\u0000").getBytes("UTF-8") // null-terminated - val litName = freshName("hole_pos") - emit(GlobalConstant(s"$litName.lit", ConstantArray(IntegerType8(), utf8.map { b => ConstantInteger8(b) }.toList))) + // Reused from LiteralUTF8String + val utf8 = (posfmt + "\u0000").getBytes("UTF-8") // null-terminated + val litName = freshName("hole_pos") + emit(GlobalConstant(s"$litName.lit", ConstantArray(IntegerType8(), utf8.map { b => ConstantInteger8(b) }.toList))) - emit(Call("_", Ccc(), VoidType(), ConstantGlobal("hole"), List(ConstantGlobal(s"$litName.lit")))) - RetVoid() + emit(Call("_", Ccc(), VoidType(), ConstantGlobal("hole"), List(ConstantGlobal(s"$litName.lit")))) + RetVoid() } def transform(label: machine.Label): ConstantGlobal = @@ -432,15 +436,17 @@ object Transformer { val resumptionType = NamedType("Resumption"); val promptType = NamedType("Prompt"); val referenceType = NamedType("Reference"); + val headerType = NamedType("Header"); + val referenceCountType = NamedType("ReferenceCount"); def transform(tpe: machine.Type): Type = tpe match { - case machine.Positive() => positiveType - case machine.Negative() => negativeType - case machine.Type.Prompt() => promptType - case machine.Type.Stack() => resumptionType - case machine.Type.Int() => IntegerType64() - case machine.Type.Byte() => IntegerType8() - case machine.Type.Double() => DoubleType() + case machine.Positive() => positiveType + case machine.Negative() => negativeType + case machine.Type.Prompt() => promptType + case machine.Type.Stack() => resumptionType + case machine.Type.Int() => IntegerType64() + case machine.Type.Byte() => IntegerType8() + case machine.Type.Double() => DoubleType() case machine.Type.Reference(tpe) => referenceType } @@ -449,13 +455,13 @@ object Transformer { def typeSize(tpe: machine.Type): Int = tpe match { - case machine.Positive() => 16 - case machine.Negative() => 16 - case machine.Type.Prompt() => 8 // TODO Make fat? - case machine.Type.Stack() => 8 // TODO Make fat? - case machine.Type.Int() => 8 // TODO Make fat? - case machine.Type.Byte() => 1 - case machine.Type.Double() => 8 // TODO Make fat? + case machine.Positive() => 16 + case machine.Negative() => 16 + case machine.Type.Prompt() => 8 // TODO Make fat? + case machine.Type.Stack() => 8 // TODO Make fat? + case machine.Type.Int() => 8 // TODO Make fat? + case machine.Type.Byte() => 1 + case machine.Type.Double() => 8 // TODO Make fat? case machine.Type.Reference(_) => 16 } @@ -465,8 +471,10 @@ object Transformer { val terminator = prog; - val basicBlocks = FC.basicBlocks; FC.basicBlocks = null; - val instructions = BC.instructions; BC.instructions = null; + val basicBlocks = FC.basicBlocks; + FC.basicBlocks = null; + val instructions = BC.instructions; + BC.instructions = null; val entryBlock = BasicBlock("entry", instructions, terminator); val function = Function(Private(), Ccc(), VoidType(), name, parameters, entryBlock :: basicBlocks); @@ -480,8 +488,10 @@ object Transformer { val terminator = prog; - val basicBlocks = FC.basicBlocks; FC.basicBlocks = null; - val instructions = BC.instructions; BC.instructions = null; + val basicBlocks = FC.basicBlocks; + FC.basicBlocks = null; + val instructions = BC.instructions; + BC.instructions = null; val entryBlock = BasicBlock("entry", instructions, terminator); val function = Function(Private(), Tailcc(true), VoidType(), name, parameters :+ Parameter(stackType, "stack"), entryBlock :: basicBlocks); @@ -514,29 +524,17 @@ object Transformer { } def getEraser(environment: machine.Environment, kind: EraserKind)(using C: ModuleContext): Operand = { - val types = environment.map{ _.tpe }; - val freshEnvironment = environment.map{ + val types = environment.map { + _.tpe + }; + val freshEnvironment = environment.map { case machine.Variable(name, tpe) => machine.Variable(freshName(name), tpe) }; C.erasers.getOrElseUpdate((types, kind), { kind match { case ObjectEraser => - val eraser = ConstantGlobal(freshName("eraser")); - 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("release"), List(LocalReference(objectType, "object")))); - RetVoid() - }; - eraser + createEraserForNormalObjects(freshEnvironment) case StackEraser | StackFrameEraser => val eraser = ConstantGlobal(freshName("eraser")); defineFunction(eraser.name, List(Parameter(stackPointerType, "stackPointer"))) { @@ -557,8 +555,10 @@ object Transformer { } def getSharer(environment: machine.Environment, kind: SharerKind)(using C: ModuleContext): Operand = { - val types = environment.map{ _.tpe }; - val freshEnvironment = environment.map{ + val types = environment.map { + _.tpe + }; + val freshEnvironment = environment.map { case machine.Variable(name, tpe) => machine.Variable(freshName(name), tpe) }; @@ -675,11 +675,11 @@ object Transformer { loadAllVariables(currentEnvironmentPtr, chunk, alias, tpe) } else { // Here we do the same as for the normal slot plus some extra stuff - + // For non-last chunkedEnvironments, layout is chunk ++ [Pos link] - val tpe = StructureType(chunk.map { case machine.Variable(_, t) => transform(t) } :+ positiveType) // the same as for the normal case + positive Type for the link + val tpe = StructureType(chunk.map { case machine.Variable(_, t) => transform(t) } :+ positiveType) // the same as for the normal case + positive Type for the link loadAllVariables(currentEnvironmentPtr, chunk, alias, tpe) - + // Follow link to next block. The Link pointer is the last element of the chunk val linkPtr = LocalReference(PointerType(), freshName("link_pointer")) emit(GetElementPtr(linkPtr.name, tpe, currentEnvironmentPtr, List(0, chunk.length))) @@ -707,15 +707,15 @@ object Transformer { values match { case Nil => () case value :: values => - if values.contains(value) then { - shareValue(value); - loop(values) - } else if freeInBody.contains(value) then { - shareValue(value); - loop(values) - } else { - loop(values) - } + if values.contains(value) then { + shareValue(value); + loop(values) + } else if freeInBody.contains(value) then { + shareValue(value); + loop(values) + } else { + loop(values) + } } }; loop(values) @@ -728,17 +728,17 @@ object Transformer { def shareValue(value: machine.Variable)(using FunctionContext, BlockContext): Unit = { Option(value.tpe).collect { - case machine.Positive() => Call("_", Ccc(), VoidType(), sharePositive, List(transform(value))) - case machine.Negative() => Call("_", Ccc(), VoidType(), shareNegative, List(transform(value))) - case machine.Type.Stack() => Call("_", Ccc(), VoidType(), shareResumption, List(transform(value))) + case machine.Positive() => Call("_", Ccc(), VoidType(), sharePositive, List(transform(value))) + case machine.Negative() => Call("_", Ccc(), VoidType(), shareNegative, List(transform(value))) + case machine.Type.Stack() => Call("_", Ccc(), VoidType(), shareResumption, List(transform(value))) }.map(emit) } def eraseValue(value: machine.Variable)(using FunctionContext, BlockContext): Unit = { Option(value.tpe).collect { - case machine.Positive() => Call("_", Ccc(), VoidType(), erasePositive, List(transform(value))) - case machine.Negative() => Call("_", Ccc(), VoidType(), eraseNegative, List(transform(value))) - case machine.Type.Stack() => Call("_", Ccc(), VoidType(), eraseResumption, List(transform(value))) + case machine.Positive() => Call("_", Ccc(), VoidType(), erasePositive, List(transform(value))) + case machine.Negative() => Call("_", Ccc(), VoidType(), eraseNegative, List(transform(value))) + case machine.Type.Stack() => Call("_", Ccc(), VoidType(), eraseResumption, List(transform(value))) }.map(emit) } @@ -825,7 +825,7 @@ object Transformer { def setStack(stack: Operand)(using C: BlockContext) = C.stack = stack; - val escapeSeqs: Map[Char, String] = Map('\'' -> raw"'", '\"' -> raw"\"", '\\' -> raw"\\", '\n' -> raw"\n", '\t' -> raw"\t", '\r' -> raw"\r") + val escapeSeqs: Map[Char, String] = Map('\'' -> raw"'", '\"' -> raw"\"", '\\' -> raw" \\ ", '\n' -> raw" \ n", '\t' -> raw" \ t", '\r' -> raw" \ r") def escape(scalaString: String): String = scalaString.foldLeft(StringBuilder()) { (acc, c) => @@ -903,7 +903,7 @@ object Transformer { // we create a new Positive Object with tag 0... emit(InsertValue(temporaryName, ConstantAggregateZero(positiveType), ConstantInt(0), 0)) // we create a fake Link Tag 0 that we just use for store a tag in the Pos emit(InsertValue(linkValName, LocalReference(positiveType, temporaryName), headObject, 1)) - + // ... and inject it into our environment... val extendedEnv = envChunk :+ machine.Variable(linkValName, machine.Positive()) @@ -937,4 +937,80 @@ object Transformer { emit(Load(name, transform(tpe), field, alias)) } } + + /** + * Creates an eraser for normal objects (e.g. Pos and Neg). + * When @eraseObject is called, we also call the eraser. And for Pos and Neg objects, that is the eraser. + * The object is called 2 times. + * 1. Phase: When call @eraseObject -> We call release to prepend the object to our to-do-list. + * 2. Phase: When call acquire -> We call %erase on each children, so the object can be reused again. + * + * @param freshEnvironment the variables of the object + * @return the eraser + */ + private def createEraserForNormalObjects(freshEnvironment: machine.Environment)(using C: ModuleContext): Operand = { + val eraser = ConstantGlobal(freshName("eraser")) + defineFunction(eraser.name, List(Parameter(objectType, "object"))) { + val objectRefCountPtr = LocalReference(PointerType(), freshName("objectReferenceCount")) + val referenceCount = LocalReference(referenceCountType, freshName("referenceCount")) + val releaseLabel = freshName("release") + val eraseChildrenLabel = freshName("eraseChildren") + + // Entry block: Check reference count to determine phase + // Switch: if refCount == 0, go to "release" (first phase), else "eraseChildren" (second phase) + // Note: When refCount == 0, this is the first call from eraseObject -> just release + // When refCount != 0, this is the second call from acquire -> erase children + emit(GetElementPtr(objectRefCountPtr.name, headerType, LocalReference(objectType, "object"), List(0, 0))) + emit(Load(referenceCount.name, referenceCountType, objectRefCountPtr, Object)) + + createAndEmitReleaseBasicBlock(releaseLabel) + createAndEmitEraseChildrenBasicBlock(freshEnvironment, eraseChildrenLabel) + + // Return switch terminator from entry block + Switch(referenceCount, eraseChildrenLabel, List((0, releaseLabel))) + } + + eraser + } + + private def createAndEmitReleaseBasicBlock(releaseLabel: String)(using ModuleContext, FunctionContext): Unit = { + emit(BasicBlock(releaseLabel, List( + Call("", Ccc(), VoidType(), ConstantGlobal("release"), List(LocalReference(objectType, "object"))) + ), RetVoid())) + } + + private def createAndEmitEraseChildrenBasicBlock(freshEnvironment: Environment, eraseChildrenLabel: String)(using ModuleContext, FunctionContext): Unit = { + val environmentRef = LocalReference(environmentType, freshName("environment")) + val eraseChildrenInstructions = mutable.ListBuffer[Instruction]() + + // Get environment pointer + eraseChildrenInstructions += Call(environmentRef.name, Ccc(), environmentType, ConstantGlobal("objectEnvironment"), List(LocalReference(objectType, "object"))) + + // Load environment variables and erase them + val envType = environmentType(freshEnvironment) + freshEnvironment.zipWithIndex.foreach { case (machine.Variable(varName, tpe), i) => + val fieldPtr = LocalReference(PointerType(), freshName(varName + "_pointer")) + val loadedVarName = freshName(varName) + // Erase the value based on its type + tpe match { + case machine.Positive() => + emitElementPtrAndLoadOfEnvironmentVariable(environmentRef, eraseChildrenInstructions, envType, tpe, i, fieldPtr, loadedVarName) + eraseChildrenInstructions += Call("_", Ccc(), VoidType(), erasePositive, List(LocalReference(transform(tpe), loadedVarName))) + case machine.Negative() => + emitElementPtrAndLoadOfEnvironmentVariable(environmentRef, eraseChildrenInstructions, envType, tpe, i, fieldPtr, loadedVarName) + eraseChildrenInstructions += Call("_", Ccc(), VoidType(), eraseNegative, List(LocalReference(transform(tpe), loadedVarName))) + case machine.Type.Stack() => + emitElementPtrAndLoadOfEnvironmentVariable(environmentRef, eraseChildrenInstructions, envType, tpe, i, fieldPtr, loadedVarName) + eraseChildrenInstructions += Call("_", Ccc(), VoidType(), eraseResumption, List(LocalReference(transform(tpe), loadedVarName))) + case _ => // Int, Byte, Double, Reference, etc. don't need erasing + } + } + + emit(BasicBlock(eraseChildrenLabel, eraseChildrenInstructions.toList, RetVoid())) + } + + private def emitElementPtrAndLoadOfEnvironmentVariable(environmentRef: LocalReference, eraseChildrenInstructions: ListBuffer[Instruction], envType: LLVMType, tpe: machine.Type, i: Int, fieldPtr: LocalReference, loadedVarName: String) = { + eraseChildrenInstructions += GetElementPtr(fieldPtr.name, envType, environmentRef, List(0, i)) + eraseChildrenInstructions += Load(loadedVarName, transform(tpe), fieldPtr, Object) + } } diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 944fdd0d91..f79f4ddc15 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -4,19 +4,21 @@ #include /** - * @brief Block-Struktur für die Freelist. + * @brief Block-Struktur für die Freelist & To-Do-List. * * Jeder freie Block zeigt auf den nächsten freien Block. */ typedef struct Block { struct Block* next; + void (*eraser)(void *object); } Block; // Globale Variablen const bool DEBUG = false; static Block* freeList = NULL; // Head of the Freelist +static Block* todoList = NULL; // Head of the To-Do-List static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage static const int blockSize = 64; // The size of each block (64B) @@ -24,10 +26,10 @@ static const int blockSize = 64; // The size of each block (64B) // How much storage do we allocate at the beginning of a program? =4GB static const size_t chunkSize = (size_t)4294967296ULL; -void printFreeList(const Block *freeList) { - printf("All Elements in Free List:\n"); +void printTodoList(const Block *todoList) { + printf("All Elements in Todo List:\n"); - const Block *b = freeList; + const Block *b = todoList; while (b != NULL) { printf(" Block at %p\n", (void *)b); b = b->next; @@ -62,13 +64,13 @@ void cInitializeMemory() /** * 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. + * Wenn die Todolist leer ist, nimmt er den nächsten Block im Chunk. + * Wenn die Todolist nicht leer ist, nimmt er den ersten Eintrag daraus. */ void* acquire(uint8_t size) { - // 1. Falls Freelist leer ist → neuer Block - if (freeList == NULL) + // 1. Falls Todolist leer ist → neuer Block + if (todoList == NULL) { if (nextUnusedBlock + blockSize > endOfChunk) { @@ -84,9 +86,19 @@ void* acquire(uint8_t size) return block; } - // 2. Falls Freelist nicht leer ist → wiederverwenden - Block* block = freeList; - freeList = block->next; + // 2. Falls Todolist nicht leer ist → wiederverwenden + Block* block = todoList; + todoList = block->next; + +// block->eraser((void*)block); + +// // Zweiter Eraser-Aufruf: Children löschen +// // Der Eraser wurde beim ersten Aufruf (in eraseObject) nicht aufgerufen, +// // sondern nur das Objekt wurde zur todoList hinzugefügt. +// // Jetzt, beim Wiederverwenden, rufen wir den Eraser auf, um die Children zu löschen. +// if (block->eraser != NULL) { +// block->eraser((void*)block); +// } if (DEBUG) { printf("[acquire] Reusing block: %p\n", (void*)block); @@ -96,17 +108,19 @@ void* acquire(uint8_t size) /** - * Gibt einen Block zurück in die Freelist. + * Gibt einen Block zurück in die To-Do-List. * - * @param ptr Zeiger auf den Block. + * @param ptr Zeiger auf den Block (noch als Object mit Header). */ void release(void* ptr) { if (!ptr) return; Block* block = (Block*)ptr; - block->next = freeList; - freeList = block; + + // Block zur todoList hinzufügen (von vorne) + block->next = todoList; + todoList = block; if (DEBUG) { printf("[release] Freed block: %p\n", ptr); @@ -117,10 +131,10 @@ void release(void* ptr) * */ void assertNumberLeakedBlocks(int expected) { - // Count how many blocks are in the freelist - size_t numberOfElementsInFreeList = 0; - for (const Block* b = freeList; b != NULL; b = b->next) { - numberOfElementsInFreeList++; + // Count how many blocks are in the todoList + size_t numberOfElementsInTodoList = 0; + for (const Block* b = todoList; b != NULL; b = b->next) { + numberOfElementsInTodoList++; } // Total number of blocks that were ever allocated @@ -128,14 +142,14 @@ void assertNumberLeakedBlocks(int expected) { const size_t totalAllocated = (nextUnusedBlock - firstBlock) / blockSize; // Calculate the number of leaked blocks - const size_t numberOfLeakedBlocks = totalAllocated - numberOfElementsInFreeList; + const size_t numberOfLeakedBlocks = totalAllocated - numberOfElementsInTodoList; // Report if there are any leaked blocks if (numberOfLeakedBlocks != expected) { printf("firstBlock: %p\n", (void*)firstBlock); printf("nextUnusedBlock: %p\n", (void*)nextUnusedBlock); - printFreeList(freeList); + printTodoList(todoList); // we print all remaining block addresses // TODO df: Is commented out because it creates warning now // for (uint8_t* p = firstBlock; p < nextUnusedBlock; p += blockSize) { @@ -151,7 +165,7 @@ void assertNumberLeakedBlocks(int expected) { // } // } - printf("Test failed!, Total Allocated: %zu, Elements in Free List: %zu \n", totalAllocated, numberOfElementsInFreeList); + printf("Test failed!, Total Allocated: %zu, Elements in Free List: %zu \n", totalAllocated, numberOfElementsInTodoList); exit(1); } } @@ -179,8 +193,8 @@ void assertThatAllAsynchronousOperationsAreFinished() { void testIfAllBlocksAreFreed() { assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles - assertLeakFree(); // testing malloc & calloc & free - assertNumberLeakedBlocks(0); // testing aquire & release +// assertLeakFree(); // testing malloc & calloc & free +// assertNumberLeakedBlocks(0); // testing aquire & release } From 86654faf678c09416b1304bf2c3e05b7ad8cbf8f Mon Sep 17 00:00:00 2001 From: Flat Date: Tue, 11 Nov 2025 17:21:45 +0100 Subject: [PATCH 43/57] Deleted monitoring for benchmarking --- libraries/common/array.effekt | 6 +- libraries/llvm/bytearray.c | 6 +- libraries/llvm/cMalloc.c | 4 +- libraries/llvm/io.c | 130 +++++++++++++++++----------------- libraries/llvm/rts.ll | 8 +-- 5 files changed, 75 insertions(+), 79 deletions(-) diff --git a/libraries/common/array.effekt b/libraries/common/array.effekt index 11557af7b5..f96590cb85 100644 --- a/libraries/common/array.effekt +++ b/libraries/common/array.effekt @@ -18,7 +18,7 @@ extern type Array[T] */ extern llvm """ -declare noalias ptr @cCalloc(i64, i64) +declare noalias ptr @calloc(i64, i64) define void @array_erase(%Object %object) { entry: @@ -43,7 +43,7 @@ loop: %cmp = icmp ult i64 %inc, %size br i1 %cmp, label %loop, label %exit exit: - call void @cFree(%Object %object) ; we delete the array + call void @free(%Object %object) ; we delete the array ret void } """ @@ -56,7 +56,7 @@ extern def allocate[T](size: Int) at global: Array[T] = llvm """ %size0 = shl i64 ${size}, 4 %size1 = add i64 %size0, 24 - %calloc = tail call noalias ptr @cCalloc(i64 %size1, i64 1) + %calloc = tail call noalias ptr @calloc(i64 %size1, i64 1) %eraser_pointer = getelementptr ptr, ptr %calloc, i64 1 %size_pointer = getelementptr ptr, ptr %calloc, i64 2 store i64 0, ptr %calloc diff --git a/libraries/llvm/bytearray.c b/libraries/llvm/bytearray.c index f44663e53e..a0585332ff 100644 --- a/libraries/llvm/bytearray.c +++ b/libraries/llvm/bytearray.c @@ -4,8 +4,6 @@ #include // For memcopy #include -#include "memtrack.c" - /** We represent bytearrays like positive types. * * - The field `tag` contains the size @@ -20,12 +18,12 @@ void c_bytearray_erase_noop(void* object) { - cFree(object); + free(object); } struct Pos c_bytearray_new(const Int size) { int object_size = sizeof(struct Header) + size; - void *objPtr = cMalloc(object_size); + void *objPtr = malloc(object_size); struct Header *headerPtr = objPtr; *headerPtr = (struct Header) { .rc = 0, .eraser = c_bytearray_erase_noop, }; return (struct Pos) { diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index f79f4ddc15..ad112594ae 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -2,6 +2,7 @@ #include #include #include +#include "memtrack.c" /** * @brief Block-Struktur für die Freelist & To-Do-List. @@ -193,9 +194,8 @@ void assertThatAllAsynchronousOperationsAreFinished() { void testIfAllBlocksAreFreed() { assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles -// assertLeakFree(); // testing malloc & calloc & free + assertLeakFree(); // testing malloc & calloc & free // assertNumberLeakedBlocks(0); // testing aquire & release - } // small testprogram to test the allocator diff --git a/libraries/llvm/io.c b/libraries/llvm/io.c index 710adf6452..154de35ae4 100644 --- a/libraries/llvm/io.c +++ b/libraries/llvm/io.c @@ -19,7 +19,7 @@ void c_io_println(String text) { String c_io_readln() { uint64_t capacity = 64; uint64_t length = 0; - char* buffer = cMalloc(capacity * sizeof(char)); + char* buffer = malloc(capacity * sizeof(char)); for (int c = getchar(); c != '\n' && c != EOF; c = getchar()) { if (length >= capacity) { @@ -31,7 +31,7 @@ String c_io_readln() { } String result = c_bytearray_construct(length, (const uint8_t*)buffer); - cFree(buffer); + free(buffer); return result; } @@ -74,7 +74,7 @@ void c_resume_int_fs(uv_fs_t* request) { Stack stack = (Stack)request->data; uv_fs_req_cleanup(request); - cFree(request); + free(request); resume_Int(stack, result); } @@ -85,7 +85,7 @@ void c_fs_open(String path, int flags, Stack stack) { char* path_str = c_bytearray_into_nullterminated_string(path); erasePositive((struct Pos) path); - uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); + uv_fs_t* request = malloc(sizeof(uv_fs_t)); request->data = stack; int result = uv_fs_open(uv_default_loop(), request, path_str, flags, 0777, c_resume_int_fs); @@ -93,12 +93,12 @@ void c_fs_open(String path, int flags, Stack stack) { if (result < 0) { // TODO free path_str? uv_fs_req_cleanup(request); - cFree(request); + free(request); resume_Int(stack, result); } // Free the string since libuv copies it into the request - cFree(path_str); + free(path_str); return; } @@ -121,9 +121,9 @@ void c_fs_cb(uv_fs_t* request) { int64_t result = (int64_t)request->result; uv_fs_req_cleanup(request); - cFree(request); + free(request); erasePositive(closure->buffer); - cFree(closure); + free(closure); resume_Int(stack, result); } @@ -131,21 +131,21 @@ void c_fs_read(Int file, struct Pos buffer, Int offset, Int size, Int position, uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer) + offset), size); - io_closure_t* read_closure = cMalloc(sizeof(io_closure_t)); + io_closure_t* read_closure = malloc(sizeof(io_closure_t)); read_closure->stack = stack; read_closure->buffer = buffer; read_closure->offset = offset; - uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); + uv_fs_t* request = malloc(sizeof(uv_fs_t)); request->data = read_closure; int result = uv_fs_read(uv_default_loop(), request, file, &buf, 1, position, c_fs_cb); if (result < 0) { uv_fs_req_cleanup(request); - cFree(request); + free(request); resume_Int(stack, result); - cFree(read_closure); + free(read_closure); } } @@ -154,34 +154,34 @@ void c_fs_write(Int file, struct Pos buffer, Int offset, Int size, Int position, uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer) + offset), size); - io_closure_t* write_closure = cMalloc(sizeof(io_closure_t)); + io_closure_t* write_closure = malloc(sizeof(io_closure_t)); write_closure->stack = stack; write_closure->buffer = buffer; write_closure->offset = offset; - uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); + uv_fs_t* request = malloc(sizeof(uv_fs_t)); request->data = write_closure; int result = uv_fs_write(uv_default_loop(), request, file, &buf, 1, position, c_fs_cb); if (result < 0) { uv_fs_req_cleanup(request); - cFree(request); + free(request); resume_Int(stack, result); - cFree(write_closure); + free(write_closure); } } void c_fs_close(Int file, Stack stack) { - uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); + uv_fs_t* request = malloc(sizeof(uv_fs_t)); request->data = stack; int result = uv_fs_close(uv_default_loop(), request, file, c_resume_int_fs); if (result < 0) { uv_fs_req_cleanup(request); - cFree(request); + free(request); resume_Int(stack, result); } } @@ -192,7 +192,7 @@ void c_fs_mkdir(String path, Stack stack) { char* path_str = c_bytearray_into_nullterminated_string(path); erasePositive((struct Pos) path); - uv_fs_t* request = cMalloc(sizeof(uv_fs_t)); + uv_fs_t* request = malloc(sizeof(uv_fs_t)); request->data = stack; // Perform the mkdir operation @@ -200,12 +200,12 @@ void c_fs_mkdir(String path, Stack stack) { if (result < 0) { uv_fs_req_cleanup(request); - cFree(request); + free(request); resume_Int(stack, result); } // Free the string since libuv copies it into the request - cFree(path_str); + free(path_str); return; } @@ -217,12 +217,12 @@ void c_tcp_connect_cb(uv_connect_t* request, int status) { Stack stack = (Stack)request->data; if (status < 0) { - uv_close((uv_handle_t*)request->handle, (uv_close_cb)cFree); - cFree(request); + uv_close((uv_handle_t*)request->handle, (uv_close_cb)free); + free(request); resume_Int(stack, status); } else { int64_t handle = (int64_t)request->handle; - cFree(request); + free(request); resume_Int(stack, handle); } } @@ -231,26 +231,26 @@ void c_tcp_connect(String host, Int port, Stack stack) { char* host_str = c_bytearray_into_nullterminated_string(host); erasePositive(host); - uv_tcp_t* tcp_handle = cMalloc(sizeof(uv_tcp_t)); + uv_tcp_t* tcp_handle = malloc(sizeof(uv_tcp_t)); int result = uv_tcp_init(uv_default_loop(), tcp_handle); if (result < 0) { - cFree(tcp_handle); - cFree(host_str); + free(tcp_handle); + free(host_str); resume_Int(stack, result); return; } - uv_connect_t* connect_req = cMalloc(sizeof(uv_connect_t)); + uv_connect_t* connect_req = malloc(sizeof(uv_connect_t)); connect_req->data = stack; struct sockaddr_in addr; result = uv_ip4_addr(host_str, port, &addr); - cFree(host_str); + free(host_str); if (result < 0) { - cFree(tcp_handle); - cFree(connect_req); + free(tcp_handle); + free(connect_req); resume_Int(stack, result); return; } @@ -258,8 +258,8 @@ void c_tcp_connect(String host, Int port, Stack stack) { result = uv_tcp_connect(connect_req, tcp_handle, (const struct sockaddr*)&addr, c_tcp_connect_cb); if (result < 0) { - cFree(tcp_handle); - cFree(connect_req); + free(tcp_handle); + free(connect_req); resume_Int(stack, result); return; } @@ -272,7 +272,7 @@ void c_tcp_read_cb(uv_stream_t* stream, ssize_t bytes_read, const uv_buf_t* buf) uv_read_stop(stream); erasePositive(read_closure->buffer); - cFree(read_closure); + free(read_closure); resume_Int(stack, (int64_t)bytes_read); } @@ -289,7 +289,7 @@ void c_tcp_read(Int handle, struct Pos buffer, Int offset, Int size, Stack stack (void)size; uv_stream_t* stream = (uv_stream_t*)handle; - io_closure_t* read_closure = cMalloc(sizeof(io_closure_t)); + io_closure_t* read_closure = malloc(sizeof(io_closure_t)); read_closure->stack = stack; read_closure->buffer = buffer; read_closure->offset = offset; @@ -298,7 +298,7 @@ void c_tcp_read(Int handle, struct Pos buffer, Int offset, Int size, Stack stack int result = uv_read_start(stream, c_tcp_read_alloc_cb, c_tcp_read_cb); if (result < 0) { - cFree(read_closure); + free(read_closure); stream->data = NULL; resume_Int(stack, result); } @@ -306,10 +306,10 @@ void c_tcp_read(Int handle, struct Pos buffer, Int offset, Int size, Stack stack void c_tcp_write_cb(uv_write_t* request, int status) { io_closure_t* write_closure = (io_closure_t*)request->data; - cFree(request); + free(request); erasePositive(write_closure->buffer); Stack stack = write_closure->stack; - cFree(write_closure); + free(write_closure); resume_Int(stack, (int64_t)status); } @@ -318,26 +318,26 @@ void c_tcp_write(Int handle, struct Pos buffer, Int offset, Int size, Stack stac uv_buf_t buf = uv_buf_init((char*)(c_bytearray_data(buffer) + offset), size); - io_closure_t* write_closure = cMalloc(sizeof(io_closure_t)); + io_closure_t* write_closure = malloc(sizeof(io_closure_t)); write_closure->stack = stack; write_closure->buffer = buffer; write_closure->offset = offset; - uv_write_t* request = cMalloc(sizeof(uv_write_t)); + uv_write_t* request = malloc(sizeof(uv_write_t)); request->data = write_closure; int result = uv_write(request, stream, &buf, 1, c_tcp_write_cb); if (result < 0) { - cFree(request); + free(request); resume_Int(stack, result); - cFree(write_closure); + free(write_closure); } } void c_tcp_close_cb(uv_handle_t* handle) { Stack stack = (Stack)handle->data; - cFree(handle); + free(handle); resume_Pos(stack, Unit); } @@ -351,27 +351,27 @@ Int c_tcp_bind(String host, Int port) { char* host_str = c_bytearray_into_nullterminated_string(host); erasePositive(host); - uv_tcp_t* tcp_handle = cMalloc(sizeof(uv_tcp_t)); + uv_tcp_t* tcp_handle = malloc(sizeof(uv_tcp_t)); int result = uv_tcp_init(uv_default_loop(), tcp_handle); if (result < 0) { - cFree(tcp_handle); - cFree(host_str); + free(tcp_handle); + free(host_str); return result; } struct sockaddr_in addr; result = uv_ip4_addr(host_str, port, &addr); - cFree(host_str); + free(host_str); if (result < 0) { - uv_close((uv_handle_t*)tcp_handle, (uv_close_cb)cFree); + uv_close((uv_handle_t*)tcp_handle, (uv_close_cb)free); return result; } result = uv_tcp_bind(tcp_handle, (const struct sockaddr*)&addr, 0); if (result < 0) { - uv_close((uv_handle_t*)tcp_handle, (uv_close_cb)cFree); + uv_close((uv_handle_t*)tcp_handle, (uv_close_cb)free); return result; } @@ -390,19 +390,19 @@ void c_tcp_listen_cb(uv_stream_t* server, int status) { if (status < 0) { server->data = NULL; - cFree(listen_closure); + free(listen_closure); erasePositive(closure_handler); resume_Int(closure_stack, status); return; } - uv_tcp_t* client = cMalloc(sizeof(uv_tcp_t)); + uv_tcp_t* client = malloc(sizeof(uv_tcp_t)); int result = uv_tcp_init(uv_default_loop(), client); if (result < 0) { - cFree(client); + free(client); server->data = NULL; - cFree(listen_closure); + free(listen_closure); erasePositive(closure_handler); resume_Int(closure_stack, result); return; @@ -410,9 +410,9 @@ void c_tcp_listen_cb(uv_stream_t* server, int status) { result = uv_accept(server, (uv_stream_t*)client); if (result < 0) { - uv_close((uv_handle_t*)client, (uv_close_cb)cFree); + uv_close((uv_handle_t*)client, (uv_close_cb)free); server->data = NULL; - cFree(listen_closure); + free(listen_closure); erasePositive(closure_handler); resume_Int(closure_stack, result); return; @@ -425,14 +425,14 @@ void c_tcp_listen_cb(uv_stream_t* server, int status) { void c_tcp_listen(Int listener, struct Pos handler, Stack stack) { uv_stream_t* server = (uv_stream_t*)listener; - tcp_listen_closure_t* listen_closure = cMalloc(sizeof(tcp_listen_closure_t)); + tcp_listen_closure_t* listen_closure = malloc(sizeof(tcp_listen_closure_t)); listen_closure->stack = stack; listen_closure->handler = handler; server->data = listen_closure; int result = uv_listen(server, SOMAXCONN, c_tcp_listen_cb); if (result < 0) { - cFree(listen_closure); + free(listen_closure); erasePositive(handler); resume_Int(stack, result); return; @@ -449,7 +449,7 @@ void c_tcp_shutdown(Int handle, Stack stack) { if (listen_closure) { Stack closure_stack = listen_closure->stack; struct Pos closure_handler = listen_closure->handler; - cFree(listen_closure); + free(listen_closure); erasePositive(closure_handler); resume_Int(closure_stack, 0); } @@ -555,13 +555,13 @@ void c_resume_unit_timer(uv_timer_t* handle) { Stack stack = handle->data; uv_timer_stop(handle); - uv_close((uv_handle_t*)handle, (uv_close_cb)cFree); + uv_close((uv_handle_t*)handle, (uv_close_cb)free); resume_Pos(stack, Unit); } void c_timer_start(Int millis, Stack stack) { - uv_timer_t* timer = cMalloc(sizeof(uv_timer_t)); + uv_timer_t* timer = malloc(sizeof(uv_timer_t)); timer->data = stack; uv_timer_init(uv_default_loop(), timer); @@ -620,7 +620,7 @@ void c_promise_erase_listeners(void *object) { while (current != NULL) { head = current->head; tail = current->tail; - cFree(current); + free(current); eraseStack(head); current = tail; }; @@ -630,14 +630,14 @@ void c_promise_erase_listeners(void *object) { erasePositive(promise->payload.value); break; } - cFree(promise); // Free the promise object itself + free(promise); // Free the promise object itself } void c_promise_resume_listeners(Listeners* listeners, struct Pos value) { if (listeners != NULL) { Stack head = listeners->head; Listeners* tail = listeners->tail; - cFree(listeners); + free(listeners); c_promise_resume_listeners(tail, value); sharePositive(value); resume_Pos(head, value); @@ -693,7 +693,7 @@ void c_promise_await(struct Pos promise, Stack stack) { head = p->payload.listeners.head; tail = p->payload.listeners.tail; if (head != NULL) { - node = (Listeners*)cMalloc(sizeof(Listeners)); + node = (Listeners*)malloc(sizeof(Listeners)); node->head = head; node->tail = tail; p->payload.listeners.head = stack; @@ -713,7 +713,7 @@ void c_promise_await(struct Pos promise, Stack stack) { } struct Pos c_promise_make() { - Promise* promise = (Promise*)cMalloc(sizeof(Promise)); + Promise* promise = (Promise*)malloc(sizeof(Promise)); promise->rc = 0; promise->eraser = c_promise_erase_listeners; diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 8fdb94b73f..07816ff3a9 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -99,8 +99,6 @@ declare ptr @acquire(i64) declare void @release(ptr) declare void @testIfAllBlocksAreFreed() -declare ptr @cMalloc(i64) -declare void @cFree(ptr) declare ptr @malloc(i64) declare void @free(ptr) @@ -534,7 +532,7 @@ decrement: ret void free: - call void @cFree(%Prompt %prompt) ;df: wird gecalled, obwohl es nie allocated wurde -> gibt warning + call void @free(%Prompt %prompt) ;df: wird gecalled, obwohl es nie allocated wurde -> gibt warning ret void } @@ -557,7 +555,7 @@ define private %Stack @underflowStack(%Stack %stack) { store %Stack null, ptr %promptStack_pointer, !alias.scope !13, !noalias !23 call void @erasePrompt(%Prompt %prompt) - call void @cFree(%Stack %stack) ;df: wird gecalled, obwohl es nie allocated wurde -> gibt warning + call void @free(%Stack %stack) ;df: wird gecalled, obwohl es nie allocated wurde -> gibt warning ret %Stack %rest } @@ -727,7 +725,7 @@ define private void @eraseFrames(%StackPointer %stackPointer) alwaysinline { define private void @freeStack(%StackPointer %stackPointer) alwaysinline { %base = getelementptr %StackValue, %StackPointer %stackPointer, i64 -1 - call void @cFree(%Base %base) + call void @free(%Base %base) ret void } From 259ce40f8bb36da664e30fb0037f7b97e621477b Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 13 Nov 2025 10:31:39 +0100 Subject: [PATCH 44/57] Optimization: refs are also running on acquire & release now. But caution: eraseChildren here is not optimized yet, because it always erases the field as well. But maybe you can do it only when necessary --- examples/flat/nooptimized/primitive_ref.check | 1 + .../flat/nooptimized/primitive_ref.effekt | 9 +++++++ examples/flat/nooptimized/ref_allocate.check | 1 + examples/flat/nooptimized/ref_allocate.effekt | 19 +++++++++++++ examples/flat/nooptimized/simple_ref.check | 1 + examples/flat/nooptimized/simple_ref.effekt | 20 ++++++++++++++ libraries/common/ref.effekt | 27 +++++++++++++------ 7 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 examples/flat/nooptimized/primitive_ref.check create mode 100644 examples/flat/nooptimized/primitive_ref.effekt create mode 100644 examples/flat/nooptimized/ref_allocate.check create mode 100644 examples/flat/nooptimized/ref_allocate.effekt create mode 100644 examples/flat/nooptimized/simple_ref.check create mode 100644 examples/flat/nooptimized/simple_ref.effekt diff --git a/examples/flat/nooptimized/primitive_ref.check b/examples/flat/nooptimized/primitive_ref.check new file mode 100644 index 0000000000..62f9457511 --- /dev/null +++ b/examples/flat/nooptimized/primitive_ref.check @@ -0,0 +1 @@ +6 \ No newline at end of file diff --git a/examples/flat/nooptimized/primitive_ref.effekt b/examples/flat/nooptimized/primitive_ref.effekt new file mode 100644 index 0000000000..6aa82602e4 --- /dev/null +++ b/examples/flat/nooptimized/primitive_ref.effekt @@ -0,0 +1,9 @@ +import ref + +def main() = { + val x = 5 + val y = 6 + val z = ref(x) + z.set(y) + println(z.get()) +} diff --git a/examples/flat/nooptimized/ref_allocate.check b/examples/flat/nooptimized/ref_allocate.check new file mode 100644 index 0000000000..c7930257df --- /dev/null +++ b/examples/flat/nooptimized/ref_allocate.check @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/examples/flat/nooptimized/ref_allocate.effekt b/examples/flat/nooptimized/ref_allocate.effekt new file mode 100644 index 0000000000..2b46ceaa72 --- /dev/null +++ b/examples/flat/nooptimized/ref_allocate.effekt @@ -0,0 +1,19 @@ +import ref + +// md = Person requires 2 saving slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int + ) + +def main() = { + val p1 = ref::allocate() + val p2 = Person(1, 2, 3, 4, 5, 6, 7) + p1.set(p2) + println(p1.get().field7) +} diff --git a/examples/flat/nooptimized/simple_ref.check b/examples/flat/nooptimized/simple_ref.check new file mode 100644 index 0000000000..f11c82a4cb --- /dev/null +++ b/examples/flat/nooptimized/simple_ref.check @@ -0,0 +1 @@ +9 \ No newline at end of file diff --git a/examples/flat/nooptimized/simple_ref.effekt b/examples/flat/nooptimized/simple_ref.effekt new file mode 100644 index 0000000000..3549dea0fa --- /dev/null +++ b/examples/flat/nooptimized/simple_ref.effekt @@ -0,0 +1,20 @@ +import ref + +// md = Person requires 2 saving slots +record Person( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int + ) + +def main() = { + val p1 = Person(1, 2, 3, 4, 5, 6, 7) + val p2 = Person(1, 2, 3, 4, 5, 6, 9) + val p3 = ref(p1) + p3.set(p2) + println(p3.get().field7) +} diff --git a/libraries/common/ref.effekt b/libraries/common/ref.effekt index 871a8d4dfb..fc23f394c9 100644 --- a/libraries/common/ref.effekt +++ b/libraries/common/ref.effekt @@ -11,12 +11,22 @@ extern js """ extern llvm """ define void @c_ref_erase(%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 + + entry: + %objectReferenceCount = getelementptr %Header, %Object %object, i64 0, i32 0 + %referenceCount = load %ReferenceCount, ptr %objectReferenceCount, !noalias !24, !alias.scope !14 + switch %ReferenceCount %referenceCount, label %eraseChildren [i64 0, label %release] + + release: + call ccc void @release(%Object %object) + ret void + + eraseChildren: + %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 @erasePositive(%Pos %field) + ret void } """ @@ -38,7 +48,8 @@ extern def allocate[T]() at global: Ref[T] = chez "(box #f)" llvm """ ; sizeof Header + sizeof Pos = 32 - %ref = tail call noalias ptr @malloc(i64 noundef 32) + %ref = tail call noalias ptr @acquire(i64 noundef 32) + %refEraser = getelementptr ptr, ptr %ref, i64 1 %fieldTag = getelementptr ptr, ptr %ref, i64 2 %fieldData_pointer = getelementptr ptr, ptr %ref, i64 3 @@ -60,7 +71,7 @@ extern def ref[T](init: T) at global: Ref[T] = %initTag = extractvalue %Pos ${init}, 0 %initObject_pointer = extractvalue %Pos ${init}, 1 ; sizeof Header + sizeof Pos = 32 - %ref = tail call noalias ptr @malloc(i64 noundef 32) + %ref = tail call noalias ptr @acquire(i64 noundef 32) %refEraser = getelementptr ptr, ptr %ref, i64 1 %refField = getelementptr ptr, ptr %ref, i64 2 From 410f56f5bea0c33075c338e2df636fff4b97b1eb Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 13 Nov 2025 10:32:31 +0100 Subject: [PATCH 45/57] We substituted the test in assertNumberLeakedBlocks with flushTodoList function instead because the test does not work anymore --- libraries/llvm/cMalloc.c | 47 +++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index ad112594ae..43f1249c2c 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -132,7 +132,7 @@ void release(void* ptr) * */ void assertNumberLeakedBlocks(int expected) { - // Count how many blocks are in the todoList + // Calculate the number of leaked blocks size_t numberOfElementsInTodoList = 0; for (const Block* b = todoList; b != NULL; b = b->next) { numberOfElementsInTodoList++; @@ -145,29 +145,26 @@ void assertNumberLeakedBlocks(int expected) { // Calculate the number of leaked blocks const size_t numberOfLeakedBlocks = totalAllocated - numberOfElementsInTodoList; - // Report if there are any leaked blocks + // if there leakes slots... if (numberOfLeakedBlocks != expected) { - printf("firstBlock: %p\n", (void*)firstBlock); - printf("nextUnusedBlock: %p\n", (void*)nextUnusedBlock); - - printTodoList(todoList); - - // we print all remaining block addresses // TODO df: Is commented out because it creates warning now -// for (uint8_t* p = firstBlock; p < nextUnusedBlock; p += blockSize) { -// bool inFreeList = false; -// for (const Block* b = freeList; b != NULL; b = b->next) { -// if ((void*)b == (void*)p) { -// inFreeList = true; -// break; -// } -// } -// if (!inFreeList) { -// printf(" still used: %p\n", p); -// } -// } - - printf("Test failed!, Total Allocated: %zu, Elements in Free List: %zu \n", totalAllocated, numberOfElementsInTodoList); - exit(1); + // we traverse each slot of the to-do-list and add it to the to-do list if missing + size_t numberOfSlotsToFlush = numberOfLeakedBlocks; + uint8_t* slotToAnalyze = (endOfChunk - chunkSize); // the start of the chunk + while(numberOfSlotsToFlush != expected) { + Block* blockToFlush = (Block*) slotToAnalyze; + bool isMissing = true; + for (const Block* b = todoList; b != NULL; b = b->next) { + if ((void*)b == (void*)blockToFlush) { + isMissing = false; + break; + } + } + if (isMissing) { + release(blockToFlush); + numberOfSlotsToFlush--; + } + slotToAnalyze = slotToAnalyze + blockSize; + } } } @@ -193,8 +190,8 @@ void assertThatAllAsynchronousOperationsAreFinished() { */ void testIfAllBlocksAreFreed() { - assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles - assertLeakFree(); // testing malloc & calloc & free +// assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles +// assertLeakFree(); // testing malloc & calloc & free // assertNumberLeakedBlocks(0); // testing aquire & release } From aaeed38e216216c1e06da2ad0b43a115e03447af Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 13 Nov 2025 10:33:19 +0100 Subject: [PATCH 46/57] optimization: "Normal" effekt slots load environment pointer only if there is at least one eraseable object --- .../effekt/generator/llvm/Transformer.scala | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 3860770945..a596c84ee1 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -944,7 +944,7 @@ object Transformer { * The object is called 2 times. * 1. Phase: When call @eraseObject -> We call release to prepend the object to our to-do-list. * 2. Phase: When call acquire -> We call %erase on each children, so the object can be reused again. - * + * * @param freshEnvironment the variables of the object * @return the eraser */ @@ -969,7 +969,7 @@ object Transformer { // Return switch terminator from entry block Switch(referenceCount, eraseChildrenLabel, List((0, releaseLabel))) } - + eraser } @@ -983,30 +983,44 @@ object Transformer { val environmentRef = LocalReference(environmentType, freshName("environment")) val eraseChildrenInstructions = mutable.ListBuffer[Instruction]() - // Get environment pointer - eraseChildrenInstructions += Call(environmentRef.name, Ccc(), environmentType, ConstantGlobal("objectEnvironment"), List(LocalReference(objectType, "object"))) - // Load environment variables and erase them val envType = environmentType(freshEnvironment) - freshEnvironment.zipWithIndex.foreach { case (machine.Variable(varName, tpe), i) => - val fieldPtr = LocalReference(PointerType(), freshName(varName + "_pointer")) - val loadedVarName = freshName(varName) - // Erase the value based on its type - tpe match { - case machine.Positive() => - emitElementPtrAndLoadOfEnvironmentVariable(environmentRef, eraseChildrenInstructions, envType, tpe, i, fieldPtr, loadedVarName) - eraseChildrenInstructions += Call("_", Ccc(), VoidType(), erasePositive, List(LocalReference(transform(tpe), loadedVarName))) - case machine.Negative() => - emitElementPtrAndLoadOfEnvironmentVariable(environmentRef, eraseChildrenInstructions, envType, tpe, i, fieldPtr, loadedVarName) - eraseChildrenInstructions += Call("_", Ccc(), VoidType(), eraseNegative, List(LocalReference(transform(tpe), loadedVarName))) - case machine.Type.Stack() => - emitElementPtrAndLoadOfEnvironmentVariable(environmentRef, eraseChildrenInstructions, envType, tpe, i, fieldPtr, loadedVarName) - eraseChildrenInstructions += Call("_", Ccc(), VoidType(), eraseResumption, List(LocalReference(transform(tpe), loadedVarName))) - case _ => // Int, Byte, Double, Reference, etc. don't need erasing + + val isAtLeastOneObjectToRelease: Boolean = + freshEnvironment.exists { + case machine.Variable(_, tpe) => + tpe match { + case machine.Positive() | machine.Negative() | machine.Type.Stack() => true + case _ => false + } } + + if (isAtLeastOneObjectToRelease) { + // Get environment pointer + eraseChildrenInstructions += Call(environmentRef.name, Ccc(), environmentType, ConstantGlobal("objectEnvironment"), List(LocalReference(objectType, "object"))) + + freshEnvironment.zipWithIndex.foreach { case (machine.Variable(varName, tpe), i) => + val fieldPtr = LocalReference(PointerType(), freshName(varName + "_pointer")) + val loadedVarName = freshName(varName) + // Erase the value based on its type + tpe match { + case machine.Positive() => + emitElementPtrAndLoadOfEnvironmentVariable(environmentRef, eraseChildrenInstructions, envType, tpe, i, fieldPtr, loadedVarName) + eraseChildrenInstructions += Call("_", Ccc(), VoidType(), erasePositive, List(LocalReference(transform(tpe), loadedVarName))) + case machine.Negative() => + emitElementPtrAndLoadOfEnvironmentVariable(environmentRef, eraseChildrenInstructions, envType, tpe, i, fieldPtr, loadedVarName) + eraseChildrenInstructions += Call("_", Ccc(), VoidType(), eraseNegative, List(LocalReference(transform(tpe), loadedVarName))) + case machine.Type.Stack() => + emitElementPtrAndLoadOfEnvironmentVariable(environmentRef, eraseChildrenInstructions, envType, tpe, i, fieldPtr, loadedVarName) + eraseChildrenInstructions += Call("_", Ccc(), VoidType(), eraseResumption, List(LocalReference(transform(tpe), loadedVarName))) + case _ => // Int, Byte, Double, Reference, etc. don't need erasing + } + } + emit(BasicBlock(eraseChildrenLabel, eraseChildrenInstructions.toList, RetVoid())) + } else { + // If not: we can return immediately, since we don't need to erase anything + emit(BasicBlock(eraseChildrenLabel, eraseChildrenInstructions.toList, RetVoid())) } - - emit(BasicBlock(eraseChildrenLabel, eraseChildrenInstructions.toList, RetVoid())) } private def emitElementPtrAndLoadOfEnvironmentVariable(environmentRef: LocalReference, eraseChildrenInstructions: ListBuffer[Instruction], envType: LLVMType, tpe: machine.Type, i: Int, fieldPtr: LocalReference, loadedVarName: String) = { From b24a7b844b6e67f8d9b2875939d1e4abdc4597e1 Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 13 Nov 2025 16:31:49 +0100 Subject: [PATCH 47/57] lazy reference counting fix. A sentinel slot is implemented such that the second call of the eraser calls the eraseChildren Block now. We do not flush the todo list here --- libraries/llvm/cMalloc.c | 94 +++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 43f1249c2c..659f4af7f4 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -5,7 +5,7 @@ #include "memtrack.c" /** - * @brief Block-Struktur für die Freelist & To-Do-List. + * @brief Block-Struktur für die To-Do-List & Used-List. * * Jeder freie Block zeigt auf den nächsten freien Block. */ @@ -15,11 +15,22 @@ typedef struct Block void (*eraser)(void *object); } Block; + +/** + * Sentinel-Wert: Ein Pointer, der garantiert niemals ein echter Heap-Pointer ist. + * + * Wir nutzen (Block*)1, weil: + * - echte Heap-Pointer immer mindestens 8-Byte aligned sind + * - 1 also niemals kollidiert + * - ReferenceCount != 0 bleibt (wenn Header überlagert ist) + */ +#define SENTINEL_BLOCK ((Block*)1) + // Globale Variablen const bool DEBUG = false; -static Block* freeList = NULL; // Head of the Freelist -static Block* todoList = NULL; // Head of the To-Do-List +static Block* todoList = SENTINEL_BLOCK; // Head of the To-Do-List +static Block* usedList = SENTINEL_BLOCK; // Head of the Used-List static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage static const int blockSize = 64; // The size of each block (64B) @@ -71,7 +82,7 @@ void cInitializeMemory() void* acquire(uint8_t size) { // 1. Falls Todolist leer ist → neuer Block - if (todoList == NULL) + if (todoList == SENTINEL_BLOCK) { if (nextUnusedBlock + blockSize > endOfChunk) { @@ -89,21 +100,14 @@ void* acquire(uint8_t size) // 2. Falls Todolist nicht leer ist → wiederverwenden Block* block = todoList; - todoList = block->next; - -// block->eraser((void*)block); - -// // Zweiter Eraser-Aufruf: Children löschen -// // Der Eraser wurde beim ersten Aufruf (in eraseObject) nicht aufgerufen, -// // sondern nur das Objekt wurde zur todoList hinzugefügt. -// // Jetzt, beim Wiederverwenden, rufen wir den Eraser auf, um die Children zu löschen. -// if (block->eraser != NULL) { -// block->eraser((void*)block); -// } if (DEBUG) { printf("[acquire] Reusing block: %p\n", (void*)block); } + + todoList = block->next; + block->eraser((void*)block); + return (void*)block; } @@ -117,15 +121,16 @@ void release(void* ptr) { if (!ptr) return; - Block* block = (Block*)ptr; + if (DEBUG) { + printf("[release] Freed block: %p\n", ptr); +// printf("[release] todo head: %p\n", todoList); +// printTodoList(todoList); + } // Block zur todoList hinzufügen (von vorne) + Block* block = (Block*)ptr; block->next = todoList; todoList = block; - - if (DEBUG) { - printf("[release] Freed block: %p\n", ptr); - } } /** @@ -134,7 +139,7 @@ void release(void* ptr) void assertNumberLeakedBlocks(int expected) { // Calculate the number of leaked blocks size_t numberOfElementsInTodoList = 0; - for (const Block* b = todoList; b != NULL; b = b->next) { + for (const Block* b = todoList; b != SENTINEL_BLOCK; b = b->next) { numberOfElementsInTodoList++; } @@ -146,26 +151,27 @@ void assertNumberLeakedBlocks(int expected) { const size_t numberOfLeakedBlocks = totalAllocated - numberOfElementsInTodoList; // if there leakes slots... - if (numberOfLeakedBlocks != expected) { - // we traverse each slot of the to-do-list and add it to the to-do list if missing - size_t numberOfSlotsToFlush = numberOfLeakedBlocks; - uint8_t* slotToAnalyze = (endOfChunk - chunkSize); // the start of the chunk - while(numberOfSlotsToFlush != expected) { - Block* blockToFlush = (Block*) slotToAnalyze; - bool isMissing = true; - for (const Block* b = todoList; b != NULL; b = b->next) { - if ((void*)b == (void*)blockToFlush) { - isMissing = false; - break; - } - } - if (isMissing) { - release(blockToFlush); - numberOfSlotsToFlush--; - } - slotToAnalyze = slotToAnalyze + blockSize; - } - } +// if (numberOfLeakedBlocks != expected) { +// exit(1); +//// // we traverse each slot of the to-do-list and add it to the to-do list if missing +//// size_t numberOfSlotsToFlush = numberOfLeakedBlocks; +//// uint8_t* slotToAnalyze = (endOfChunk - chunkSize); // the start of the chunk +//// while(numberOfSlotsToFlush != expected) { +//// Block* blockToFlush = (Block*) slotToAnalyze; +//// bool isMissing = true; +//// for (const Block* b = todoList; b != NULL; b = b->next) { +//// if ((void*)b == (void*)blockToFlush) { +//// isMissing = false; +//// break; +//// } +//// } +//// if (isMissing) { +//// release(blockToFlush); +//// numberOfSlotsToFlush--; +//// } +//// slotToAnalyze = slotToAnalyze + blockSize; +//// } +// } } // If there are any open libuv handles or requests, this function will block until they are closed. @@ -190,9 +196,9 @@ void assertThatAllAsynchronousOperationsAreFinished() { */ void testIfAllBlocksAreFreed() { -// assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles -// assertLeakFree(); // testing malloc & calloc & free -// assertNumberLeakedBlocks(0); // testing aquire & release + assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles + assertLeakFree(); // testing malloc & calloc & free + assertNumberLeakedBlocks(0); // testing aquire & release } // small testprogram to test the allocator From 22b1d6c9163a8febc62236ea12a824e25b8f5712 Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 14 Nov 2025 10:36:59 +0100 Subject: [PATCH 48/57] speedup of flushing todo-list approach by implementing a usedBitMask which stores the index of the block as bits with 1 = used, 0 = not in usage --- libraries/llvm/cMalloc.c | 122 ++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 659f4af7f4..d5b44dff33 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -30,7 +30,8 @@ typedef struct Block const bool DEBUG = false; static Block* todoList = SENTINEL_BLOCK; // Head of the To-Do-List -static Block* usedList = SENTINEL_BLOCK; // Head of the Used-List +static uint8_t* usedBitMask; +static size_t bitsetSize; static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage static const int blockSize = 64; // The size of each block (64B) @@ -38,6 +39,31 @@ static const int blockSize = 64; // The size of each block (64B) // How much storage do we allocate at the beginning of a program? =4GB static const size_t chunkSize = (size_t)4294967296ULL; +// Berechnet den Block-Index zu einer gegebenen Pointer‐Adresse. +// Dazu wird die Adresse in einen Abstand zum Chunk‐Anfang umgerechnet +// und anschließend durch die Blockgröße geteilt. +static inline size_t blockIndexFromPtr(const void* ptr) { + return ((uint8_t*)ptr - (endOfChunk - chunkSize)) / blockSize; +} + +// Markiert einen Block als „benutzt“, indem im Bitset das passende Bit +// gesetzt wird. index >> 3 wählt das Byte, index & 7 wählt das Bit darin. +static inline void usedSet(size_t index) { + usedBitMask[index >> 3] |= (1 << (index & 7)); +} + +// Markiert einen Block als „frei“, indem im Bitset das passende Bit +// gelöscht (auf 0 gesetzt) wird. +static inline void usedClear(size_t index) { + usedBitMask[index >> 3] &= ~(1 << (index & 7)); +} + +// Prüft, ob ein bestimmter Block genutzt ist. Schiebt das relevante Bit +// auf Position 0 und liest dann aus, ob es 1 oder 0 ist. +static inline bool usedTest(size_t index) { + return (usedBitMask[index >> 3] >> (index & 7)) & 1; +} + void printTodoList(const Block *todoList) { printf("All Elements in Todo List:\n"); @@ -64,8 +90,23 @@ void cInitializeMemory() nextUnusedBlock = mem; endOfChunk = mem + chunkSize; + + // ----------------------------- + // Bitset für alle 4GB Blöcke bauen + // ----------------------------- + size_t numberOfSlots = chunkSize / blockSize; + size_t requiredBytesForRepresentingEachSlot = (numberOfSlots + 7) / 8; + bitsetSize = requiredBytesForRepresentingEachSlot * 8; + + usedBitMask = calloc(requiredBytesForRepresentingEachSlot, 1); + if (!usedBitMask) { + printf("Error: bitset alloc failed!\n"); + exit(1); + } + if (DEBUG) { printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); + printf("[init] Bitset: %zu bytes for %zu blocks\n", requiredBytesForRepresentingEachSlot, numberOfSlots); // we require 8MB to store the bitmask } } @@ -95,19 +136,31 @@ void* acquire(uint8_t size) if (DEBUG) { printf("[acquire] New block: %p\n", block); } + + // Markieren des Blocks als frei + size_t idx = blockIndexFromPtr(block); +// printf("idx: %zu\n", idx); + usedSet(idx); + return block; } // 2. Falls Todolist nicht leer ist → wiederverwenden Block* block = todoList; + todoList = block->next; if (DEBUG) { printf("[acquire] Reusing block: %p\n", (void*)block); } - todoList = block->next; + // 3. Wir müssen die Children von dem Block erasen, um den Block neu befüllen zu können. block->eraser((void*)block); + // 4. Markieren des Blocks als verwendet + size_t idx = blockIndexFromPtr(block); +// printf("idx: %zu\n", idx); + usedSet(idx); + return (void*)block; } @@ -131,47 +184,48 @@ void release(void* ptr) Block* block = (Block*)ptr; block->next = todoList; todoList = block; + + // Markieren des Blocks als frei + size_t idx = blockIndexFromPtr(block); +// printf("idx: %zu\n", idx); + usedClear(idx); } /** -* +* Checks if we have leaked blocks. A block is leaked, if it is not in the to-do-list. +* If we have leaked blocks, we flush all of them to our to-do list. */ void assertNumberLeakedBlocks(int expected) { - // Calculate the number of leaked blocks + // Total number of blocks that were ever allocated + uint8_t* firstSlot = (endOfChunk - chunkSize); // the start of the chunk + const size_t totalAllocated = (nextUnusedBlock - firstSlot) / blockSize; + size_t numberOfElementsInTodoList = 0; for (const Block* b = todoList; b != SENTINEL_BLOCK; b = b->next) { numberOfElementsInTodoList++; } + size_t numberOfLeakedBlocks = 0; - // Total number of blocks that were ever allocated - const uint8_t* firstBlock = (endOfChunk - chunkSize); // the start of the chunk - const size_t totalAllocated = (nextUnusedBlock - firstBlock) / blockSize; - - // Calculate the number of leaked blocks - const size_t numberOfLeakedBlocks = totalAllocated - numberOfElementsInTodoList; - - // if there leakes slots... -// if (numberOfLeakedBlocks != expected) { -// exit(1); -//// // we traverse each slot of the to-do-list and add it to the to-do list if missing -//// size_t numberOfSlotsToFlush = numberOfLeakedBlocks; -//// uint8_t* slotToAnalyze = (endOfChunk - chunkSize); // the start of the chunk -//// while(numberOfSlotsToFlush != expected) { -//// Block* blockToFlush = (Block*) slotToAnalyze; -//// bool isMissing = true; -//// for (const Block* b = todoList; b != NULL; b = b->next) { -//// if ((void*)b == (void*)blockToFlush) { -//// isMissing = false; -//// break; -//// } -//// } -//// if (isMissing) { -//// release(blockToFlush); -//// numberOfSlotsToFlush--; -//// } -//// slotToAnalyze = slotToAnalyze + blockSize; -//// } -// } +// printf("requiredBytesForRepresentingEachSlot: %zu\n", bitsetSize); + + // Flush: turn all "used" (bit=1) blocks into todoList + for (uint8_t* ptr = (uint8_t*)firstSlot; ptr < nextUnusedBlock; ptr+= blockSize) { +// printf("block: %p\n", ptr); + size_t idx = blockIndexFromPtr(ptr); +// printf("idx: %zu\n", idx); + bool isUsed = usedTest(idx); +// printf("isUsed: %d\n", isUsed); + if (isUsed) { + release((Block*)ptr); + numberOfLeakedBlocks++; + } + } + + // Assert that the number of leaked blocks is as expected + if ((totalAllocated - numberOfElementsInTodoList - numberOfLeakedBlocks) != expected) { + printf("Totally allocated: %zu | numberOfElementsInTodoList %zu | numberOfLeakedBlocks %zu \n", totalAllocated, numberOfElementsInTodoList, numberOfLeakedBlocks); + exit(1); + } } // If there are any open libuv handles or requests, this function will block until they are closed. @@ -198,7 +252,7 @@ void testIfAllBlocksAreFreed() { assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles assertLeakFree(); // testing malloc & calloc & free - assertNumberLeakedBlocks(0); // testing aquire & release + assertNumberLeakedBlocks(0); // testing acquire & release } // small testprogram to test the allocator From d487a546d6e52d55c8f06474b37e52bd3b46f62e Mon Sep 17 00:00:00 2001 From: Flat Date: Fri, 14 Nov 2025 11:46:53 +0100 Subject: [PATCH 49/57] comment the flushTodo list out again for testing --- libraries/llvm/cMalloc.c | 123 +++++++++++---------------------------- 1 file changed, 34 insertions(+), 89 deletions(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index d5b44dff33..f0e5ae0de5 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -27,42 +27,14 @@ typedef struct Block #define SENTINEL_BLOCK ((Block*)1) // Globale Variablen - const bool DEBUG = false; static Block* todoList = SENTINEL_BLOCK; // Head of the To-Do-List -static uint8_t* usedBitMask; -static size_t bitsetSize; static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage -static const int blockSize = 64; // The size of each block (64B) +static const int chunkSize = 64; // The size of each chunk (64B) // How much storage do we allocate at the beginning of a program? =4GB -static const size_t chunkSize = (size_t)4294967296ULL; - -// Berechnet den Block-Index zu einer gegebenen Pointer‐Adresse. -// Dazu wird die Adresse in einen Abstand zum Chunk‐Anfang umgerechnet -// und anschließend durch die Blockgröße geteilt. -static inline size_t blockIndexFromPtr(const void* ptr) { - return ((uint8_t*)ptr - (endOfChunk - chunkSize)) / blockSize; -} - -// Markiert einen Block als „benutzt“, indem im Bitset das passende Bit -// gesetzt wird. index >> 3 wählt das Byte, index & 7 wählt das Bit darin. -static inline void usedSet(size_t index) { - usedBitMask[index >> 3] |= (1 << (index & 7)); -} - -// Markiert einen Block als „frei“, indem im Bitset das passende Bit -// gelöscht (auf 0 gesetzt) wird. -static inline void usedClear(size_t index) { - usedBitMask[index >> 3] &= ~(1 << (index & 7)); -} - -// Prüft, ob ein bestimmter Block genutzt ist. Schiebt das relevante Bit -// auf Position 0 und liest dann aus, ob es 1 oder 0 ist. -static inline bool usedTest(size_t index) { - return (usedBitMask[index >> 3] >> (index & 7)) & 1; -} +static const size_t totalAllocationSize = (size_t)4294967296ULL; void printTodoList(const Block *todoList) { printf("All Elements in Todo List:\n"); @@ -81,7 +53,7 @@ void printTodoList(const Block *todoList) { */ void cInitializeMemory() { - uint8_t* mem = (uint8_t*)malloc(chunkSize); + uint8_t* mem = (uint8_t*)malloc(totalAllocationSize); if (!mem) { printf("Error: malloc() failed!\n"); @@ -89,24 +61,10 @@ void cInitializeMemory() } nextUnusedBlock = mem; - endOfChunk = mem + chunkSize; - - // ----------------------------- - // Bitset für alle 4GB Blöcke bauen - // ----------------------------- - size_t numberOfSlots = chunkSize / blockSize; - size_t requiredBytesForRepresentingEachSlot = (numberOfSlots + 7) / 8; - bitsetSize = requiredBytesForRepresentingEachSlot * 8; - - usedBitMask = calloc(requiredBytesForRepresentingEachSlot, 1); - if (!usedBitMask) { - printf("Error: bitset alloc failed!\n"); - exit(1); - } + endOfChunk = mem + totalAllocationSize; if (DEBUG) { printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); - printf("[init] Bitset: %zu bytes for %zu blocks\n", requiredBytesForRepresentingEachSlot, numberOfSlots); // we require 8MB to store the bitmask } } @@ -125,23 +83,18 @@ void* acquire(uint8_t size) // 1. Falls Todolist leer ist → neuer Block if (todoList == SENTINEL_BLOCK) { - if (nextUnusedBlock + blockSize > endOfChunk) + if (nextUnusedBlock + chunkSize > endOfChunk) { printf("Error: Out of memory!\n"); exit(1); } void* block = nextUnusedBlock; - nextUnusedBlock += blockSize; + nextUnusedBlock += chunkSize; if (DEBUG) { printf("[acquire] New block: %p\n", block); } - // Markieren des Blocks als frei - size_t idx = blockIndexFromPtr(block); -// printf("idx: %zu\n", idx); - usedSet(idx); - return block; } @@ -156,11 +109,6 @@ void* acquire(uint8_t size) // 3. Wir müssen die Children von dem Block erasen, um den Block neu befüllen zu können. block->eraser((void*)block); - // 4. Markieren des Blocks als verwendet - size_t idx = blockIndexFromPtr(block); -// printf("idx: %zu\n", idx); - usedSet(idx); - return (void*)block; } @@ -176,19 +124,12 @@ void release(void* ptr) if (DEBUG) { printf("[release] Freed block: %p\n", ptr); -// printf("[release] todo head: %p\n", todoList); -// printTodoList(todoList); } // Block zur todoList hinzufügen (von vorne) Block* block = (Block*)ptr; block->next = todoList; todoList = block; - - // Markieren des Blocks als frei - size_t idx = blockIndexFromPtr(block); -// printf("idx: %zu\n", idx); - usedClear(idx); } /** @@ -197,35 +138,39 @@ void release(void* ptr) */ void assertNumberLeakedBlocks(int expected) { // Total number of blocks that were ever allocated - uint8_t* firstSlot = (endOfChunk - chunkSize); // the start of the chunk - const size_t totalAllocated = (nextUnusedBlock - firstSlot) / blockSize; + uint8_t* firstSlot = (endOfChunk - totalAllocationSize); // the start of the chunk + const size_t totalAllocated = (nextUnusedBlock - firstSlot) / chunkSize; size_t numberOfElementsInTodoList = 0; for (const Block* b = todoList; b != SENTINEL_BLOCK; b = b->next) { numberOfElementsInTodoList++; } - size_t numberOfLeakedBlocks = 0; - -// printf("requiredBytesForRepresentingEachSlot: %zu\n", bitsetSize); - - // Flush: turn all "used" (bit=1) blocks into todoList - for (uint8_t* ptr = (uint8_t*)firstSlot; ptr < nextUnusedBlock; ptr+= blockSize) { -// printf("block: %p\n", ptr); - size_t idx = blockIndexFromPtr(ptr); -// printf("idx: %zu\n", idx); - bool isUsed = usedTest(idx); -// printf("isUsed: %d\n", isUsed); - if (isUsed) { - release((Block*)ptr); - numberOfLeakedBlocks++; - } - } - // Assert that the number of leaked blocks is as expected - if ((totalAllocated - numberOfElementsInTodoList - numberOfLeakedBlocks) != expected) { - printf("Totally allocated: %zu | numberOfElementsInTodoList %zu | numberOfLeakedBlocks %zu \n", totalAllocated, numberOfElementsInTodoList, numberOfLeakedBlocks); + if (numberOfElementsInTodoList != totalAllocated) { exit(1); } + +// +//// printf("requiredBytesForRepresentingEachSlot: %zu\n", bitsetSize); +// +// // Flush: turn all "used" (bit=1) blocks into todoList +// for (uint8_t* ptr = (uint8_t*)firstSlot; ptr < nextUnusedBlock; ptr+= chunkSize) { +//// printf("block: %p\n", ptr); +// size_t idx = blockIndexFromPtr(ptr); +//// printf("idx: %zu\n", idx); +// bool isUsed = usedTest(idx); +//// printf("isUsed: %d\n", isUsed); +// if (isUsed) { +// release((Block*)ptr); +// numberOfLeakedBlocks++; +// } +// } +// +// // Assert that the number of leaked blocks is as expected +// if ((totalAllocated - numberOfElementsInTodoList - numberOfLeakedBlocks) != expected) { +// printf("Totally allocated: %zu | numberOfElementsInTodoList %zu | numberOfLeakedBlocks %zu \n", totalAllocated, numberOfElementsInTodoList, numberOfLeakedBlocks); +// exit(1); +// } } // If there are any open libuv handles or requests, this function will block until they are closed. @@ -250,9 +195,9 @@ void assertThatAllAsynchronousOperationsAreFinished() { */ void testIfAllBlocksAreFreed() { - assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles - assertLeakFree(); // testing malloc & calloc & free - assertNumberLeakedBlocks(0); // testing acquire & release +// assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles +// assertLeakFree(); // testing malloc & calloc & free +// assertNumberLeakedBlocks(0); // testing acquire & release } // small testprogram to test the allocator From 53aa8c478d780be92ff117ac9259609493623b26 Mon Sep 17 00:00:00 2001 From: Flat Date: Mon, 17 Nov 2025 16:43:17 +0100 Subject: [PATCH 50/57] Explicitly implemented flushtodo list --- libraries/llvm/cMalloc.c | 75 ++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index f0e5ae0de5..05d8a2182b 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -28,13 +28,15 @@ typedef struct Block // Globale Variablen const bool DEBUG = false; + +static Block* freeList = SENTINEL_BLOCK; // Head of the free-List static Block* todoList = SENTINEL_BLOCK; // Head of the To-Do-List + static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage -static const int chunkSize = 64; // The size of each chunk (64B) -// How much storage do we allocate at the beginning of a program? =4GB -static const size_t totalAllocationSize = (size_t)4294967296ULL; +static const int chunkSize = 64; // The size of each chunk (64B) +static const size_t totalAllocationSize = (size_t)4294967296ULL; // How much storage do we allocate at the beginning of a program? =4GB void printTodoList(const Block *todoList) { printf("All Elements in Todo List:\n"); @@ -49,7 +51,7 @@ void printTodoList(const Block *todoList) { } /** - * Initialisiert den großen Speicherbereich (4GB). + * Initialisiert den großen Speicherbereich für den Allokator. */ void cInitializeMemory() { @@ -68,6 +70,23 @@ void cInitializeMemory() } } +static inline void flushTodoList(void) { + + const int BATCH_SIZE = 64; + + for (int i = 0; i < BATCH_SIZE && todoList != SENTINEL_BLOCK; i++) { + Block* block = todoList; + todoList = block->next; + + void (*fn)(void*) = block->eraser; + fn(block); + + block->next = freeList; + freeList = block; + } +} + + // ----------------------------- // Allokator // ----------------------------- @@ -80,36 +99,42 @@ void cInitializeMemory() */ void* acquire(uint8_t size) { - // 1. Falls Todolist leer ist → neuer Block - if (todoList == SENTINEL_BLOCK) + // 1. Fast Path - Falls Todolist was hat → reuse Block + if (freeList != SENTINEL_BLOCK) { - if (nextUnusedBlock + chunkSize > endOfChunk) - { - printf("Error: Out of memory!\n"); - exit(1); - } - - void* block = nextUnusedBlock; - nextUnusedBlock += chunkSize; - if (DEBUG) { - printf("[acquire] New block: %p\n", block); - } + Block* block = freeList; + freeList = block->next; + if (DEBUG) printf("[acquire] FAST → %p\n", (void*)block); return block; } + // 2. Slow Path - free-List leer -> Sweep & try free list again + if (todoList != SENTINEL_BLOCK) { - // 2. Falls Todolist nicht leer ist → wiederverwenden - Block* block = todoList; - todoList = block->next; + flushTodoList(); - if (DEBUG) { - printf("[acquire] Reusing block: %p\n", (void*)block); + // Nach Sweep ist freeList höchstwahrscheinlich gefüllt + if (freeList != SENTINEL_BLOCK) { + + Block* block = freeList; + freeList = block->next; + + if (DEBUG) printf("[acquire] SWEEP → %p\n", (void*)block); + return block; + } + } + + // 3. Fallbäck - wir allokieren einen neuen Block + if (nextUnusedBlock + chunkSize > endOfChunk) { + fprintf(stderr, "Arena exhausted – out of memory.\n"); + exit(1); } - // 3. Wir müssen die Children von dem Block erasen, um den Block neu befüllen zu können. - block->eraser((void*)block); + Block* fresh = (Block*)nextUnusedBlock; + nextUnusedBlock += chunkSize; - return (void*)block; + if (DEBUG) printf("[acquire] NEW → %p\n", (void*)fresh); + return fresh; } From f6321e5f0e5ab6aff7d573d3b66be07b099240df Mon Sep 17 00:00:00 2001 From: Flat Date: Tue, 18 Nov 2025 09:38:34 +0100 Subject: [PATCH 51/57] more test programs + we now batch the flush todo list when having >= 64 slots in the todo list + added more test programs to test that approach --- ...locate_first_then_release_flush_once.check | 65 ++++ ...ocate_first_then_release_flush_once.effekt | 148 +++++++++ ...ocate_first_then_release_flush_twice.check | 0 ...cate_first_then_release_flush_twice.effekt | 282 ++++++++++++++++++ .../nooptimized/trigger_child_freeing.check | 0 .../nooptimized/trigger_child_freeing.effekt | 64 ++++ .../flat/nooptimized/trigger_flush_once.check | 0 .../nooptimized/trigger_flush_once.effekt | 17 ++ .../nooptimized/trigger_flush_twice.check | 0 .../nooptimized/trigger_flush_twice.effekt | 17 ++ .../trigger_one_child_freeing.check | 0 .../trigger_one_child_freeing.effekt | 26 ++ .../trigger_two_child_freeing.check | 0 .../trigger_two_child_freeing.effekt | 28 ++ libraries/llvm/cMalloc.c | 23 +- 15 files changed, 664 insertions(+), 6 deletions(-) create mode 100644 examples/flat/nooptimized/allocate_first_then_release_flush_once.check create mode 100644 examples/flat/nooptimized/allocate_first_then_release_flush_once.effekt create mode 100644 examples/flat/nooptimized/allocate_first_then_release_flush_twice.check create mode 100644 examples/flat/nooptimized/allocate_first_then_release_flush_twice.effekt create mode 100644 examples/flat/nooptimized/trigger_child_freeing.check create mode 100644 examples/flat/nooptimized/trigger_child_freeing.effekt create mode 100644 examples/flat/nooptimized/trigger_flush_once.check create mode 100644 examples/flat/nooptimized/trigger_flush_once.effekt create mode 100644 examples/flat/nooptimized/trigger_flush_twice.check create mode 100644 examples/flat/nooptimized/trigger_flush_twice.effekt create mode 100644 examples/flat/nooptimized/trigger_one_child_freeing.check create mode 100644 examples/flat/nooptimized/trigger_one_child_freeing.effekt create mode 100644 examples/flat/nooptimized/trigger_two_child_freeing.check create mode 100644 examples/flat/nooptimized/trigger_two_child_freeing.effekt diff --git a/examples/flat/nooptimized/allocate_first_then_release_flush_once.check b/examples/flat/nooptimized/allocate_first_then_release_flush_once.check new file mode 100644 index 0000000000..90fa4d6e3b --- /dev/null +++ b/examples/flat/nooptimized/allocate_first_then_release_flush_once.check @@ -0,0 +1,65 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 \ No newline at end of file diff --git a/examples/flat/nooptimized/allocate_first_then_release_flush_once.effekt b/examples/flat/nooptimized/allocate_first_then_release_flush_once.effekt new file mode 100644 index 0000000000..4d1f729a1a --- /dev/null +++ b/examples/flat/nooptimized/allocate_first_then_release_flush_once.effekt @@ -0,0 +1,148 @@ + +record OneSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int +) + +def allocate65SlotsAndReleaseThem() = { + var x1 = OneSlotPerson(1, 2, 3, 4, 5, 6) + var x2 = OneSlotPerson(2, 3, 4, 5, 6, 7) + var x3 = OneSlotPerson(3, 4, 5, 6, 7, 8) + var x4 = OneSlotPerson(4, 5, 6, 7, 8, 9) + var x5 = OneSlotPerson(5, 6, 7, 8, 9, 10) + var x6 = OneSlotPerson(6, 7, 8, 9, 10, 11) + var x7 = OneSlotPerson(7, 8, 9, 10, 11, 12) + var x8 = OneSlotPerson(8, 9, 10, 11, 12, 13) + var x9 = OneSlotPerson(9, 10, 11, 12, 13, 14) + var x10 = OneSlotPerson(10, 11, 12, 13, 14, 15) + var x11 = OneSlotPerson(11, 12, 13, 14, 15, 16) + var x12 = OneSlotPerson(12, 13, 14, 15, 16, 17) + var x13 = OneSlotPerson(13, 14, 15, 16, 17, 18) + var x14 = OneSlotPerson(14, 15, 16, 17, 18, 19) + var x15 = OneSlotPerson(15, 16, 17, 18, 19, 20) + var x16 = OneSlotPerson(16, 17, 18, 19, 20, 21) + var x17 = OneSlotPerson(17, 18, 19, 20, 21, 22) + var x18 = OneSlotPerson(18, 19, 20, 21, 22, 23) + var x19 = OneSlotPerson(19, 20, 21, 22, 23, 24) + var x20 = OneSlotPerson(20, 21, 22, 23, 24, 25) + var x21 = OneSlotPerson(21, 22, 23, 24, 25, 26) + var x22 = OneSlotPerson(22, 23, 24, 25, 26, 27) + var x23 = OneSlotPerson(23, 24, 25, 26, 27, 28) + var x24 = OneSlotPerson(24, 25, 26, 27, 28, 29) + var x25 = OneSlotPerson(25, 26, 27, 28, 29, 30) + var x26 = OneSlotPerson(26, 27, 28, 29, 30, 31) + var x27 = OneSlotPerson(27, 28, 29, 30, 31, 32) + var x28 = OneSlotPerson(28, 29, 30, 31, 32, 33) + var x29 = OneSlotPerson(29, 30, 31, 32, 33, 34) + var x30 = OneSlotPerson(30, 31, 32, 33, 34, 35) + var x31 = OneSlotPerson(31, 32, 33, 34, 35, 36) + var x32 = OneSlotPerson(32, 33, 34, 35, 36, 37) + var x33 = OneSlotPerson(33, 34, 35, 36, 37, 38) + var x34 = OneSlotPerson(34, 35, 36, 37, 38, 39) + var x35 = OneSlotPerson(35, 36, 37, 38, 39, 40) + var x36 = OneSlotPerson(36, 37, 38, 39, 40, 41) + var x37 = OneSlotPerson(37, 38, 39, 40, 41, 42) + var x38 = OneSlotPerson(38, 39, 40, 41, 42, 43) + var x39 = OneSlotPerson(39, 40, 41, 42, 43, 44) + var x40 = OneSlotPerson(40, 41, 42, 43, 44, 45) + var x41 = OneSlotPerson(41, 42, 43, 44, 45, 46) + var x42 = OneSlotPerson(42, 43, 44, 45, 46, 47) + var x43 = OneSlotPerson(43, 44, 45, 46, 47, 48) + var x44 = OneSlotPerson(44, 45, 46, 47, 48, 49) + var x45 = OneSlotPerson(45, 46, 47, 48, 49, 50) + var x46 = OneSlotPerson(46, 47, 48, 49, 50, 51) + var x47 = OneSlotPerson(47, 48, 49, 50, 51, 52) + var x48 = OneSlotPerson(48, 49, 50, 51, 52, 53) + var x49 = OneSlotPerson(49, 50, 51, 52, 53, 54) + var x50 = OneSlotPerson(50, 51, 52, 53, 54, 55) + var x51 = OneSlotPerson(51, 52, 53, 54, 55, 56) + var x52 = OneSlotPerson(52, 53, 54, 55, 56, 57) + var x53 = OneSlotPerson(53, 54, 55, 56, 57, 58) + var x54 = OneSlotPerson(54, 55, 56, 57, 58, 59) + var x55 = OneSlotPerson(55, 56, 57, 58, 59, 60) + var x56 = OneSlotPerson(56, 57, 58, 59, 60, 61) + var x57 = OneSlotPerson(57, 58, 59, 60, 61, 62) + var x58 = OneSlotPerson(58, 59, 60, 61, 62, 63) + var x59 = OneSlotPerson(59, 60, 61, 62, 63, 64) + var x60 = OneSlotPerson(60, 61, 62, 63, 64, 65) + var x61 = OneSlotPerson(61, 62, 63, 64, 65, 66) + var x62 = OneSlotPerson(62, 63, 64, 65, 66, 67) + var x63 = OneSlotPerson(63, 64, 65, 66, 67, 68) + var x64 = OneSlotPerson(64, 65, 66, 67, 68, 69) + var x65 = OneSlotPerson(65, 66, 67, 68, 69, 70) + + println(x1.field1) + println(x2.field1) + println(x3.field1) + println(x4.field1) + println(x5.field1) + println(x6.field1) + println(x7.field1) + println(x8.field1) + println(x9.field1) + println(x10.field1) + println(x11.field1) + println(x12.field1) + println(x13.field1) + println(x14.field1) + println(x15.field1) + println(x16.field1) + println(x17.field1) + println(x18.field1) + println(x19.field1) + println(x20.field1) + println(x21.field1) + println(x22.field1) + println(x23.field1) + println(x24.field1) + println(x25.field1) + println(x26.field1) + println(x27.field1) + println(x28.field1) + println(x29.field1) + println(x30.field1) + println(x31.field1) + println(x32.field1) + println(x33.field1) + println(x34.field1) + println(x35.field1) + println(x36.field1) + println(x37.field1) + println(x38.field1) + println(x39.field1) + println(x40.field1) + println(x41.field1) + println(x42.field1) + println(x43.field1) + println(x44.field1) + println(x45.field1) + println(x46.field1) + println(x47.field1) + println(x48.field1) + println(x49.field1) + println(x50.field1) + println(x51.field1) + println(x52.field1) + println(x53.field1) + println(x54.field1) + println(x55.field1) + println(x56.field1) + println(x57.field1) + println(x58.field1) + println(x59.field1) + println(x60.field1) + println(x61.field1) + println(x62.field1) + println(x63.field1) + println(x64.field1) + println(x65.field1) +} + +def main() = { + allocate65SlotsAndReleaseThem(); + var finalObjectThatTriggersFlushingTodoList = OneSlotPerson(1, 2, 3, 4, 5, 6) +} diff --git a/examples/flat/nooptimized/allocate_first_then_release_flush_twice.check b/examples/flat/nooptimized/allocate_first_then_release_flush_twice.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/flat/nooptimized/allocate_first_then_release_flush_twice.effekt b/examples/flat/nooptimized/allocate_first_then_release_flush_twice.effekt new file mode 100644 index 0000000000..d9b4ccbb54 --- /dev/null +++ b/examples/flat/nooptimized/allocate_first_then_release_flush_twice.effekt @@ -0,0 +1,282 @@ + +record OneSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int +) + +// we first allocate 128 slots and then release them all, such that we have exactly 128 slots on the to-do-list +// therefore, we have to flush the to-do list twice to clear it completely +def allocate128SlotsAndReleaseThem() = { + var x1 = OneSlotPerson(1, 2, 3, 4, 5, 6) + var x2 = OneSlotPerson(2, 3, 4, 5, 6, 7) + var x3 = OneSlotPerson(3, 4, 5, 6, 7, 8) + var x4 = OneSlotPerson(4, 5, 6, 7, 8, 9) + var x5 = OneSlotPerson(5, 6, 7, 8, 9, 10) + var x6 = OneSlotPerson(6, 7, 8, 9, 10, 11) + var x7 = OneSlotPerson(7, 8, 9, 10, 11, 12) + var x8 = OneSlotPerson(8, 9, 10, 11, 12, 13) + var x9 = OneSlotPerson(9, 10, 11, 12, 13, 14) + var x10 = OneSlotPerson(10, 11, 12, 13, 14, 15) + var x11 = OneSlotPerson(11, 12, 13, 14, 15, 16) + var x12 = OneSlotPerson(12, 13, 14, 15, 16, 17) + var x13 = OneSlotPerson(13, 14, 15, 16, 17, 18) + var x14 = OneSlotPerson(14, 15, 16, 17, 18, 19) + var x15 = OneSlotPerson(15, 16, 17, 18, 19, 20) + var x16 = OneSlotPerson(16, 17, 18, 19, 20, 21) + var x17 = OneSlotPerson(17, 18, 19, 20, 21, 22) + var x18 = OneSlotPerson(18, 19, 20, 21, 22, 23) + var x19 = OneSlotPerson(19, 20, 21, 22, 23, 24) + var x20 = OneSlotPerson(20, 21, 22, 23, 24, 25) + var x21 = OneSlotPerson(21, 22, 23, 24, 25, 26) + var x22 = OneSlotPerson(22, 23, 24, 25, 26, 27) + var x23 = OneSlotPerson(23, 24, 25, 26, 27, 28) + var x24 = OneSlotPerson(24, 25, 26, 27, 28, 29) + var x25 = OneSlotPerson(25, 26, 27, 28, 29, 30) + var x26 = OneSlotPerson(26, 27, 28, 29, 30, 31) + var x27 = OneSlotPerson(27, 28, 29, 30, 31, 32) + var x28 = OneSlotPerson(28, 29, 30, 31, 32, 33) + var x29 = OneSlotPerson(29, 30, 31, 32, 33, 34) + var x30 = OneSlotPerson(30, 31, 32, 33, 34, 35) + var x31 = OneSlotPerson(31, 32, 33, 34, 35, 36) + var x32 = OneSlotPerson(32, 33, 34, 35, 36, 37) + var x33 = OneSlotPerson(33, 34, 35, 36, 37, 38) + var x34 = OneSlotPerson(34, 35, 36, 37, 38, 39) + var x35 = OneSlotPerson(35, 36, 37, 38, 39, 40) + var x36 = OneSlotPerson(36, 37, 38, 39, 40, 41) + var x37 = OneSlotPerson(37, 38, 39, 40, 41, 42) + var x38 = OneSlotPerson(38, 39, 40, 41, 42, 43) + var x39 = OneSlotPerson(39, 40, 41, 42, 43, 44) + var x40 = OneSlotPerson(40, 41, 42, 43, 44, 45) + var x41 = OneSlotPerson(41, 42, 43, 44, 45, 46) + var x42 = OneSlotPerson(42, 43, 44, 45, 46, 47) + var x43 = OneSlotPerson(43, 44, 45, 46, 47, 48) + var x44 = OneSlotPerson(44, 45, 46, 47, 48, 49) + var x45 = OneSlotPerson(45, 46, 47, 48, 49, 50) + var x46 = OneSlotPerson(46, 47, 48, 49, 50, 51) + var x47 = OneSlotPerson(47, 48, 49, 50, 51, 52) + var x48 = OneSlotPerson(48, 49, 50, 51, 52, 53) + var x49 = OneSlotPerson(49, 50, 51, 52, 53, 54) + var x50 = OneSlotPerson(50, 51, 52, 53, 54, 55) + var x51 = OneSlotPerson(51, 52, 53, 54, 55, 56) + var x52 = OneSlotPerson(52, 53, 54, 55, 56, 57) + var x53 = OneSlotPerson(53, 54, 55, 56, 57, 58) + var x54 = OneSlotPerson(54, 55, 56, 57, 58, 59) + var x55 = OneSlotPerson(55, 56, 57, 58, 59, 60) + var x56 = OneSlotPerson(56, 57, 58, 59, 60, 61) + var x57 = OneSlotPerson(57, 58, 59, 60, 61, 62) + var x58 = OneSlotPerson(58, 59, 60, 61, 62, 63) + var x59 = OneSlotPerson(59, 60, 61, 62, 63, 64) + var x60 = OneSlotPerson(60, 61, 62, 63, 64, 65) + var x61 = OneSlotPerson(61, 62, 63, 64, 65, 66) + var x62 = OneSlotPerson(62, 63, 64, 65, 66, 67) + var x63 = OneSlotPerson(63, 64, 65, 66, 67, 68) + var x64 = OneSlotPerson(64, 65, 66, 67, 68, 69) + var x65 = OneSlotPerson(65, 66, 67, 68, 69, 70) + var x66 = OneSlotPerson(66, 67, 68, 69, 70, 71) + var x67 = OneSlotPerson(67, 68, 69, 70, 71, 72) + var x68 = OneSlotPerson(68, 69, 70, 71, 72, 73) + var x69 = OneSlotPerson(69, 70, 71, 72, 73, 74) + var x70 = OneSlotPerson(70, 71, 72, 73, 74, 75) + var x71 = OneSlotPerson(71, 72, 73, 74, 75, 76) + var x72 = OneSlotPerson(72, 73, 74, 75, 76, 77) + var x73 = OneSlotPerson(73, 74, 75, 76, 77, 78) + var x74 = OneSlotPerson(74, 75, 76, 77, 78, 79) + var x75 = OneSlotPerson(75, 76, 77, 78, 79, 80) + var x76 = OneSlotPerson(76, 77, 78, 79, 80, 81) + var x77 = OneSlotPerson(77, 78, 79, 80, 81, 82) + var x78 = OneSlotPerson(78, 79, 80, 81, 82, 83) + var x79 = OneSlotPerson(79, 80, 81, 82, 83, 84) + var x80 = OneSlotPerson(80, 81, 82, 83, 84, 85) + var x81 = OneSlotPerson(81, 82, 83, 84, 85, 86) + var x82 = OneSlotPerson(82, 83, 84, 85, 86, 87) + var x83 = OneSlotPerson(83, 84, 85, 86, 87, 88) + var x84 = OneSlotPerson(84, 85, 86, 87, 88, 89) + var x85 = OneSlotPerson(85, 86, 87, 88, 89, 90) + var x86 = OneSlotPerson(86, 87, 88, 89, 90, 91) + var x87 = OneSlotPerson(87, 88, 89, 90, 91, 92) + var x88 = OneSlotPerson(88, 89, 90, 91, 92, 93) + var x89 = OneSlotPerson(89, 90, 91, 92, 93, 94) + var x90 = OneSlotPerson(90, 91, 92, 93, 94, 95) + var x91 = OneSlotPerson(91, 92, 93, 94, 95, 96) + var x92 = OneSlotPerson(92, 93, 94, 95, 96, 97) + var x93 = OneSlotPerson(93, 94, 95, 96, 97, 98) + var x94 = OneSlotPerson(94, 95, 96, 97, 98, 99) + var x95 = OneSlotPerson(95, 96, 97, 98, 99, 100) + var x96 = OneSlotPerson(96, 97, 98, 99, 100, 101) + var x97 = OneSlotPerson(97, 98, 99, 100, 101, 102) + var x98 = OneSlotPerson(98, 99, 100, 101, 102, 103) + var x99 = OneSlotPerson(99, 100, 101, 102, 103, 104) + var x100 = OneSlotPerson(100, 101, 102, 103, 104, 105) + var x101 = OneSlotPerson(101, 102, 103, 104, 105, 106) + var x102 = OneSlotPerson(102, 103, 104, 105, 106, 107) + var x103 = OneSlotPerson(103, 104, 105, 106, 107, 108) + var x104 = OneSlotPerson(104, 105, 106, 107, 108, 109) + var x105 = OneSlotPerson(105, 106, 107, 108, 109, 110) + var x106 = OneSlotPerson(106, 107, 108, 109, 110, 111) + var x107 = OneSlotPerson(107, 108, 109, 110, 111, 112) + var x108 = OneSlotPerson(108, 109, 110, 111, 112, 113) + var x109 = OneSlotPerson(109, 110, 111, 112, 113, 114) + var x110 = OneSlotPerson(110, 111, 112, 113, 114, 115) + var x111 = OneSlotPerson(111, 112, 113, 114, 115, 116) + var x112 = OneSlotPerson(112, 113, 114, 115, 116, 117) + var x113 = OneSlotPerson(113, 114, 115, 116, 117, 118) + var x114 = OneSlotPerson(114, 115, 116, 117, 118, 119) + var x115 = OneSlotPerson(115, 116, 117, 118, 119, 120) + var x116 = OneSlotPerson(116, 117, 118, 119, 120, 121) + var x117 = OneSlotPerson(117, 118, 119, 120, 121, 122) + var x118 = OneSlotPerson(118, 119, 120, 121, 122, 123) + var x119 = OneSlotPerson(119, 120, 121, 122, 123, 124) + var x120 = OneSlotPerson(120, 121, 122, 123, 124, 125) + var x121 = OneSlotPerson(121, 122, 123, 124, 125, 126) + var x122 = OneSlotPerson(122, 123, 124, 125, 126, 127) + var x123 = OneSlotPerson(123, 124, 125, 126, 127, 128) + var x124 = OneSlotPerson(124, 125, 126, 127, 128, 129) + var x125 = OneSlotPerson(125, 126, 127, 128, 129, 130) + var x126 = OneSlotPerson(126, 127, 128, 129, 130, 131) + var x127 = OneSlotPerson(127, 128, 129, 130, 131, 132) + var x128 = OneSlotPerson(128, 129, 130, 131, 132, 133) + + println(x1.field1) + println(x2.field1) + println(x3.field1) + println(x4.field1) + println(x5.field1) + println(x6.field1) + println(x7.field1) + println(x8.field1) + println(x9.field1) + println(x10.field1) + println(x11.field1) + println(x12.field1) + println(x13.field1) + println(x14.field1) + println(x15.field1) + println(x16.field1) + println(x17.field1) + println(x18.field1) + println(x19.field1) + println(x20.field1) + println(x21.field1) + println(x22.field1) + println(x23.field1) + println(x24.field1) + println(x25.field1) + println(x26.field1) + println(x27.field1) + println(x28.field1) + println(x29.field1) + println(x30.field1) + println(x31.field1) + println(x32.field1) + println(x33.field1) + println(x34.field1) + println(x35.field1) + println(x36.field1) + println(x37.field1) + println(x38.field1) + println(x39.field1) + println(x40.field1) + println(x41.field1) + println(x42.field1) + println(x43.field1) + println(x44.field1) + println(x45.field1) + println(x46.field1) + println(x47.field1) + println(x48.field1) + println(x49.field1) + println(x50.field1) + println(x51.field1) + println(x52.field1) + println(x53.field1) + println(x54.field1) + println(x55.field1) + println(x56.field1) + println(x57.field1) + println(x58.field1) + println(x59.field1) + println(x60.field1) + println(x61.field1) + println(x62.field1) + println(x63.field1) + println(x64.field1) + println(x65.field1) + println(x66.field1) + println(x67.field1) + println(x68.field1) + println(x69.field1) + println(x70.field1) + println(x71.field1) + println(x72.field1) + println(x73.field1) + println(x74.field1) + println(x75.field1) + println(x76.field1) + println(x77.field1) + println(x78.field1) + println(x79.field1) + println(x80.field1) + println(x81.field1) + println(x82.field1) + println(x83.field1) + println(x84.field1) + println(x85.field1) + println(x86.field1) + println(x87.field1) + println(x88.field1) + println(x89.field1) + println(x90.field1) + println(x91.field1) + println(x92.field1) + println(x93.field1) + println(x94.field1) + println(x95.field1) + println(x96.field1) + println(x97.field1) + println(x98.field1) + println(x99.field1) + println(x100.field1) + println(x101.field1) + println(x102.field1) + println(x103.field1) + println(x104.field1) + println(x105.field1) + println(x106.field1) + println(x107.field1) + println(x108.field1) + println(x109.field1) + println(x110.field1) + println(x111.field1) + println(x112.field1) + println(x113.field1) + println(x114.field1) + println(x115.field1) + println(x116.field1) + println(x117.field1) + println(x118.field1) + println(x119.field1) + println(x120.field1) + println(x121.field1) + println(x122.field1) + println(x123.field1) + println(x124.field1) + println(x125.field1) + println(x126.field1) + println(x127.field1) + println(x128.field1) +} + +def main() = { + //allocate128SlotsAndReleaseThem(); + // allocate & flush to-do list twice + //var flushingTodoListFirstTime = OneSlotPerson(1, 2, 3, 4, 5, 6) + //var flushingTodoListSecondTime = OneSlotPerson(1, 2, 3, 4, 5, 7) + + // deallocate again + //println(flushingTodoListFirstTime.field1) + //println(flushingTodoListSecondTime.field1) +} diff --git a/examples/flat/nooptimized/trigger_child_freeing.check b/examples/flat/nooptimized/trigger_child_freeing.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/flat/nooptimized/trigger_child_freeing.effekt b/examples/flat/nooptimized/trigger_child_freeing.effekt new file mode 100644 index 0000000000..2f96672f0c --- /dev/null +++ b/examples/flat/nooptimized/trigger_child_freeing.effekt @@ -0,0 +1,64 @@ +// a simple program that creates a more complex object +// -> child1 +// parent +// -> child2 -> child3 +// -> child4 -> child5 -> child6 +// -> child7 -> child8 +// where child1 is OneSlotPerson and child2 and child3 are TwoSlotPerson and child4 and child5 and child6 are ThreeSlotPerson, and child7 and child8 are TwoSlotPerson + +record OneSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int +) + +record TwoSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int +) + +record ThreeSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int, + field7: Int, + field8: Int, + field9: Int, + field10: Int, + field11: Int, + field12: Int, + field13: Int +) + +record Person( + child1: OneSlotPerson, + child2: TwoSlotPerson, + child3: ThreeSlotPerson, + child4: TwoSlotPerson, + child5: OneSlotPerson +) + +def main() = { + var iters = 1 + while(iters <= 66) { + var x = Person( + OneSlotPerson(iters, iters + 1, iters + 2, iters + 3, iters + 4, iters + 5), + TwoSlotPerson(iters, iters + 1, iters + 2, iters + 3, iters + 4, iters + 5, iters + 6), + ThreeSlotPerson(iters, iters + 1, iters + 2, iters + 3, iters + 4, iters + 5, iters + 6, iters + 7, iters + 8, iters + 9, iters + 10, iters + 11, iters + 12), + TwoSlotPerson(iters, iters + 1, iters + 2, iters + 3, iters + 4, iters + 5, iters + 7), + OneSlotPerson(iters, iters + 1, iters + 2, iters + 3, iters + 4, iters + 6) + ) + iters = iters + 1 + } +} diff --git a/examples/flat/nooptimized/trigger_flush_once.check b/examples/flat/nooptimized/trigger_flush_once.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/flat/nooptimized/trigger_flush_once.effekt b/examples/flat/nooptimized/trigger_flush_once.effekt new file mode 100644 index 0000000000..c1306808c5 --- /dev/null +++ b/examples/flat/nooptimized/trigger_flush_once.effekt @@ -0,0 +1,17 @@ +// a simple program that triggers flush to-do-list once +record OneSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int +) + +def main() = { + var iters = 1 + while(iters <= 65) { + var x = OneSlotPerson(iters, iters + 1, iters + 2, iters + 3, iters + 4, iters + 5) + iters = iters + 1 + } +} diff --git a/examples/flat/nooptimized/trigger_flush_twice.check b/examples/flat/nooptimized/trigger_flush_twice.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/flat/nooptimized/trigger_flush_twice.effekt b/examples/flat/nooptimized/trigger_flush_twice.effekt new file mode 100644 index 0000000000..b364f2fdfc --- /dev/null +++ b/examples/flat/nooptimized/trigger_flush_twice.effekt @@ -0,0 +1,17 @@ +// a simple program that triggers flush to-do-list twice +record OneSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int +) + +def main() = { + var iters = 1 + while(iters <= 129) { + var x = OneSlotPerson(iters, iters + 1, iters + 2, iters + 3, iters + 4, iters + 5) + iters = iters + 1 + } +} diff --git a/examples/flat/nooptimized/trigger_one_child_freeing.check b/examples/flat/nooptimized/trigger_one_child_freeing.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/flat/nooptimized/trigger_one_child_freeing.effekt b/examples/flat/nooptimized/trigger_one_child_freeing.effekt new file mode 100644 index 0000000000..a44b4896c2 --- /dev/null +++ b/examples/flat/nooptimized/trigger_one_child_freeing.effekt @@ -0,0 +1,26 @@ +// a simple program that creates a more complex object +// parent -> child1 +// where child1 is OneSlotPerson + +record OneSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int +) + +record Person( + child1: OneSlotPerson +) + +def main() = { + var iters = 1 + while(iters <= 65) { + var x = Person( + OneSlotPerson(iters, iters + 1, iters + 2, iters + 3, iters + 4, iters + 5) + ) + iters = iters + 1 + } +} diff --git a/examples/flat/nooptimized/trigger_two_child_freeing.check b/examples/flat/nooptimized/trigger_two_child_freeing.check new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/flat/nooptimized/trigger_two_child_freeing.effekt b/examples/flat/nooptimized/trigger_two_child_freeing.effekt new file mode 100644 index 0000000000..e8eb319334 --- /dev/null +++ b/examples/flat/nooptimized/trigger_two_child_freeing.effekt @@ -0,0 +1,28 @@ +// a simple program that creates a more complex object +// parent -> child1 +// where child1 is OneSlotPerson + +record OneSlotPerson( + field1: Int, + field2: Int, + field3: Int, + field4: Int, + field5: Int, + field6: Int +) + +record Person( + child1: OneSlotPerson, + child2: OneSlotPerson +) + +def main() = { + var iters = 1 + while(iters <= 64) { + var x = Person( + OneSlotPerson(iters, iters + 1, iters + 2, iters + 3, iters + 4, iters + 5), + OneSlotPerson(iters + 6, iters + 7, iters + 8, iters + 9, iters + 10, iters + 11) + ) + iters = iters + 1 + } +} diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 05d8a2182b..330b1f5afb 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -32,6 +32,9 @@ const bool DEBUG = false; static Block* freeList = SENTINEL_BLOCK; // Head of the free-List static Block* todoList = SENTINEL_BLOCK; // Head of the To-Do-List +static size_t todoCount = 0; // Anzahl dirty Blöcke +static const size_t TODO_LIMIT = 64; + static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage @@ -71,19 +74,24 @@ void cInitializeMemory() } static inline void flushTodoList(void) { + size_t toClean = todoCount < TODO_LIMIT ? todoCount : TODO_LIMIT; // Limit the number of blocks to clean - const int BATCH_SIZE = 64; - - for (int i = 0; i < BATCH_SIZE && todoList != SENTINEL_BLOCK; i++) { + for (int i = 0; i < toClean; i++) { Block* block = todoList; todoList = block->next; - void (*fn)(void*) = block->eraser; - fn(block); + if (DEBUG) { + printf("[flushTodoList] Freeing block: %p\n", (void*)block); + } + + // Call the eraser function + block->eraser(block); block->next = freeList; freeList = block; } + + todoCount -= toClean; } @@ -99,6 +107,7 @@ static inline void flushTodoList(void) { */ void* acquire(uint8_t size) { + if (DEBUG) printf("[acquire] todoCount: %zu\n", todoCount); // 1. Fast Path - Falls Todolist was hat → reuse Block if (freeList != SENTINEL_BLOCK) { @@ -109,7 +118,7 @@ void* acquire(uint8_t size) return block; } // 2. Slow Path - free-List leer -> Sweep & try free list again - if (todoList != SENTINEL_BLOCK) { + if (todoCount >= TODO_LIMIT) { flushTodoList(); @@ -155,6 +164,8 @@ void release(void* ptr) Block* block = (Block*)ptr; block->next = todoList; todoList = block; + + todoCount++; } /** From f05cd0688e5ea60463660dc0161f0cb6ed7fe9e5 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 19 Nov 2025 08:33:23 +0100 Subject: [PATCH 52/57] testing approach with 128 bytes instead of 64 --- .../effekt/generator/llvm/Transformer.scala | 23 +++++++++++++------ libraries/llvm/cMalloc.c | 12 +++++----- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index a596c84ee1..8b3943a3ab 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -17,6 +17,7 @@ import scala.collection.mutable.ListBuffer object Transformer { val llvmFeatureFlags: List[String] = List("llvm") + val slotSize: Int = 128 // the size of a heap slot in bytes def transform(program: machine.Program)(using ErrorReporter): List[Definition] = program match { case machine.Program(declarations, definitions, entry) => @@ -582,7 +583,7 @@ object Transformer { } /** - * Produces an Object. It is always 64 bytes long + * Produces an Object. It is always ${slotSize} bytes long */ def produceObject(role: String, environment: machine.Environment, freeInBody: Set[machine.Variable])(using ModuleContext, FunctionContext, BlockContext): Operand = { if (environment.isEmpty) { @@ -663,7 +664,7 @@ object Transformer { alias match { // if we have a sharded object case Object if !fitsInOneSavingSlot(environment) => - // Follow chained 64-byte blocks created in produceObject + // Follow chained ${slotSize}-byte blocks created in produceObject val chunkedEnvironments = splitEnvironment(environment) var currentEnvironmentPtr: Operand = elementPointer @@ -848,22 +849,30 @@ object Transformer { * Is required to do fixed-sized-allocation. */ private def splitEnvironment(environment: machine.Environment): List[machine.Environment] = { - val slotSize = 64 // we want to use 64 bytes for each saving slot - var headerSize = 16 // we use 16 byte for the last saving block only, the others are 32 bytes + val headerSize = 16 // we use 16 byte for the last saving block only, the others are 32 bytes + val linkSize = 16 // how much bytes we need to store the link to the next block (%Pos object) + + // How much bytes do we need to reserve to save a slot? + // The last block only needs 16 bytes, because here we only need to store the header. + // For all other blocks we need 32 bytes, because we need to store the header and the link to the next block. + var reservedSpaceForSlot = headerSize + var currentEnvironment = List[machine.Variable]() var result = List[machine.Environment]() var isLast = true for (variable <- environment.reverse) { val variableSize = typeSize(variable.tpe) - if (headerSize + (environmentSize(currentEnvironment) + variableSize) <= slotSize) { + if (reservedSpaceForSlot + (environmentSize(currentEnvironment) + variableSize) <= slotSize) { currentEnvironment = currentEnvironment :+ variable } else { + if (isLast) { + reservedSpaceForSlot = headerSize + linkSize // normal header size + size of the positive type which is the reference to the next object + isLast = false + } result = result :+ currentEnvironment.reverse currentEnvironment = List(variable) - isLast = false - headerSize = 32 // normal header size + size of the positive type which is the reference to the next object } } result = result :+ currentEnvironment.reverse diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 330b1f5afb..7ae750a5dc 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -27,7 +27,7 @@ typedef struct Block #define SENTINEL_BLOCK ((Block*)1) // Globale Variablen -const bool DEBUG = false; +const bool DEBUG = true; static Block* freeList = SENTINEL_BLOCK; // Head of the free-List static Block* todoList = SENTINEL_BLOCK; // Head of the To-Do-List @@ -38,7 +38,7 @@ static const size_t TODO_LIMIT = 64; static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage -static const int chunkSize = 64; // The size of each chunk (64B) +static const int slotSize = 128; // The size of each chunk (128B) static const size_t totalAllocationSize = (size_t)4294967296ULL; // How much storage do we allocate at the beginning of a program? =4GB void printTodoList(const Block *todoList) { @@ -134,13 +134,13 @@ void* acquire(uint8_t size) } // 3. Fallbäck - wir allokieren einen neuen Block - if (nextUnusedBlock + chunkSize > endOfChunk) { + if (nextUnusedBlock + slotSize > endOfChunk) { fprintf(stderr, "Arena exhausted – out of memory.\n"); exit(1); } Block* fresh = (Block*)nextUnusedBlock; - nextUnusedBlock += chunkSize; + nextUnusedBlock += slotSize; if (DEBUG) printf("[acquire] NEW → %p\n", (void*)fresh); return fresh; @@ -175,7 +175,7 @@ void release(void* ptr) void assertNumberLeakedBlocks(int expected) { // Total number of blocks that were ever allocated uint8_t* firstSlot = (endOfChunk - totalAllocationSize); // the start of the chunk - const size_t totalAllocated = (nextUnusedBlock - firstSlot) / chunkSize; + const size_t totalAllocated = (nextUnusedBlock - firstSlot) / slotSize; size_t numberOfElementsInTodoList = 0; for (const Block* b = todoList; b != SENTINEL_BLOCK; b = b->next) { @@ -190,7 +190,7 @@ void assertNumberLeakedBlocks(int expected) { //// printf("requiredBytesForRepresentingEachSlot: %zu\n", bitsetSize); // // // Flush: turn all "used" (bit=1) blocks into todoList -// for (uint8_t* ptr = (uint8_t*)firstSlot; ptr < nextUnusedBlock; ptr+= chunkSize) { +// for (uint8_t* ptr = (uint8_t*)firstSlot; ptr < nextUnusedBlock; ptr+= slotSize) { //// printf("block: %p\n", ptr); // size_t idx = blockIndexFromPtr(ptr); //// printf("idx: %zu\n", idx); From 3ce997938eda9f13f1807427ce05a98faa33a684 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 19 Nov 2025 09:36:08 +0100 Subject: [PATCH 53/57] deactivated debug --- libraries/llvm/cMalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 7ae750a5dc..95269af7c8 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -27,7 +27,7 @@ typedef struct Block #define SENTINEL_BLOCK ((Block*)1) // Globale Variablen -const bool DEBUG = true; +const bool DEBUG = false; static Block* freeList = SENTINEL_BLOCK; // Head of the free-List static Block* todoList = SENTINEL_BLOCK; // Head of the To-Do-List From 23e3a341c3ce7118678ecb52e53fdc634feb9a8a Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 19 Nov 2025 13:45:47 +0100 Subject: [PATCH 54/57] back to 64 bytes --- .../src/main/scala/effekt/generator/llvm/Transformer.scala | 2 +- libraries/llvm/cMalloc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 8b3943a3ab..76474922f7 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -17,7 +17,7 @@ import scala.collection.mutable.ListBuffer object Transformer { val llvmFeatureFlags: List[String] = List("llvm") - val slotSize: Int = 128 // the size of a heap slot in bytes + val slotSize: Int = 64 // the size of a heap slot in bytes def transform(program: machine.Program)(using ErrorReporter): List[Definition] = program match { case machine.Program(declarations, definitions, entry) => diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index 95269af7c8..a4e6b62af1 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -38,7 +38,7 @@ static const size_t TODO_LIMIT = 64; static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block static uint8_t* endOfChunk = NULL; // End of the allocated Storage -static const int slotSize = 128; // The size of each chunk (128B) +static const int slotSize = 64; // The size of each chunk (128B) static const size_t totalAllocationSize = (size_t)4294967296ULL; // How much storage do we allocate at the beginning of a program? =4GB void printTodoList(const Block *todoList) { From 24771ce6e9874514fa80e7cb299a5abd4dc63a15 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 19 Nov 2025 15:49:42 +0100 Subject: [PATCH 55/57] reduced back to simple lazy reference counting --- .../effekt/generator/llvm/Transformer.scala | 6 +- libraries/common/ref.effekt | 4 +- libraries/llvm/cMalloc.c | 276 +++--------------- libraries/llvm/cMalloc.h | 13 - libraries/llvm/io.c | 1 - libraries/llvm/memtrack.c | 141 --------- libraries/llvm/rts.ll | 83 +----- 7 files changed, 51 insertions(+), 473 deletions(-) delete mode 100644 libraries/llvm/cMalloc.h delete mode 100644 libraries/llvm/memtrack.c diff --git a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala index 76474922f7..c8bf5d3fef 100644 --- a/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala +++ b/effekt/shared/src/main/scala/effekt/generator/llvm/Transformer.scala @@ -32,7 +32,6 @@ object Transformer { Call("stack", Ccc(), stackType, withEmptyStack, List()), Call("", Ccc(), VoidType(), initializeMemory, List()), Call("_", Tailcc(false), VoidType(), transform(entry), List(LocalReference(stackType, "stack"))), - Call("", Ccc(), VoidType(), testIfAllBlocksAreFreed, List()), ) val entryBlock = BasicBlock("entry", entryInstructions, RetVoid()) val entryFunction = Function(External(), Ccc(), VoidType(), "effektMain", List(), List(entryBlock)) @@ -756,8 +755,7 @@ object Transformer { emit(Load(returnAddressName, returnAddressType, returnAddressPointer, StackPointer)); } - val initializeMemory = ConstantGlobal("cInitializeMemory"); - val testIfAllBlocksAreFreed = ConstantGlobal("testIfAllBlocksAreFreed"); + val initializeMemory = ConstantGlobal("initializeMemory"); val newObject = ConstantGlobal("newObject"); val objectEnvironment = ConstantGlobal("objectEnvironment"); @@ -952,7 +950,7 @@ object Transformer { * When @eraseObject is called, we also call the eraser. And for Pos and Neg objects, that is the eraser. * The object is called 2 times. * 1. Phase: When call @eraseObject -> We call release to prepend the object to our to-do-list. - * 2. Phase: When call acquire -> We call %erase on each children, so the object can be reused again. + * 2. Phase: When call acquire -> We call %erase on each child, so the object can be reused again. * * @param freshEnvironment the variables of the object * @return the eraser diff --git a/libraries/common/ref.effekt b/libraries/common/ref.effekt index fc23f394c9..5ffff0e1fe 100644 --- a/libraries/common/ref.effekt +++ b/libraries/common/ref.effekt @@ -48,7 +48,7 @@ extern def allocate[T]() at global: Ref[T] = chez "(box #f)" llvm """ ; sizeof Header + sizeof Pos = 32 - %ref = tail call noalias ptr @acquire(i64 noundef 32) + %ref = tail call noalias ptr @acquire() %refEraser = getelementptr ptr, ptr %ref, i64 1 %fieldTag = getelementptr ptr, ptr %ref, i64 2 @@ -71,7 +71,7 @@ extern def ref[T](init: T) at global: Ref[T] = %initTag = extractvalue %Pos ${init}, 0 %initObject_pointer = extractvalue %Pos ${init}, 1 ; sizeof Header + sizeof Pos = 32 - %ref = tail call noalias ptr @acquire(i64 noundef 32) + %ref = tail call noalias ptr @acquire() %refEraser = getelementptr ptr, ptr %ref, i64 1 %refField = getelementptr ptr, ptr %ref, i64 2 diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c index a4e6b62af1..344a38fcf3 100644 --- a/libraries/llvm/cMalloc.c +++ b/libraries/llvm/cMalloc.c @@ -1,264 +1,76 @@ -#include -#include -#include -#include -#include "memtrack.c" +#include /** - * @brief Block-Struktur für die To-Do-List & Used-List. + * @brief Slot-Struktur für die To-Do-List & Used-List. * - * Jeder freie Block zeigt auf den nächsten freien Block. + * Jeder freie Slot zeigt auf den nächsten freien Slot. */ -typedef struct Block +typedef struct Slot { - struct Block* next; + struct Slot* next; void (*eraser)(void *object); -} Block; - +} Slot; /** - * Sentinel-Wert: Ein Pointer, der garantiert niemals ein echter Heap-Pointer ist. - * - * Wir nutzen (Block*)1, weil: - * - echte Heap-Pointer immer mindestens 8-Byte aligned sind - * - 1 also niemals kollidiert - * - ReferenceCount != 0 bleibt (wenn Header überlagert ist) + * Sentinel-Slot: Fakes a block with a RC=1. It is used to mark the end of the To-Do-List. + * But it is not a real heap-object, because it is not 8-byte aligned. */ -#define SENTINEL_BLOCK ((Block*)1) - -// Globale Variablen -const bool DEBUG = false; - -static Block* freeList = SENTINEL_BLOCK; // Head of the free-List -static Block* todoList = SENTINEL_BLOCK; // Head of the To-Do-List +#define SENTINEL_SLOT ((Slot*)1) -static size_t todoCount = 0; // Anzahl dirty Blöcke -static const size_t TODO_LIMIT = 64; +static Slot* todoList = SENTINEL_SLOT; // Head of the To-Do-List -static uint8_t* nextUnusedBlock = NULL; // Pointer to the next unused Block -static uint8_t* endOfChunk = NULL; // End of the allocated Storage +static uint8_t* nextUnusedSlot = NULL; // Pointer to the next unused Slot static const int slotSize = 64; // The size of each chunk (128B) static const size_t totalAllocationSize = (size_t)4294967296ULL; // How much storage do we allocate at the beginning of a program? =4GB -void printTodoList(const Block *todoList) { - printf("All Elements in Todo List:\n"); - - const Block *b = todoList; - while (b != NULL) { - printf(" Block at %p\n", (void *)b); - b = b->next; - } - - printf("(end of list)\n"); -} - /** - * Initialisiert den großen Speicherbereich für den Allokator. + * Initializes the memory for our effekt-objects that are created by newObject and deleted by eraseObject. */ -void cInitializeMemory() -{ - uint8_t* mem = (uint8_t*)malloc(totalAllocationSize); - if (!mem) - { - printf("Error: malloc() failed!\n"); - exit(1); - } - - nextUnusedBlock = mem; - endOfChunk = mem + totalAllocationSize; - - if (DEBUG) { - printf("[init] Memory initialized: %p - %p\n", (void*)mem, (void*)endOfChunk); - } +void initializeMemory() { + + // we allocate memory once from the os and use it for all effekt objects + nextUnusedSlot = (uint8_t*)mmap( + NULL, // Let the kernel pick the address + totalAllocationSize, // Size of region + PROT_READ | PROT_WRITE, // Access permissions + MAP_PRIVATE | MAP_ANONYMOUS, // Not backed by a file + -1, // No file descriptor + 0 // Offset + ); } -static inline void flushTodoList(void) { - size_t toClean = todoCount < TODO_LIMIT ? todoCount : TODO_LIMIT; // Limit the number of blocks to clean - - for (int i = 0; i < toClean; i++) { - Block* block = todoList; - todoList = block->next; - - if (DEBUG) { - printf("[flushTodoList] Freeing block: %p\n", (void*)block); - } - - // Call the eraser function - block->eraser(block); - - block->next = freeList; - freeList = block; - } - - todoCount -= toClean; -} - - -// ----------------------------- -// Allokator -// ----------------------------- - /** - * Einfacher Speicher-Allocator. - * - * Wenn die Todolist leer ist, nimmt er den nächsten Block im Chunk. - * Wenn die Todolist nicht leer ist, nimmt er den ersten Eintrag daraus. + * A simple allocator for the effekt slots that are created by newObject and deleted by eraseObject. + * If we have an element in the to-do-list we can pop it and ensure that it is reusable by calling its eraser to erase the children. + * Otherwise, bump allocate a new slot. */ -void* acquire(uint8_t size) -{ - if (DEBUG) printf("[acquire] todoCount: %zu\n", todoCount); - // 1. Fast Path - Falls Todolist was hat → reuse Block - if (freeList != SENTINEL_BLOCK) - { - Block* block = freeList; - freeList = block->next; - - if (DEBUG) printf("[acquire] FAST → %p\n", (void*)block); - return block; - } - // 2. Slow Path - free-List leer -> Sweep & try free list again - if (todoCount >= TODO_LIMIT) { - - flushTodoList(); +void* acquire() { + // 1. If there a slot to reuse... + if (todoList != SENTINEL_SLOT) { - // Nach Sweep ist freeList höchstwahrscheinlich gefüllt - if (freeList != SENTINEL_BLOCK) { + // ...pop it from to-do-list + Slot* reusedSlot = todoList; + todoList = reusedSlot->next; - Block* block = freeList; - freeList = block->next; + // Call the eraser function on it. After that, it is safe to reuse it again. + reusedSlot->eraser(reusedSlot); - if (DEBUG) printf("[acquire] SWEEP → %p\n", (void*)block); - return block; - } + return reusedSlot; } - // 3. Fallbäck - wir allokieren einen neuen Block - if (nextUnusedBlock + slotSize > endOfChunk) { - fprintf(stderr, "Arena exhausted – out of memory.\n"); - exit(1); - } - - Block* fresh = (Block*)nextUnusedBlock; - nextUnusedBlock += slotSize; - - if (DEBUG) printf("[acquire] NEW → %p\n", (void*)fresh); + // 2. Fallback - we bump-allocate a new slot + Slot* fresh = (Slot*)nextUnusedSlot; + nextUnusedSlot += slotSize; return fresh; } /** - * Gibt einen Block zurück in die To-Do-List. - * - * @param ptr Zeiger auf den Block (noch als Object mit Header). + * Pushes a slot on the top of the To-Do-List. */ -void release(void* ptr) -{ - if (!ptr) return; - - if (DEBUG) { - printf("[release] Freed block: %p\n", ptr); - } - - // Block zur todoList hinzufügen (von vorne) - Block* block = (Block*)ptr; - block->next = todoList; - todoList = block; - - todoCount++; -} - -/** -* Checks if we have leaked blocks. A block is leaked, if it is not in the to-do-list. -* If we have leaked blocks, we flush all of them to our to-do list. -*/ -void assertNumberLeakedBlocks(int expected) { - // Total number of blocks that were ever allocated - uint8_t* firstSlot = (endOfChunk - totalAllocationSize); // the start of the chunk - const size_t totalAllocated = (nextUnusedBlock - firstSlot) / slotSize; - - size_t numberOfElementsInTodoList = 0; - for (const Block* b = todoList; b != SENTINEL_BLOCK; b = b->next) { - numberOfElementsInTodoList++; - } - - if (numberOfElementsInTodoList != totalAllocated) { - exit(1); - } - -// -//// printf("requiredBytesForRepresentingEachSlot: %zu\n", bitsetSize); -// -// // Flush: turn all "used" (bit=1) blocks into todoList -// for (uint8_t* ptr = (uint8_t*)firstSlot; ptr < nextUnusedBlock; ptr+= slotSize) { -//// printf("block: %p\n", ptr); -// size_t idx = blockIndexFromPtr(ptr); -//// printf("idx: %zu\n", idx); -// bool isUsed = usedTest(idx); -//// printf("isUsed: %d\n", isUsed); -// if (isUsed) { -// release((Block*)ptr); -// numberOfLeakedBlocks++; -// } -// } -// -// // Assert that the number of leaked blocks is as expected -// if ((totalAllocated - numberOfElementsInTodoList - numberOfLeakedBlocks) != expected) { -// printf("Totally allocated: %zu | numberOfElementsInTodoList %zu | numberOfLeakedBlocks %zu \n", totalAllocated, numberOfElementsInTodoList, numberOfLeakedBlocks); -// exit(1); -// } -} - -// If there are any open libuv handles or requests, this function will block until they are closed. -// This might happen if the program has asynchronous operations (e.g. read, write file) that are not finished. -// This is necessary to ensure that all memory is freed. -void assertThatAllAsynchronousOperationsAreFinished() { - // 1. Complete all open libuv operations. This blocks until there are no more active handles or requests open. - uv_run(uv_default_loop(), UV_RUN_DEFAULT); - - // 2. Now the loop can be closed. - int close_result = uv_loop_close(uv_default_loop()); - if (close_result != 0) { - printf("Error: uv_loop_close still detected some active handles\n"); - exit(1); - } -} - - -/** -* Works as a Unit-Test, if all used blocks are freed at the end of the program. -* If not, we print an error message, making test fail. -*/ -void testIfAllBlocksAreFreed() -{ -// assertThatAllAsynchronousOperationsAreFinished(); // closing all open handles -// assertLeakFree(); // testing malloc & calloc & free -// assertNumberLeakedBlocks(0); // testing acquire & release -} - -// small testprogram to test the allocator -//int main(void) -//{ -// cInitializeMemory(); -// assertNumberLeakedBlocks(0); -// -// void* a = acquire((uint8_t)1024); -// assertNumberLeakedBlocks(1); -// -// void* b = acquire((uint8_t)1024); -// assertNumberLeakedBlocks(2); -// -// release(a); -// assertNumberLeakedBlocks(1); -// -// release(b); -// assertNumberLeakedBlocks(0); -// -// void* c = acquire((uint8_t)1024); // should reuse a -// assertNumberLeakedBlocks(1); -// -// release(c); -// testIfAllBlocksAreFreed(); -// -// return 0; -//} +void release(void* ptr) { + Slot* slot = (Slot*)ptr; + slot->next = todoList; + todoList = slot; +} \ No newline at end of file diff --git a/libraries/llvm/cMalloc.h b/libraries/llvm/cMalloc.h deleted file mode 100644 index bad50feb9e..0000000000 --- a/libraries/llvm/cMalloc.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef CMALLOC_H -#define CMALLOC_H - -extern const bool DEBUG; - -void cInitializeMemory(); -void* acquire(uint8_t size); -void release(void* ptr); -void assertNumberLeakedBlocks(int expected); -void testIfAllBlocksAreFreed(); - - -#endif \ No newline at end of file diff --git a/libraries/llvm/io.c b/libraries/llvm/io.c index 154de35ae4..010a57154c 100644 --- a/libraries/llvm/io.c +++ b/libraries/llvm/io.c @@ -3,7 +3,6 @@ #include #include // to compare flag names -#include "cMalloc.h" // Println and Readln and Random // ----------------------------- diff --git a/libraries/llvm/memtrack.c b/libraries/llvm/memtrack.c deleted file mode 100644 index b526cc5561..0000000000 --- a/libraries/llvm/memtrack.c +++ /dev/null @@ -1,141 +0,0 @@ -/** - * df: Simple memory tracker in C - * - * Tracks all invocations of malloc, calloc and free and prints a report an effekt program if it contains any memory leak. - */ - -#include -#include -#include "cMalloc.h" - -const bool debug = false; // if we should print debug messages - -// Here we track all allocations done by cMalloc, cCalloc and cFree -typedef struct Allocation -{ - void* ptr; // pointer returned by malloc - size_t size; // allocated size (for debugging only) - struct Allocation* next; // next entry -} Allocation; - -static Allocation* allocList = NULL; // Head of the list - -// Track total allocations for summary -static size_t totalAllocated = 0; -static size_t totalFreed = 0; - -// Internal helper: add allocation to alloclist -static void addAllocation(void* ptr, size_t size) -{ - Allocation* entry = malloc(sizeof(Allocation)); - entry->ptr = ptr; - entry->size = size; - entry->next = allocList; - allocList = entry; - totalAllocated += size; -} - -// Internal helper: remove allocation from alloclist -void removeAllocation(void* ptr) -{ - Allocation** curr = &allocList; - while (*curr) - { - if ((*curr)->ptr == ptr) - { - Allocation* toFree = *curr; - totalFreed += toFree->size; - *curr = toFree->next; - free(toFree); - return; - } - curr = &((*curr)->next); - } - if (debug) { - // Pointer not found — double free or invalid free was called - printf("Warning: cFree() was called on untracked pointer %p\n", ptr); - } -} - -// call normal malloc() of the C library and track the allocation -void* cMalloc(size_t size) -{ - void* ptr = malloc(size); - if (!ptr) - { - printf("Allocation failed with %p\n", &size); - exit(1); - } - addAllocation(ptr, size); - if (debug) { - printf("[cMalloc] New block: %p\n", ptr); - } - return ptr; -} - -// call normal calloc() of the C library and track the allocation -void* cCalloc(size_t mmemb, size_t size) -{ - void* ptr = calloc(mmemb, size); - if (!ptr) - { - printf("Allocation failed with %p\n", &size); - exit(1); - } - addAllocation(ptr, size); - if (debug) { - printf("[cCalloc] New block: %p\n", ptr); - } - return ptr; -} - -// call normal free() of the C library and track the allocation -void cFree(void* ptr) -{ - if (ptr == NULL) - return; - if (debug) { - printf("[cFree] Freed block: %p\n", ptr); - } - removeAllocation(ptr); - free(ptr); -} - -// If there are any remaining allocations (potential leaks), we report all of them and terminate the program -void assertLeakFree(void) -{ - if (allocList != NULL) - { - printf("\n----- MEMORY LEAK REPORT -----\n"); - Allocation* curr = allocList; - while (curr) - { - printf("LEAK: %p (%zu bytes) allocated\n", - curr->ptr, curr->size); - curr = curr->next; - } - printf("------------------------------\n"); - printf("Total allocated: %zu bytes\n", totalAllocated); - printf("Total freed: %zu bytes\n", totalFreed); - printf("Still allocated: %zu bytes\n", totalAllocated - totalFreed); - printf("------------------------------\n"); - } -} - -// Simple demo - // int main(void) - // { - // printf("Start memtrack demo\n"); - // - // char* a = cMalloc(10); - // int* b = cMalloc(sizeof(int) * 5); - // double* c = cMalloc(sizeof(double) * 3); - // - // cFree(b); - // //cFree(a); // Intentionally leaked - // //cFree(c); // Intentionally leaked - // - // assertLeakFree(); // Show leaks at end - // - // return 0; - // } diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 07816ff3a9..14fd3e54c3 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -94,10 +94,9 @@ ; Foreign imports -declare void @cInitializeMemory() -declare ptr @acquire(i64) +declare void @initializeMemory() +declare ptr @acquire() declare void @release(ptr) -declare void @testIfAllBlocksAreFreed() declare ptr @malloc(i64) @@ -139,84 +138,8 @@ define private %Prompt @freshPrompt() { ret %Prompt %prompt } -; 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 @acquire(i64 %size) + %object = call ptr @acquire() %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 From a3a28d07e0358d56b62d9db47b7589c4ef307dd1 Mon Sep 17 00:00:00 2001 From: Flat Date: Wed, 19 Nov 2025 17:52:16 +0100 Subject: [PATCH 56/57] converted lazy reference counting into llvm --- libraries/llvm/cMalloc.c | 76 ----------------------------- libraries/llvm/main.c | 1 - libraries/llvm/rts.ll | 101 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 95 insertions(+), 83 deletions(-) delete mode 100644 libraries/llvm/cMalloc.c diff --git a/libraries/llvm/cMalloc.c b/libraries/llvm/cMalloc.c deleted file mode 100644 index 344a38fcf3..0000000000 --- a/libraries/llvm/cMalloc.c +++ /dev/null @@ -1,76 +0,0 @@ -#include - -/** - * @brief Slot-Struktur für die To-Do-List & Used-List. - * - * Jeder freie Slot zeigt auf den nächsten freien Slot. - */ -typedef struct Slot -{ - struct Slot* next; - void (*eraser)(void *object); -} Slot; - -/** - * Sentinel-Slot: Fakes a block with a RC=1. It is used to mark the end of the To-Do-List. - * But it is not a real heap-object, because it is not 8-byte aligned. - */ -#define SENTINEL_SLOT ((Slot*)1) - -static Slot* todoList = SENTINEL_SLOT; // Head of the To-Do-List - -static uint8_t* nextUnusedSlot = NULL; // Pointer to the next unused Slot - -static const int slotSize = 64; // The size of each chunk (128B) -static const size_t totalAllocationSize = (size_t)4294967296ULL; // How much storage do we allocate at the beginning of a program? =4GB - -/** - * Initializes the memory for our effekt-objects that are created by newObject and deleted by eraseObject. - */ -void initializeMemory() { - - // we allocate memory once from the os and use it for all effekt objects - nextUnusedSlot = (uint8_t*)mmap( - NULL, // Let the kernel pick the address - totalAllocationSize, // Size of region - PROT_READ | PROT_WRITE, // Access permissions - MAP_PRIVATE | MAP_ANONYMOUS, // Not backed by a file - -1, // No file descriptor - 0 // Offset - ); -} - -/** - * A simple allocator for the effekt slots that are created by newObject and deleted by eraseObject. - * If we have an element in the to-do-list we can pop it and ensure that it is reusable by calling its eraser to erase the children. - * Otherwise, bump allocate a new slot. - */ -void* acquire() { - // 1. If there a slot to reuse... - if (todoList != SENTINEL_SLOT) { - - // ...pop it from to-do-list - Slot* reusedSlot = todoList; - todoList = reusedSlot->next; - - // Call the eraser function on it. After that, it is safe to reuse it again. - reusedSlot->eraser(reusedSlot); - - return reusedSlot; - } - - // 2. Fallback - we bump-allocate a new slot - Slot* fresh = (Slot*)nextUnusedSlot; - nextUnusedSlot += slotSize; - return fresh; -} - - -/** - * Pushes a slot on the top of the To-Do-List. - */ -void release(void* ptr) { - Slot* slot = (Slot*)ptr; - slot->next = todoList; - todoList = slot; -} \ No newline at end of file diff --git a/libraries/llvm/main.c b/libraries/llvm/main.c index 916da9650b..d95f9a5359 100644 --- a/libraries/llvm/main.c +++ b/libraries/llvm/main.c @@ -11,7 +11,6 @@ #include "types.c" #include "bytearray.c" -#include "cMalloc.c" #include "io.c" #include "panic.c" diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index 14fd3e54c3..b1f2d562e8 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -93,12 +93,6 @@ %String = type %Pos ; Foreign imports - -declare void @initializeMemory() -declare ptr @acquire() -declare void @release(ptr) - - declare ptr @malloc(i64) declare void @free(ptr) declare ptr @realloc(ptr, i64) @@ -123,6 +117,101 @@ declare void @exit(i64) declare void @llvm.assume(i1) +; typedef struct Slot +; { +; struct Slot* next; +; void (*eraser)(void *object); +; } Slot; +%struct.Slot = type { %struct.Slot*, void (i8*)* } + +; initializes the todoList with a sentinel slot. +; Sentinel Slot: Fakes a block with a RC=1. It is used to mark the end of the To-Do-List. +; But it is not a real heap-object, because it is not 8-byte aligned. +@todoList = global %struct.Slot* inttoptr (i64 1 to %struct.Slot*) + + +@nextUnusedSlot = global i8* null ; Pointer to the next unused Slot +@slotSize = constant i32 64 ; The size of each chunk (64 bytes) +@totalAllocationSize = constant i64 4294967296 ; How much storage do we allocate at the beginning of a program? =4GB + +; Initializes the memory for our effekt-objects that are created by newObject and deleted by eraseObject. +define void @initializeMemory() { +entry: + ; we use mmap to allocate memory from the OS. + %size = load i64, i64* @totalAllocationSize + + + %startAddress = call ptr @malloc(i64 %size) + + store i8* %startAddress, i8** @nextUnusedSlot + ret void +} + + +define %Object* @acquire() { +entry: + ; Load todoList head + %head = load %struct.Slot*, %struct.Slot** @todoList, align 8 + + %isSentinel = icmp eq %struct.Slot* %head, inttoptr (i64 1 to %struct.Slot*) + br i1 %isSentinel, label %bump_alloc, label %reuse + +reuse: + ; Pop from todoList + ; Slot* reusedSlot = todoList; + ; todoList = reusedSlot->next; + %next = getelementptr %struct.Slot, %struct.Slot* %head, i32 0, i32 0 + %nextVal = load %struct.Slot*, %struct.Slot** %next + store %struct.Slot* %nextVal, %struct.Slot** @todoList + + ; Call eraser + ; reusedSlot->eraser(reusedSlot); + %eraserPtr = getelementptr %struct.Slot, %struct.Slot* %head, i32 0, i32 1 + %eraser = load void (%struct.Slot*)*, void (%struct.Slot*)** %eraserPtr + call void %eraser(%struct.Slot* %head) + + ; return reusedSlot; + ret %struct.Slot* %head + +bump_alloc: + ; Load raw pointer + %rawBump = load i8*, i8** @nextUnusedSlot + + ; Treat it as Object* + %objectBump = bitcast i8* %rawBump to %Object* + + ; Load object size + %sizeBump = load i64, i64* @slotSize + + ; Move bump pointer forward by object size + %nextBump = getelementptr i8, i8* %rawBump, i64 %sizeBump + store i8* %nextBump, i8** @nextUnusedSlot + + ; Return the typed pointer + ret %Object* %objectBump +} + +; Pushes a slot on the top of the To-Do-List. +define void @release(%Object* %object) { +entry: + ; Cast Object to Slot + ;%slot = bitcast %Object* %object to %struct.Slot* + + ; oldHead = todoList + ;%oldHead = load %struct.Slot*, %struct.Slot** @todoList + + ; ptr->next = oldHead + ;%nextPtr = getelementptr %struct.Slot, %struct.Slot* %slot, i32 0, i32 0 + ;store %struct.Slot* %oldHead, %struct.Slot** %nextPtr + + ; todoList = ptr + ;store %struct.Slot* %slot, %struct.Slot** @todoList + + ret void +} + + + ; Prompts define private %Prompt @currentPrompt(%Stack %stack) { From 387226db2563f3f816a2cd52d5467968a138d58e Mon Sep 17 00:00:00 2001 From: Flat Date: Thu, 20 Nov 2025 15:54:34 +0100 Subject: [PATCH 57/57] optimized llvm allocator --- libraries/llvm/rts.ll | 60 +++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/libraries/llvm/rts.ll b/libraries/llvm/rts.ll index b1f2d562e8..737b8e08ed 100644 --- a/libraries/llvm/rts.ll +++ b/libraries/llvm/rts.ll @@ -122,33 +122,31 @@ declare void @llvm.assume(i1) ; struct Slot* next; ; void (*eraser)(void *object); ; } Slot; -%struct.Slot = type { %struct.Slot*, void (i8*)* } +%struct.Slot = type { %struct.Slot*, %Eraser } ; initializes the todoList with a sentinel slot. ; Sentinel Slot: Fakes a block with a RC=1. It is used to mark the end of the To-Do-List. ; But it is not a real heap-object, because it is not 8-byte aligned. -@todoList = global %struct.Slot* inttoptr (i64 1 to %struct.Slot*) +@todoList = dso_local unnamed_addr global %struct.Slot* inttoptr (i64 1 to %struct.Slot*), align 8 +@nextUnusedSlot = dso_local unnamed_addr global i8* null, align 8 ; Pointer to the next unused Slot - -@nextUnusedSlot = global i8* null ; Pointer to the next unused Slot -@slotSize = constant i32 64 ; The size of each chunk (64 bytes) -@totalAllocationSize = constant i64 4294967296 ; How much storage do we allocate at the beginning of a program? =4GB +@slotSize = constant i8 64, align 8 ; The size of each chunk (64 bytes) +@totalAllocationSize = constant i64 4294967296, align 8 ; How much storage do we allocate at the beginning of a program? =4GB ; Initializes the memory for our effekt-objects that are created by newObject and deleted by eraseObject. -define void @initializeMemory() { +define private void @initializeMemory() nounwind { entry: ; we use mmap to allocate memory from the OS. - %size = load i64, i64* @totalAllocationSize - + %size = load i64, i64* @totalAllocationSize, align 8 - %startAddress = call ptr @malloc(i64 %size) + %startAddress = call noalias ptr @malloc(i64 %size) store i8* %startAddress, i8** @nextUnusedSlot ret void } -define %Object* @acquire() { +define private %struct.Slot* @acquire() nounwind { entry: ; Load todoList head %head = load %struct.Slot*, %struct.Slot** @todoList, align 8 @@ -160,52 +158,46 @@ reuse: ; Pop from todoList ; Slot* reusedSlot = todoList; ; todoList = reusedSlot->next; - %next = getelementptr %struct.Slot, %struct.Slot* %head, i32 0, i32 0 - %nextVal = load %struct.Slot*, %struct.Slot** %next - store %struct.Slot* %nextVal, %struct.Slot** @todoList + %nextptr = getelementptr inbounds %struct.Slot, %struct.Slot* %head, i32 0, i32 0 + %next = load %struct.Slot*, %struct.Slot** %nextptr, align 8 + store %struct.Slot* %next, %struct.Slot** @todoList, align 8 ; Call eraser ; reusedSlot->eraser(reusedSlot); - %eraserPtr = getelementptr %struct.Slot, %struct.Slot* %head, i32 0, i32 1 - %eraser = load void (%struct.Slot*)*, void (%struct.Slot*)** %eraserPtr - call void %eraser(%struct.Slot* %head) + %eraserptr = getelementptr inbounds %struct.Slot, %struct.Slot* %head, i32 0, i32 1 + %eraser = load %Eraser, void (%struct.Slot*)** %eraserptr, align 8, !alias.scope !14, !noalias !24 + tail call void %eraser(%struct.Slot* %head) ; return reusedSlot; ret %struct.Slot* %head bump_alloc: ; Load raw pointer - %rawBump = load i8*, i8** @nextUnusedSlot + %rawBump = load i8*, i8** @nextUnusedSlot, align 8 ; Treat it as Object* - %objectBump = bitcast i8* %rawBump to %Object* - - ; Load object size - %sizeBump = load i64, i64* @slotSize + %slot = bitcast i8* %rawBump to %struct.Slot* ; Move bump pointer forward by object size - %nextBump = getelementptr i8, i8* %rawBump, i64 %sizeBump - store i8* %nextBump, i8** @nextUnusedSlot + %sizeBump = load i8, i8* @slotSize, align 8 + %nextBump = getelementptr i8, i8* %rawBump, i8 %sizeBump + store i8* %nextBump, i8** @nextUnusedSlot, align 8 - ; Return the typed pointer - ret %Object* %objectBump + ret %struct.Slot* %slot } ; Pushes a slot on the top of the To-Do-List. -define void @release(%Object* %object) { +define private void @release(%struct.Slot* %slot) nounwind { entry: - ; Cast Object to Slot - ;%slot = bitcast %Object* %object to %struct.Slot* - ; oldHead = todoList - ;%oldHead = load %struct.Slot*, %struct.Slot** @todoList + %oldHead = load %struct.Slot*, %struct.Slot** @todoList, align 8 ; ptr->next = oldHead - ;%nextPtr = getelementptr %struct.Slot, %struct.Slot* %slot, i32 0, i32 0 - ;store %struct.Slot* %oldHead, %struct.Slot** %nextPtr + %nextPtr = getelementptr %struct.Slot, %struct.Slot* %slot, i32 0, i32 0 + store %struct.Slot* %oldHead, %struct.Slot** %nextPtr, align 8 ; todoList = ptr - ;store %struct.Slot* %slot, %struct.Slot** @todoList + store %struct.Slot* %slot, %struct.Slot** @todoList, align 8 ret void }