#include "arm32.h" #include #include "common/passert.h" #define LOG_MODULE "jit" #include "common/logging.h" namespace pound::jit::decoder { /* Increase value as more instructions get implemented */ #define INSTRUCTION_ARRAY_CAPACITY 4 typedef struct { uint16_t* hash_table; uint8_t* collision_map; uint16_t hash_shift; uint16_t hash_mask; uint16_t table_size; } arm32_perfect_hash_t; /* * ============================================================================ * Foward Declarations * ============================================================================ */ void arm32_add_instruction(arm32_decoder_t* decoder, const char* name, const char* bitstring, arm32_handler_fn handler); void arm32_ADD_imm_handler(arm32_decoder_t* decoder, arm32_instruction_t instruction); void arm32_SUB_imm_handler(arm32_decoder_t* decoder, arm32_instruction_t instruction); void arm32_parse_bitstring(const char* bitstring, uint32_t* mask, uint32_t* expected); void arm32_grow_instructions_array(arm32_decoder_t* decoder, size_t new_capacity); /* * ============================================================================ * Public Functions * ============================================================================ */ void arm32_init(pound::host::memory::arena_t allocator, arm32_decoder_t* decoder) { PVM_ASSERT(nullptr != decoder); PVM_ASSERT(nullptr != allocator.data); (void)memset(decoder, 0, sizeof(arm32_decoder_t)); decoder->allocator = allocator; /* Setup Instructions array.*/ size_t instructions_array_size = INSTRUCTION_ARRAY_CAPACITY * sizeof(arm32_instruction_info_t); PVM_ASSERT(instructions_array_size <= decoder->allocator.capacity); LOG_TRACE("Growing instructions array to %d bytes", instructions_array_size); void* new_ptr = pound::host::memory::arena_allocate(&decoder->allocator, instructions_array_size); PVM_ASSERT(nullptr != new_ptr); decoder->instructions = (arm32_instruction_info_t*)new_ptr; decoder->instruction_capacity = INSTRUCTION_ARRAY_CAPACITY; /* Add all Arm32 instructions */ #define INST(fn, name, bitstring) arm32_add_instruction(decoder, name, bitstring, &arm32_##fn##_handler); #include "./arm32.inc" #undef INST LOG_TRACE("Initializing perfect hash parameters for %zu instructions", decoder->instruction_count); /* Start with table size as next power of 2 >= count * 2 */ uint16_t table_size = 1U; while (table_size < decoder->instruction_count * 2) { table_size <<= 1U; } uint16_t mask = table_size - 1; size_t slots_used_size = table_size * sizeof(bool); bool* slots_used = (bool*)pound::host::memory::arena_allocate(&decoder->allocator, slots_used_size); PVM_ASSERT(nullptr != slots_used); LOG_TRACE("Growing perfevt hash slots used array to %d bytes", slots_used_size); uint16_t shift = 0; bool perfect_hash_generated = false; for (; shift < 16; ++shift) { bool collision_free = true; (void)memset(slots_used, false, slots_used_size); for (size_t i = 0; i < decoder->instruction_count; ++i) { uint32_t value = decoder->instructions[i].mask ^ decoder->instructions[i].expected; value = ((value >> 16) ^ value) * 0x45d9f3b; value = ((value >> 16) ^ value) * 0x45d9f3b; value = (value >> 16) ^ value; uint32_t hash_seed = value; uint16_t hash = ((hash_seed >> shift) & mask); if (true == slots_used[hash]) { LOG_TRACE("COLLISION DETECTED at slot %u for shift %u", hash, shift); collision_free = false; break; } slots_used[hash] = true; } if (true == collision_free) { LOG_TRACE("COLLISION-FREE HASH found for shift %u", shift); perfect_hash_generated = true; break; } } PVM_ASSERT_MSG(true == perfect_hash_generated, "Failed to generate perfect hash - no collision-free hash found"); LOG_TRACE("Perfect hash parameters: shift=%u, mask=0x%04x, table_size=%u", shift, mask, table_size); /* TODO(GloriousTacoo:jit): Generate hash table. */ } /* * ============================================================================ * Private Functions * ============================================================================ */ void arm32_add_instruction(arm32_decoder_t* decoder, const char* name, const char* bitstring, arm32_handler_fn handler) { PVM_ASSERT(nullptr != decoder); PVM_ASSERT(nullptr != decoder->allocator.data); PVM_ASSERT(nullptr != name); PVM_ASSERT(nullptr != bitstring); PVM_ASSERT(decoder->instruction_count < decoder->instruction_capacity); arm32_opcode_t mask = 0; arm32_opcode_t expected = 0; arm32_parse_bitstring(bitstring, &mask, &expected); arm32_instruction_info_t* info = &decoder->instructions[decoder->instruction_count]; info->name = name; info->mask = mask; info->expected = expected; info->handler = handler; /* Calculate priority based on number of fixed bits. */ info->priority = 0; for (int i = 0; i < 32; ++i) { if ((mask >> i) & 1) { ++info->priority; } } ++decoder->instruction_count; LOG_TRACE("Instruction Registered: %s", info->name); LOG_TRACE("Mask: 0x%08X", info->mask); LOG_TRACE("Expected: 0x%08X", info->expected); LOG_TRACE("Priority: %d", info->priority); } void arm32_ADD_imm_handler(arm32_decoder_t* decoder, arm32_instruction_t instruction) {} void arm32_SUB_imm_handler(arm32_decoder_t* decoder, arm32_instruction_t instruction) {} void arm32_parse_bitstring(const char* bitstring, uint32_t* mask, uint32_t* expected) { PVM_ASSERT(nullptr != bitstring); PVM_ASSERT(nullptr != mask); PVM_ASSERT(nullptr != expected); PVM_ASSERT(32 == strlen(bitstring)); *mask = 0; *expected = 0; uint8_t instruction_size_bits = 32; for (unsigned int i = 0; (i < instruction_size_bits) && (bitstring[i] != '\0'); ++i) { uint32_t bit_position = 31 - i; switch (bitstring[i]) { case '0': *mask |= (1U << bit_position); break; case '1': *mask |= (1U << bit_position); *expected |= (1U << bit_position); break; case 'c': case 'n': case 'd': case 'r': case 'v': case 's': case 'S': break; default: PVM_ASSERT_MSG(false, "Invalid bitstring character: %c", bitstring[i]); } } } } // namespace pound::jit::decoder