Skip to content

Commit 09ed3ca

Browse files
committed
temp commit
Signed-off-by: Ronald Caesar <github43132@proton.me>
1 parent 2e2b5df commit 09ed3ca

File tree

5 files changed

+157
-58
lines changed

5 files changed

+157
-58
lines changed

CMakeLists.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ add_executable(Pound
9797

9898
add_subdirectory(3rd_Party)
9999
add_subdirectory(src/common)
100-
add_subdirectory(src/frontend)
101100
add_subdirectory(src/host)
102101
add_subdirectory(src/jit)
103102
add_subdirectory(src/pvm)
@@ -110,7 +109,7 @@ add_subdirectory(src/targets/switch1/hardware)
110109
include(TestBigEndian)
111110
TEST_BIG_ENDIAN(WORDS_BIGENDIAN)
112111

113-
list(APPEND POUND_PROJECT_TARGETS common frontend host pvm)
112+
list(APPEND POUND_PROJECT_TARGETS common host pvm)
114113
foreach(TARGET ${POUND_PROJECT_TARGETS})
115114
# Apply Endianness definitions to all our targets.
116115
if(WORDS_BIGENDIAN)
@@ -149,7 +148,6 @@ set_property(TARGET Pound PROPERTY CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TR
149148

150149
target_link_libraries(Pound PRIVATE
151150
common
152-
frontend
153151
host
154152
jit
155153
pvm

src/jit/decoder/arm32.cpp

Lines changed: 139 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include "arm32.h"
2-
#include <string.h>
32
#include "common/passert.h"
3+
#include <string.h>
44

55
#define LOG_MODULE "jit"
66
#include "common/logging.h"
@@ -9,18 +9,19 @@ namespace pound::jit::decoder
99
{
1010
/* Increase value as more instructions get implemented */
1111
#define INSTRUCTION_ARRAY_CAPACITY 4
12-
arm32_decoder_t g_arm32_decoder = {};
12+
13+
#define HASH_TABLE_INVALID_INDEX 0xFFFF
1314

1415
/*
1516
* ============================================================================
1617
* Foward Declarations
1718
* ============================================================================
1819
*/
19-
void arm32_add_instruction(arm32_decoder_t* decoder, const char* name, const char* bitstring, arm32_handler_fn handler);
20-
void arm32_ADD_imm_handler(arm32_decoder_t* decoder, arm32_instruction_t instruction);
20+
void arm32_add_instruction(arm32_decoder_t* decoder, const char* name, const char* bitstring);
2121

2222
void arm32_parse_bitstring(const char* bitstring, uint32_t* mask, uint32_t* expected);
2323
void arm32_grow_instructions_array(arm32_decoder_t* decoder, size_t new_capacity);
24+
uint32_t arm32_generate_hash_seed(uint32_t mask, uint32_t expected);
2425

2526
/*
2627
* ============================================================================
@@ -38,7 +39,7 @@ void arm32_init(pound::host::memory::arena_t allocator, arm32_decoder_t* decoder
3839
/* Setup Instructions array.*/
3940
size_t instructions_array_size = INSTRUCTION_ARRAY_CAPACITY * sizeof(arm32_instruction_info_t);
4041
PVM_ASSERT(instructions_array_size <= decoder->allocator.capacity);
41-
LOG_TRACE("Growing instructions array to %d bytes", instructions_array_size);
42+
LOG_TRACE("Allocated %d bytes to instructions array", instructions_array_size);
4243

4344
void* new_ptr = pound::host::memory::arena_allocate(&decoder->allocator, instructions_array_size);
4445
PVM_ASSERT(nullptr != new_ptr);
@@ -47,57 +48,131 @@ void arm32_init(pound::host::memory::arena_t allocator, arm32_decoder_t* decoder
4748
decoder->instruction_capacity = INSTRUCTION_ARRAY_CAPACITY;
4849

4950
/* Add all Arm32 instructions */
50-
#define INST(fn, name, bitstring) arm32_add_instruction(decoder, name, bitstring, &arm32_##fn##_handler);
51+
#define INST(fn, name, bitstring) arm32_add_instruction(decoder, name, bitstring);
5152
#include "./arm32.inc"
5253
#undef INST
54+
55+
LOG_TRACE("Initializing perfect hash parameters for %zu instructions", decoder->instruction_count);
56+
57+
/* Start with table size as next power of 2 >= count * 2 */
58+
uint16_t table_size = 1U;
59+
while (table_size < decoder->instruction_count * 2)
60+
{
61+
table_size <<= 1U;
62+
}
63+
64+
uint16_t mask = table_size - 1;
65+
size_t slots_used_size = table_size * sizeof(bool);
66+
bool* slots_used = (bool*)pound::host::memory::arena_allocate(&decoder->allocator, slots_used_size);
67+
PVM_ASSERT(nullptr != slots_used);
68+
LOG_TRACE("Allocated %d bytes to hash slots used array", slots_used_size);
69+
70+
uint16_t shift = 0;
71+
bool perfect_hash_generated = false;
72+
for (; shift < 16; ++shift)
73+
{
74+
bool collision_free = true;
75+
(void)memset(slots_used, false, slots_used_size);
76+
77+
uint16_t hash;
78+
for (size_t i = 0; i < decoder->instruction_count; ++i)
79+
{
80+
uint32_t hash_seed =
81+
arm32_generate_hash_seed(decoder->instructions[i].mask, decoder->instructions[i].expected);
82+
83+
/*
84+
* Now we need to convert the 32-bit hash seed into an index within our hash table.
85+
* We right shift by `shift` bits. Different shift values produce different hash distributions. We found
86+
* the shift value that ensures no two instructions map to the same hash table slot.
87+
*/
88+
hash = ((hash_seed >> shift) & mask);
89+
if (true == slots_used[hash])
90+
{
91+
LOG_TRACE("Instruction %s: Collision detected at slot %u for shift %u", decoder->instructions[i].name,
92+
hash, shift);
93+
collision_free = false;
94+
break;
95+
}
96+
slots_used[hash] = true;
97+
LOG_TRACE("Instruction %s: Collision-free hash %u found for shift %u", decoder->instructions[i].name, hash,
98+
shift);
99+
}
100+
if (true == collision_free)
101+
{
102+
perfect_hash_generated = true;
103+
break;
104+
}
105+
}
106+
107+
PVM_ASSERT_MSG(true == perfect_hash_generated, "Failed to generate perfect hash - no collision-free hash found");
108+
LOG_TRACE("Perfect hash parameters: shift=%u, mask=0x%04x, table_size=%u", shift, mask, table_size);
109+
110+
size_t hash_table_size = table_size * sizeof(uint16_t);
111+
uint16_t* hash_table = (uint16_t*)pound::host::memory::arena_allocate(&decoder->allocator, hash_table_size);
112+
PVM_ASSERT(nullptr != hash_table);
113+
LOG_TRACE("Allocated %zu bytes to hash table", hash_table_size);
114+
115+
/* Initialize hash table with invalid indices */
116+
for (size_t i = 0; i < table_size; ++i)
117+
{
118+
hash_table[i] = HASH_TABLE_INVALID_INDEX;
119+
}
120+
121+
for (size_t i = 0; i < decoder->instruction_count; ++i)
122+
{
123+
uint32_t hash_seed = arm32_generate_hash_seed(decoder->instructions[i].mask, decoder->instructions[i].expected);
124+
uint32_t hash = (hash_seed >> shift) & mask;
125+
PVM_ASSERT_MSG(HASH_TABLE_INVALID_INDEX == hash_table[hash], "Hash collision detected");
126+
127+
PVM_ASSERT(i < 0xFFFF);
128+
hash_table[hash] = (uint16_t)i;
129+
130+
LOG_TRACE("Instruction '%s' hashed to slot %u", decoder->instructions[i].name, hash);
131+
}
132+
133+
decoder->perfect_hash.hash_shift = shift;
134+
decoder->perfect_hash.hash_mask = mask;
135+
decoder->perfect_hash.table_size = table_size;
136+
decoder->perfect_hash.hash_table = hash_table;
137+
}
138+
139+
arm32_instruction_info_t* arm32_decode(arm32_decoder_t* decoder, uint32_t instruction)
140+
{
141+
53142
}
143+
/*
144+
* ============================================================================
145+
* Private Functions
146+
* ============================================================================
147+
*/
54148

55-
void arm32_add_instruction(arm32_decoder_t* decoder, const char* name, const char* bitstring, arm32_handler_fn handler)
149+
void arm32_add_instruction(arm32_decoder_t* decoder, const char* name, const char* bitstring)
56150
{
57151
PVM_ASSERT(nullptr != decoder);
58152
PVM_ASSERT(nullptr != decoder->allocator.data);
59153
PVM_ASSERT(nullptr != name);
60154
PVM_ASSERT(nullptr != bitstring);
61155
PVM_ASSERT(decoder->instruction_count < decoder->instruction_capacity);
62156

63-
arm32_opcode_t mask = 0;
64-
arm32_opcode_t expected = 0;
157+
uint32_t mask = 0;
158+
uint32_t expected = 0;
65159
arm32_parse_bitstring(bitstring, &mask, &expected);
66160

67161
arm32_instruction_info_t* info = &decoder->instructions[decoder->instruction_count];
162+
PVM_ASSERT(nullptr != info);
68163
info->name = name;
69164
info->mask = mask;
70165
info->expected = expected;
71-
info->handler = handler;
72166

73167
/* Calculate priority based on number of fixed bits. */
74-
info->priority = 0;
75-
for (int i = 0; i < 32; ++i)
76-
{
77-
if ((mask >> i) & 1)
78-
{
79-
++info->priority;
80-
}
81-
}
82-
168+
info->priority = (uint8_t)__builtin_popcount(mask);
83169
++decoder->instruction_count;
84-
LOG_TRACE("========================================");
170+
85171
LOG_TRACE("Instruction Registered: %s", info->name);
86172
LOG_TRACE("Mask: 0x%08X", info->mask);
87173
LOG_TRACE("Expected: 0x%08X", info->expected);
88174
LOG_TRACE("Priority: %d", info->priority);
89-
LOG_TRACE("========================================");
90-
91-
92-
/* TODO(GloriousTacoo:jit): Add instruction to lookup table. */
93175
}
94-
void arm32_ADD_imm_handler(arm32_decoder_t* decoder, arm32_instruction_t instruction) {}
95-
96-
/*
97-
* ============================================================================
98-
* Private Functions
99-
* ============================================================================
100-
*/
101176

102177
void arm32_parse_bitstring(const char* bitstring, uint32_t* mask, uint32_t* expected)
103178
{
@@ -109,7 +184,7 @@ void arm32_parse_bitstring(const char* bitstring, uint32_t* mask, uint32_t* expe
109184
*mask = 0;
110185
*expected = 0;
111186
uint8_t instruction_size_bits = 32;
112-
for (int i = 0; (i < instruction_size_bits) && (bitstring[i] != '\0'); ++i)
187+
for (unsigned int i = 0; (i < instruction_size_bits) && (bitstring[i] != '\0'); ++i)
113188
{
114189
uint32_t bit_position = 31 - i;
115190
switch (bitstring[i])
@@ -135,4 +210,36 @@ void arm32_parse_bitstring(const char* bitstring, uint32_t* mask, uint32_t* expe
135210
}
136211
}
137212

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+
}
243+
244+
138245
} // namespace pound::jit::decoder

src/jit/decoder/arm32.h

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,22 @@
77

88
namespace pound::jit::decoder
99
{
10-
typedef uint32_t arm32_opcode_t;
11-
typedef uint32_t arm32_instruction_t;
12-
1310
typedef struct arm32_decoder arm32_decoder_t;
14-
typedef void (*arm32_handler_fn)(arm32_decoder_t* decoder, arm32_instruction_t instruction);
11+
typedef void (*arm32_handler_fn)(arm32_decoder_t* decoder, uint32_t instruction);
12+
13+
typedef struct
14+
{
15+
uint16_t* hash_table;
16+
uint16_t hash_shift;
17+
uint16_t hash_mask;
18+
uint16_t table_size;
19+
} arm32_perfect_hash_t;
1520

1621
typedef struct
1722
{
1823
const char* name;
19-
arm32_opcode_t mask;
20-
arm32_opcode_t expected;
24+
uint32_t mask;
25+
uint32_t expected;
2126
arm32_handler_fn handler;
2227

2328
/* Use to order instructions in the lookup table. The more specific
@@ -27,27 +32,15 @@ typedef struct
2732

2833
struct arm32_decoder
2934
{
35+
arm32_perfect_hash_t perfect_hash;
3036
pound::host::memory::arena_t allocator;
3137
arm32_instruction_info_t* instructions;
3238
size_t instruction_count;
3339
size_t instruction_capacity;
34-
35-
struct
36-
{
37-
arm32_instruction_info_t** bucket;
38-
size_t count;
39-
size_t capacity;
40-
} lookup_table[4096]; /* 2^12 entries. */
4140
};
4241

4342
extern arm32_decoder_t g_arm32_decoder;
4443

4544
void arm32_init(pound::host::memory::arena_t allocator, arm32_decoder_t* decoder);
46-
47-
void arm32_add_instruction(arm32_decoder_t* decoder, const char* name, arm32_opcode_t mask, arm32_opcode_t expected,
48-
arm32_handler_fn handler);
49-
void arm32_ADD_imm_handler(arm32_decoder_t* decoder, arm32_instruction_t instruction);
50-
51-
52-
} // namespace pound::jit::decoder
53-
#endif // POUND_JIT_DECODER_ARM32_H
45+
}
46+
#endif // POUND_JIT_DECODER_ARM32_H

src/jit/decoder/arm32.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ INST(ADD_imm, "ADD (imm)", "cccc0010100Snnnnddddrrrrvvvvvvvv") // v1
6464
//INST(SBC_imm, "SBC (imm)", "cccc0010110Snnnnddddrrrrvvvvvvvv") // v1
6565
//INST(SBC_reg, "SBC (reg)", "cccc0000110Snnnnddddvvvvvrr0mmmm") // v1
6666
//INST(SBC_rsr, "SBC (rsr)", "cccc0000110Snnnnddddssss0rr1mmmm") // v1
67-
//INST(SUB_imm, "SUB (imm)", "cccc0010010Snnnnddddrrrrvvvvvvvv") // v1
67+
INST(SUB_imm, "SUB (imm)", "cccc0010010Snnnnddddrrrrvvvvvvvv") // v1
6868
//INST(SUB_reg, "SUB (reg)", "cccc0000010Snnnnddddvvvvvrr0mmmm") // v1
6969
//INST(SUB_rsr, "SUB (rsr)", "cccc0000010Snnnnddddssss0rr1mmmm") // v1
7070
//INST(TEQ_imm, "TEQ (imm)", "cccc00110011nnnn0000rrrrvvvvvvvv") // v1

src/main.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
int main()
2121
{
2222
pound::host::memory::arena_t arena = pound::host::memory::arena_init(256);
23-
pound::jit::decoder::arm32_init(arena, &pound::jit::decoder::g_arm32_decoder);
23+
pound::jit::decoder::arm32_decoder_t decoder = {};
24+
pound::jit::decoder::arm32_init(arena, &decoder);
2425
#if 0
2526
gui::window_t window = {.data = nullptr, .gl_context = nullptr};
2627
(void)gui::window_init(&window, "Pound Emulator", 640, 480);

0 commit comments

Comments
 (0)