From a63be62240a5063933cdaf2879b586c6443c269b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Isalski?= Date: Sun, 13 Sep 2026 14:50:47 +0200 Subject: [PATCH 1/6] Added hashset declaration --- src/hashset.asm | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/hashset.asm diff --git a/src/hashset.asm b/src/hashset.asm new file mode 100644 index 0000000..e1f206b --- /dev/null +++ b/src/hashset.asm @@ -0,0 +1,23 @@ +; struct Allocator { +; alloc: (U32) -> ptr +; realloc: (ptr, U32) -> ptr +; free: (ptr) -> () +; } +; + +; Creates a hashset for the specified allocator, hash function and equality function +; Arguments: +; r1 - The pointer to the allocator +; r2 - The hash function +; r3 - The equality function +; r4 - The hash function context +; r5 - The equality function context +; Result: +; r1 - The pointer to the hashset structure +; Info: +; The hash function should follow the stdlib calling convention +; The hash function receives one argument (the value of the item) and should return an integer which remains the same for the lifetime of the value. +; The equality function should follow the stdlib calling convention +; The equality function receives two arguments (the value of the two items) and should return either a zero (when the items are not equal) or any other value (if they are equal). +pub new_hashset: + \ No newline at end of file -- 2.54.0 From b9c7ffa3d9cd9e75b64d578700e737db388d0b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Isalski?= Date: Mon, 14 Sep 2026 00:19:28 +0200 Subject: [PATCH 2/6] Added new_hashset function to initialize a hash table --- src/hashset.asm | 97 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 88 insertions(+), 9 deletions(-) diff --git a/src/hashset.asm b/src/hashset.asm index e1f206b..761f105 100644 --- a/src/hashset.asm +++ b/src/hashset.asm @@ -1,16 +1,31 @@ -; struct Allocator { -; alloc: (U32) -> ptr -; realloc: (ptr, U32) -> ptr -; free: (ptr) -> () -; } -; +;struct Allocator { +; alloc: (U32) -> ptr +; realloc: (ptr, U32) -> ptr +; free: (ptr) -> () +;} + +;struct HashSet { +; count: U32 +; capacity: U32 +; alloc_ptr: U32 +; hash_ptr: U32 +; hash_context: U32 +; eq_ptr: U32 +; eq_context: U32 +; data_ptr: U32 +;} - 28 bytes + +;struct HashItem { +; hash: U32 +; value: U32 +;} ; Creates a hashset for the specified allocator, hash function and equality function ; Arguments: ; r1 - The pointer to the allocator ; r2 - The hash function -; r3 - The equality function -; r4 - The hash function context +; r3 - The hash function context +; r4 - The equality function ; r5 - The equality function context ; Result: ; r1 - The pointer to the hashset structure @@ -20,4 +35,68 @@ ; The equality function should follow the stdlib calling convention ; The equality function receives two arguments (the value of the two items) and should return either a zero (when the items are not equal) or any other value (if they are equal). pub new_hashset: - \ No newline at end of file + ; First let's free up all the registers + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 ; We will be calling alloc + + mov r8, r1 + mov r9, r2 + mov r10, r3 + mov r11, r4 + mov r12, r5 + + load_32 r5, [r8] ; Loading the alloc function address + mov r1, 28 + counter r13 + add r13, r13, 12 + jmp r5 ; allocating the space for the hashset struct itself + + ; Now r1 has the pointer to memory, r2-r7 are clobbered + + store_32 [r1], rz ; count is 0 at the start + + add r1, r1, 4 + mov r2, 8 + store_32 [r1], r2 ; initial capacity at 8 item slots + + add r1, r1, 4 + store_32 [r1], r8 ; allocator pointer is in r8 + + add r1, r1, 4 + store_32 [r1], r9 ; hash pointer is in r9 + + add r1, r1, 4 + store_32 [r1], r10 ; hash context is in r10 + + add r1, r1, 4 + store_32 [r1], r11 ; eq pointer is in r11 + + add r1, r1, 4 + store_32 [r1], r12 ; eq context is in r12 + + push r1 + + load_32 r5, [r8] ; Loading the alloc function address + mov r1, 64 + counter r13 + add r13, r13, 12 + jmp r5 ; allocating the space for the data + + load r2, [sp] ; Now r1 is data pointer, r2 is hashset pointer at eq context + + add r2, r2, 4 + store_32 [r2], r1 ; data pointer saved + + pop r1 ; Popping the pointer to start of the hashset + + pop r13 ; Everything's set up, return + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + jmp r13 \ No newline at end of file -- 2.54.0 From f70428e312375497b40b592936193fda2b131406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Isalski?= Date: Mon, 14 Sep 2026 00:24:57 +0200 Subject: [PATCH 3/6] Removed one push-pop pair --- src/hashset.asm | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/hashset.asm b/src/hashset.asm index 761f105..ee98a32 100644 --- a/src/hashset.asm +++ b/src/hashset.asm @@ -78,7 +78,7 @@ pub new_hashset: add r1, r1, 4 store_32 [r1], r12 ; eq context is in r12 - push r1 + add r12, r1, 4 ; Now r12 is hashset pointer at data_ptr load_32 r5, [r8] ; Loading the alloc function address mov r1, 64 @@ -86,12 +86,9 @@ pub new_hashset: add r13, r13, 12 jmp r5 ; allocating the space for the data - load r2, [sp] ; Now r1 is data pointer, r2 is hashset pointer at eq context - - add r2, r2, 4 store_32 [r2], r1 ; data pointer saved - pop r1 ; Popping the pointer to start of the hashset + sub r1, r12, 28 ; Getting the pointer to start of the hashset pop r13 ; Everything's set up, return pop r12 -- 2.54.0 From e309ee4fda3895fc437acfb00a38dcde7a4064b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Isalski?= Date: Tue, 15 Sep 2026 23:56:35 +0200 Subject: [PATCH 4/6] Added contains function. Changed find_index returning flags now too --- src/array.asm | 3 + src/hashset.asm | 151 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 136 insertions(+), 18 deletions(-) diff --git a/src/array.asm b/src/array.asm index c90ba38..30b4c2c 100644 --- a/src/array.asm +++ b/src/array.asm @@ -7,6 +7,7 @@ ; r5 - Predicate context ; Result: ; r1 - The index of the first element matching the provided predicate function (or -1 if not found) +; flags - 0, if the element does not exist in the array. 1 if the element does exist. ; Clobbers: r2, r3, r4, r5, r6, + what the predicate clobbers ; Info: ; The predicate function should follow the stdlib calling convention @@ -74,4 +75,6 @@ pub find_index: pop r10 pop r11 pop r12 + lsr flags, r1, 31 + xor flags, flags, 1 jmp r13 ; Return diff --git a/src/hashset.asm b/src/hashset.asm index ee98a32..5fee078 100644 --- a/src/hashset.asm +++ b/src/hashset.asm @@ -5,21 +5,139 @@ ;} ;struct HashSet { -; count: U32 -; capacity: U32 -; alloc_ptr: U32 -; hash_ptr: U32 +; buckets: U32 +; alloc_ptr: *Allocator +; hash_ptr: *(Item) -> U32 ; hash_context: U32 -; eq_ptr: U32 +; eq_ptr: *(Item, Item) -> Bool ; eq_context: U32 -; data_ptr: U32 -;} - 28 bytes - -;struct HashItem { -; hash: U32 -; value: U32 +; data_ptr: *HashBucket ;} +;struct HashBucket { +; hash0: U32 +; item0: Item +; hash1: U32 +; item1: Item +; hash2: U32 +; item2: Item +; hash3: U32 +; item3: Item +;} + +; Checks if the hashset contains a specified element +; Arguments: +; r1 - The element to check +; r2 - The pointer to the hashset +; Result: +; r1 - 0, if the element does not exist in the hashset. 1 if the element does exist. +; r2 - The pointer to the hashset +; flags - 0, if the element does not exist in the hashset. 1 if the element does exist. +pub contains: + push r13 + push r8 + push r9 + push r10 + push r11 + push r12 + + mov r8, r1 + mov r9, r2 + + add r3, r2, 8 + load_32 r3, [r3] ; Loading the hash pointer + add r2, r2, 12 + load_32 r2, [r2] ; Loading the hash context + + counter r13 + add r13, r13, 12 + jmp r3 ; Calling the hash function with element in r1, context in r2 + + mov r12, r1 + + add r10, r9, 16 + load_32 r10, [r10] ; Loading the eq pointer + add r11, r9, 20 + load_32 r11, [r11] ; Loading the eq context + + ; We have: + ; item in r8 + ; bucket pointer in r9 + ; eq pointer in r10 + ; eq context in r11 + ; item hash in r12 + + load_32 r2, [r9] ; Loading buckets + sub r2, r2, 1 + and r2, r1, r2 ; bucket_index = hash & (buckets - 1) + + add r3, r9, 24 + load_32 r3, [r3] ; Loading data pointer + lsl r2, r2, 5 ; Each bucket is 32 bytes + push r9 + add r9, r2, r3 ; Pointer to bucket + + load_32 r3, [r9] ; First hash + cmp r12, r3 + jne contains_notfirst + ; First item hash passed - checking equality + mov r13, contains_notfirst + jmp contains_eqcheck + + contains_notfirst: + add r9, r9, 8 + load_32 r3, [r9] ; Second hash + cmp r12, r3 + jne contains_notsecond + ; Second item hash passed - checking equality + mov r13, contains_notsecond + jmp contains_eqcheck + + contains_notsecond: + add r9, r9, 8 + load_32 r3, [r9] ; Third hash + cmp r12, r3 + jne contains_notthird + ; Third item hash passed - checking equality + mov r13, contains_notthird + jmp contains_eqcheck + + contains_notthird: + add r9, r9, 8 + load_32 r3, [r9] ; Fourth hash + cmp r12, r3 + jne contains_notfourth + ; Fourth item hash passed - checking equality + mov r13, contains_notfourth + jmp contains_eqcheck + + contains_notfourth: + mov r1, 0 + contains_finish: + pop r2 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop r13 + mov flags, r1 + jmp r13 + + contains_found: + mov r1, 1 + jmp contains_finish + + contains_eqcheck: + add r3, r9, 4 + load_32 r2, [r3] + mov r3, r11 + mov r1, r8 + jmp r10 ; Calling eq check + cmp r1, zr + jne contains_found + jmp r13 + ; Creates a hashset for the specified allocator, hash function and equality function ; Arguments: ; r1 - The pointer to the allocator @@ -34,7 +152,7 @@ ; The hash function receives one argument (the value of the item) and should return an integer which remains the same for the lifetime of the value. ; The equality function should follow the stdlib calling convention ; The equality function receives two arguments (the value of the two items) and should return either a zero (when the items are not equal) or any other value (if they are equal). -pub new_hashset: +pub new: ; First let's free up all the registers push r8 push r9 @@ -57,11 +175,8 @@ pub new_hashset: ; Now r1 has the pointer to memory, r2-r7 are clobbered - store_32 [r1], rz ; count is 0 at the start - - add r1, r1, 4 mov r2, 8 - store_32 [r1], r2 ; initial capacity at 8 item slots + store_32 [r1], r2 ; initial capacity at 8 buckets add r1, r1, 4 store_32 [r1], r8 ; allocator pointer is in r8 @@ -81,14 +196,14 @@ pub new_hashset: add r12, r1, 4 ; Now r12 is hashset pointer at data_ptr load_32 r5, [r8] ; Loading the alloc function address - mov r1, 64 + mov r1, 256 ; 8 buckets * 4 items * 8 bytes per each counter r13 add r13, r13, 12 jmp r5 ; allocating the space for the data store_32 [r2], r1 ; data pointer saved - sub r1, r12, 28 ; Getting the pointer to start of the hashset + sub r1, r12, 24 ; Getting the pointer to start of the hashset pop r13 ; Everything's set up, return pop r12 -- 2.54.0 From 2c2d6ec140b5e4843e7bfac3a47cea398fb0e047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Isalski?= Date: Wed, 16 Sep 2026 20:14:14 +0200 Subject: [PATCH 5/6] Added a fast path when entry hash = 0 --- src/hashset.asm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/hashset.asm b/src/hashset.asm index 5fee078..e1293ed 100644 --- a/src/hashset.asm +++ b/src/hashset.asm @@ -78,6 +78,8 @@ pub contains: add r9, r2, r3 ; Pointer to bucket load_32 r3, [r9] ; First hash + cmp r3, rz + je contains_notfourth ; If hash is zero, no items left to check cmp r12, r3 jne contains_notfirst ; First item hash passed - checking equality @@ -87,6 +89,8 @@ pub contains: contains_notfirst: add r9, r9, 8 load_32 r3, [r9] ; Second hash + cmp r3, rz + je contains_notfourth ; If hash is zero, no items left to check cmp r12, r3 jne contains_notsecond ; Second item hash passed - checking equality @@ -96,6 +100,8 @@ pub contains: contains_notsecond: add r9, r9, 8 load_32 r3, [r9] ; Third hash + cmp r3, rz + je contains_notfourth ; If hash is zero, no items left to check cmp r12, r3 jne contains_notthird ; Third item hash passed - checking equality @@ -105,6 +111,8 @@ pub contains: contains_notthird: add r9, r9, 8 load_32 r3, [r9] ; Fourth hash + cmp r3, rz + je contains_notfourth ; If hash is zero, no items left to check cmp r12, r3 jne contains_notfourth ; Fourth item hash passed - checking equality -- 2.54.0 From 2e9e7a176f80725642f7e885eeddc122419c3427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Isalski?= Date: Wed, 16 Sep 2026 20:19:47 +0200 Subject: [PATCH 6/6] Added handling item actually having hash 0 --- src/hashset.asm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hashset.asm b/src/hashset.asm index e1293ed..93d2be0 100644 --- a/src/hashset.asm +++ b/src/hashset.asm @@ -53,6 +53,11 @@ pub contains: add r13, r13, 12 jmp r3 ; Calling the hash function with element in r1, context in r2 + cmp r1, rz + jne contains_skip_one + add r1, r1, 1 ; If hash is zero, we add 1. That way hash being 0 means the item does not exist at a position + contains_skip_one: + mov r12, r1 add r10, r9, 16 -- 2.54.0