@@ -10,14 +10,7 @@ namespace pound::jit::decoder
1010/* Increase value as more instructions get implemented */
1111#define INSTRUCTION_ARRAY_CAPACITY 4
1212
13- typedef struct
14- {
15- uint16_t * hash_table;
16- uint8_t * collision_map;
17- uint16_t hash_shift;
18- uint16_t hash_mask;
19- uint16_t table_size;
20- } arm32_perfect_hash_t ;
13+ #define HASH_TABLE_INVALID_INDEX 0xFFFF
2114
2215/*
2316 * ============================================================================
@@ -30,6 +23,7 @@ void arm32_SUB_imm_handler(arm32_decoder_t* decoder, arm32_instruction_t instruc
3023
3124void arm32_parse_bitstring (const char * bitstring, uint32_t * mask, uint32_t * expected);
3225void arm32_grow_instructions_array (arm32_decoder_t * decoder, size_t new_capacity);
26+ uint32_t arm32_generate_hash_seed (uint32_t mask, uint32_t expected);
3327
3428/*
3529 * ============================================================================
@@ -47,7 +41,7 @@ void arm32_init(pound::host::memory::arena_t allocator, arm32_decoder_t* decoder
4741 /* Setup Instructions array.*/
4842 size_t instructions_array_size = INSTRUCTION_ARRAY_CAPACITY * sizeof (arm32_instruction_info_t );
4943 PVM_ASSERT (instructions_array_size <= decoder->allocator .capacity );
50- LOG_TRACE (" Growing instructions array to %d bytes " , instructions_array_size);
44+ LOG_TRACE (" Allocated %d bytes to instructions array " , instructions_array_size);
5145
5246 void * new_ptr = pound::host::memory::arena_allocate (&decoder->allocator , instructions_array_size);
5347 PVM_ASSERT (nullptr != new_ptr);
@@ -73,7 +67,7 @@ void arm32_init(pound::host::memory::arena_t allocator, arm32_decoder_t* decoder
7367 size_t slots_used_size = table_size * sizeof (bool );
7468 bool * slots_used = (bool *)pound::host::memory::arena_allocate (&decoder->allocator , slots_used_size);
7569 PVM_ASSERT (nullptr != slots_used);
76- LOG_TRACE (" Growing hash slots used array to %d bytes " , slots_used_size);
70+ LOG_TRACE (" Allocated %d bytes to hash slots used array" , slots_used_size);
7771
7872 uint16_t shift = 0 ;
7973 bool perfect_hash_generated = false ;
@@ -85,33 +79,8 @@ void arm32_init(pound::host::memory::arena_t allocator, arm32_decoder_t* decoder
8579 uint16_t hash;
8680 for (size_t i = 0 ; i < decoder->instruction_count ; ++i)
8781 {
88- /*
89- * XOR combines the mask (which bits matter) and expected value (what those btis should be)
90- * into a single 32-bit value that uniquely represents this instruction pattern.
91- * Example: ADD instruction might have mask=0x0FF00000, expected=0x00200000
92- * value = 0x0FF00000 ^ 0x00200000 = 0x0FD00000;
93- */
94- uint32_t value = decoder->instructions [i].mask ^ decoder->instructions [i].expected ;
95-
96- /*
97- * First bit mixing round - break up patterns in the value.
98- * We need to eliminate patterns (like trailing zeroes) that cause collisions.
99- * We do this by mixing the high bits (16-31) with the low bits (0-15).
100- *
101- * We then multiply a magic number to further scrambles the bits to ensure good distribution.
102- */
103- value = ((value >> 16 ) ^ value) * 0x45d9f3b ;
104-
105- /*
106- * Second bit mixing round to ensire thorough mixing
107- */
108- value = ((value >> 16 ) ^ value) * 0x45d9f3b ;
109-
110- /*
111- * Final mixing. The result is our hash seed - a 32-bit number that uniquely represents this instruction.
112- */
113- value = (value >> 16 ) ^ value;
114- uint32_t hash_seed = value;
82+ uint32_t hash_seed =
83+ arm32_generate_hash_seed (decoder->instructions [i].mask , decoder->instructions [i].expected );
11584
11685 /*
11786 * Now we need to convert the 32-bit hash seed into an index within our hash table.
@@ -121,12 +90,14 @@ void arm32_init(pound::host::memory::arena_t allocator, arm32_decoder_t* decoder
12190 hash = ((hash_seed >> shift) & mask);
12291 if (true == slots_used[hash])
12392 {
124- LOG_TRACE (" Instruction %s: Collision detected at slot %u for shift %u" , decoder->instructions [i].name , hash, shift);
93+ LOG_TRACE (" Instruction %s: Collision detected at slot %u for shift %u" , decoder->instructions [i].name ,
94+ hash, shift);
12595 collision_free = false ;
12696 break ;
12797 }
12898 slots_used[hash] = true ;
129- LOG_TRACE (" Instruction %s: Collision-free hash %u found for shift %u" , decoder->instructions [i].name , hash, shift);
99+ LOG_TRACE (" Instruction %s: Collision-free hash %u found for shift %u" , decoder->instructions [i].name , hash,
100+ shift);
130101 }
131102 if (true == collision_free)
132103 {
@@ -138,7 +109,33 @@ void arm32_init(pound::host::memory::arena_t allocator, arm32_decoder_t* decoder
138109 PVM_ASSERT_MSG (true == perfect_hash_generated, " Failed to generate perfect hash - no collision-free hash found" );
139110 LOG_TRACE (" Perfect hash parameters: shift=%u, mask=0x%04x, table_size=%u" , shift, mask, table_size);
140111
141- /* TODO(GloriousTacoo:jit): Generate hash table. */
112+ size_t hash_table_size = table_size * sizeof (uint16_t );
113+ uint16_t * hash_table = (uint16_t *)pound::host::memory::arena_allocate (&decoder->allocator , hash_table_size);
114+ PVM_ASSERT (nullptr != hash_table);
115+ LOG_TRACE (" Allocated %zu bytes to hash table" , hash_table_size);
116+
117+ /* Initialize hash table with invalid indices */
118+ for (size_t i = 0 ; i < table_size; ++i)
119+ {
120+ hash_table[i] = HASH_TABLE_INVALID_INDEX;
121+ }
122+
123+ for (size_t i = 0 ; i < decoder->instruction_count ; ++i)
124+ {
125+ uint32_t hash_seed = arm32_generate_hash_seed (decoder->instructions [i].mask , decoder->instructions [i].expected );
126+ uint32_t hash = (hash_seed >> shift) & mask;
127+ PVM_ASSERT_MSG (HASH_TABLE_INVALID_INDEX == hash_table[hash], " Hash collision detected" );
128+
129+ PVM_ASSERT (i < 0xFFFF );
130+ hash_table[hash] = (uint16_t )i;
131+
132+ LOG_TRACE (" Instruction '%s' hashed to slot %u" , decoder->instructions [i].name , hash);
133+ }
134+
135+ decoder->perfect_hash .hash_shift = shift;
136+ decoder->perfect_hash .hash_mask = mask;
137+ decoder->perfect_hash .table_size = table_size;
138+ decoder->perfect_hash .hash_table = hash_table;
142139}
143140
144141/*
@@ -166,16 +163,9 @@ void arm32_add_instruction(arm32_decoder_t* decoder, const char* name, const cha
166163 info->handler = handler;
167164
168165 /* Calculate priority based on number of fixed bits. */
169- info->priority = 0 ;
170- for (int i = 0 ; i < 32 ; ++i)
171- {
172- if ((mask >> i) & 1 )
173- {
174- ++info->priority ;
175- }
176- }
177-
166+ info->priority = (uint8_t )__builtin_popcount (mask);
178167 ++decoder->instruction_count ;
168+
179169 LOG_TRACE (" Instruction Registered: %s" , info->name );
180170 LOG_TRACE (" Mask: 0x%08X" , info->mask );
181171 LOG_TRACE (" Expected: 0x%08X" , info->expected );
@@ -220,4 +210,34 @@ void arm32_parse_bitstring(const char* bitstring, uint32_t* mask, uint32_t* expe
220210 }
221211 }
222212}
213+ uint32_t arm32_generate_hash_seed (uint32_t mask, uint32_t expected)
214+ {
215+ /*
216+ * XOR combines the mask (which bits matter) and expected value (what those btis should be)
217+ * into a single 32-bit value that uniquely represents this instruction pattern.
218+ * Example: ADD instruction might have mask=0x0FF00000, expected=0x00200000
219+ * value = 0x0FF00000 ^ 0x00200000 = 0x0FD00000;
220+ */
221+ uint32_t value = mask ^ expected;
222+
223+ /*
224+ * First bit mixing round - break up patterns in the value.
225+ * We need to eliminate patterns (like trailing zeroes) that cause collisions.
226+ * We do this by mixing the high bits (16-31) with the low bits (0-15).
227+ *
228+ * We then multiply a magic number to further scrambles the bits to ensure good distribution.
229+ */
230+ value = ((value >> 16 ) ^ value) * 0x45d9f3b ;
231+
232+ /*
233+ * Second bit mixing round to ensire thorough mixing
234+ */
235+ value = ((value >> 16 ) ^ value) * 0x45d9f3b ;
236+
237+ /*
238+ * Final mixing. The result is our hash seed - a 32-bit number that uniquely represents this instruction.
239+ */
240+ value = (value >> 16 ) ^ value;
241+ return value;
242+ }
223243} // namespace pound::jit::decoder
0 commit comments