Skip to content

Commit f0b2b31

Browse files
authored
Merge branch 'pound-emu:main' into feature-wasm-backend
2 parents 67d1178 + 0428cc1 commit f0b2b31

7 files changed

Lines changed: 310 additions & 6 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ if (BALLISTIC_BUILD_TESTS)
159159
gtest_discover_tests(${DECODER_TEST})
160160
gtest_discover_tests(${MEMORY_TEST})
161161

162-
set(TRANSLATION_TESTS movk movn movz ret)
162+
set(TRANSLATION_TESTS movk movn movz ret sub)
163163
foreach (test_name ${TRANSLATION_TESTS})
164164
set(target_name "test_${test_name}")
165165
add_executable(${target_name} "tests/translation/${target_name}.cpp")

include/bal_assembler.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,30 @@ extern "C"
9393
uint16_t imm12,
9494
uint8_t shift);
9595

96+
/// Emit a `SUB` (Immediate) instruction.
97+
///
98+
/// Subtracts a 12 bit immediate value `imm12` with the optional left shift `sh` from register
99+
/// value `rn`, and writes the result to the destination register `rd`.
100+
///
101+
/// # Safety
102+
///
103+
/// * `rn` will be truncated if it exceeds 5 bits.
104+
/// * `imm12` will be truncated if it exceeds 12 bits.
105+
/// * `sh` must have the value `0`, or `1`
106+
/// * Function does not emit instructions if `assembler->status != BAL_SUCCESS`.
107+
///
108+
/// # Errors
109+
///
110+
/// Modifies `assembler->status` to the following if an error occurs:
111+
///
112+
/// * [`BAL_ERROR_INSTRUCTION_OVERFLOW`] if `assembler->offset >= assembler->capacity`.
113+
/// * [`BAL_ERROR_INVALID_ARGUMENT`] if function arguments are invalid.
114+
void bal_emit_sub_immediate(bal_assembler_t *assembler,
115+
bal_register_index_t rd,
116+
uint8_t rn,
117+
uint16_t imm12,
118+
uint8_t shift);
119+
96120
/// Emit a `MOVZ` (Move Wide with Zero) instruction.
97121
///
98122
/// Moves a 16-bit immediate into a register, shifting it left by 0, 16, 32, or 48 bits, and

include/bal_engine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ extern "C"
145145
///
146146
/// # Safety
147147
///
148-
/// If `guest_address_staart` is `NULL`, we assume the entry point is 0x0.
148+
/// `guest_address_start` must be non-NULL.
149149
///
150150
/// # Errors
151151
///

include/bal_types.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ extern "C"
3232
OPCODE_DIV,
3333
OPCODE_AND,
3434
OPCODE_XOR,
35-
OPCODE_NOR,
35+
OPCODE_OR_NOT,
3636
OPCODE_SHIFT,
3737
OPCODE_LOAD,
3838
OPCODE_STORE,

src/bal_assembler.c

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ bal_assembler_init(bal_assembler_t *assembler, void *buffer, const size_t size,
1616
if (NULL == buffer)
1717
{
1818
BAL_LOG_ERROR(&logger, "Buffer is NULL.");
19+
return BAL_ERROR_INVALID_ARGUMENT;
1920
}
2021

2122
if ((uintptr_t)buffer % 4 != 0)
2223
{
23-
BAL_LOG_ERROR(&logger, "Buffer %p is not 4-byte aligned.");
24+
BAL_LOG_ERROR(&logger, "Buffer %p is not 4-byte aligned.", buffer);
2425
return BAL_ERROR_MEMORY_ALIGNMENT;
2526
}
2627

@@ -103,6 +104,74 @@ bal_emit_add_immediate(bal_assembler_t *assembler,
103104
assembler->buffer[assembler->offset++] = instruction;
104105
}
105106

107+
void
108+
bal_emit_sub_immediate(bal_assembler_t *assembler,
109+
const bal_register_index_t rd,
110+
const uint8_t rn,
111+
const uint16_t imm12,
112+
const uint8_t shift)
113+
{
114+
if (NULL == assembler)
115+
{
116+
return;
117+
}
118+
119+
if (assembler->status != BAL_SUCCESS)
120+
{
121+
return;
122+
}
123+
124+
const bool can_emit_return_value = can_emit(assembler);
125+
126+
if (false == can_emit_return_value)
127+
{
128+
return;
129+
}
130+
131+
if (rd > 31)
132+
{
133+
BAL_LOG_ERROR(&assembler->logger, "X%u out of range (0-31).", rd);
134+
assembler->status = BAL_ERROR_INVALID_ARGUMENT;
135+
return;
136+
}
137+
138+
if (shift != 0 && shift != 1)
139+
{
140+
BAL_LOG_ERROR(&assembler->logger, "%u is not a valid shift amount (0-1).", shift);
141+
assembler->status = BAL_ERROR_INVALID_ARGUMENT;
142+
return;
143+
}
144+
145+
const uint32_t imm12_mask = 0xFFF;
146+
const uint32_t rn_mask = 0x1F;
147+
const uint32_t imm12_uint32 = imm12 & imm12_mask;
148+
const uint32_t rn_uint32 = rn & rn_mask;
149+
const uint32_t hard_coded_bits = 0xA2U;
150+
const uint32_t shift_uint32 = shift;
151+
const uint32_t sf = 1U;
152+
153+
uint32_t instruction = 0;
154+
instruction |= sf << 31;
155+
instruction |= hard_coded_bits << 23;
156+
instruction |= shift_uint32 << 22;
157+
instruction |= imm12_uint32 << 10;
158+
instruction |= rn_uint32 << 5;
159+
instruction |= rd;
160+
161+
const char *mnemonic = "SUB (Imm)";
162+
BAL_LOG_TRACE(&assembler->logger,
163+
"[+0x%04zx] %08x %s X%u, #0x%04x, LSL #%u",
164+
assembler->offset * sizeof(uint32_t),
165+
instruction,
166+
mnemonic,
167+
rd,
168+
imm12_uint32,
169+
shift_uint32);
170+
171+
(void)mnemonic;
172+
assembler->buffer[assembler->offset++] = instruction;
173+
}
174+
106175
void
107176
bal_emit_movz(bal_assembler_t *assembler,
108177
const bal_register_index_t rd,

src/bal_engine.c

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ static uint32_t intern_constant(bal_translation_context_t *, bal_constant_t);
3636
static void translate_const(bal_translation_context_t *,
3737
const bal_decoder_instruction_metadata_t *,
3838
const uint32_t *);
39+
static void translate_sub(bal_translation_context_t *,
40+
const bal_decoder_instruction_metadata_t *,
41+
const uint32_t *);
3942
static void translate_return(bal_translation_context_t *, const uint32_t *);
43+
4044
BAL_COLD bal_error_t
4145
bal_engine_init(const bal_allocator_t *allocator, bal_engine_t *engine, bal_logger_t logger)
4246
{
@@ -146,9 +150,11 @@ bal_engine_translate(bal_engine_t *BAL_RESTRICT engine,
146150
return engine->status;
147151
}
148152

149-
if (BAL_UNLIKELY(NULL == &guest_address_start))
153+
if (BAL_UNLIKELY(NULL == guest_address_start))
150154
{
151-
BAL_LOG_INFO(&engine->logger, "Guest Address is NULL, assuming entry point is 0x0");
155+
BAL_LOG_ERROR(&engine->logger, "Guest Address is NULL, aborting translation");
156+
engine->status = BAL_ERROR_INVALID_ARGUMENT;
157+
return engine->status;
152158
}
153159

154160
const size_t max_instructions_size_bytes = max_instructions * ARM_INSTRUCTION_SIZE_BYTES;
@@ -262,6 +268,9 @@ bal_engine_translate(bal_engine_t *BAL_RESTRICT engine,
262268
case OPCODE_CONST:
263269
translate_const(&context, metadata, arm_instruction_operands);
264270
break;
271+
case OPCODE_SUB:
272+
translate_sub(&context, metadata, arm_instruction_operands);
273+
break;
265274
case OPCODE_RETURN:
266275
translate_return(&context, arm_instruction_operands);
267276
is_block_terminated = true;
@@ -574,4 +583,74 @@ translate_return(bal_translation_context_t *context, const uint32_t *arm_registe
574583
BAL_LOG_DEBUG(
575584
context->logger, " EMIT: v%u = RET v%u", context->instruction_count, rn_ssa_index);
576585
context->instruction_count++;
586+
}
587+
588+
BAL_HOT static void
589+
translate_sub(bal_translation_context_t *context,
590+
const bal_decoder_instruction_metadata_t *metadata,
591+
const uint32_t *arm_registers)
592+
{
593+
if (BAL_UNLIKELY(BAL_OPERAND_TYPE_IMMEDIATE != metadata->operands[2].type))
594+
{
595+
BAL_LOG_DEBUG(context->logger,
596+
" SKIPPED: SUB variant '%s' not yet implemented in IR layer.",
597+
metadata->name);
598+
return;
599+
}
600+
601+
const uint64_t rd = arm_registers[0];
602+
const uint64_t rn = arm_registers[1];
603+
const uint64_t imm12 = arm_registers[2];
604+
const uint64_t sh = arm_registers[3];
605+
const uint64_t shift = (1 == sh) ? 12 : 0;
606+
const uint64_t value = imm12 << shift;
607+
608+
BAL_LOG_TRACE(context->logger,
609+
" Variant='Imm' Rd=%lu Rn=%lu Imm12=0x%lX Shift=%lu Value=0x%llX",
610+
rd,
611+
rn,
612+
imm12,
613+
shift,
614+
(unsigned long long)value);
615+
616+
const uint64_t rn_ssa_index = get_or_create_ssa_index(context, rn);
617+
const uint64_t value_const_index = intern_constant(context, value);
618+
619+
if (BAL_UNLIKELY(context->status != BAL_SUCCESS))
620+
{
621+
return;
622+
}
623+
624+
bal_bit_width_t bit_width;
625+
626+
switch (metadata->operands[0].type)
627+
{
628+
case BAL_OPERAND_TYPE_REGISTER_32:
629+
bit_width = 32;
630+
break;
631+
case BAL_OPERAND_TYPE_REGISTER_64:
632+
bit_width = 64;
633+
break;
634+
default:
635+
BAL_LOG_ERROR(context->logger, "Unknown register type for SUB (Imm) Rd register");
636+
context->status = BAL_ERROR_INCORRECT_REGISTER_TYPE;
637+
return;
638+
}
639+
640+
*context->ir_instruction_cursor = (bal_instruction_t)OPCODE_SUB << BAL_OPCODE_SHIFT_POSITION
641+
| rn_ssa_index << BAL_SOURCE1_SHIFT_POSITION
642+
| value_const_index << BAL_SOURCE2_SHIFT_POSITION;
643+
*context->bit_width_cursor = bit_width;
644+
645+
BAL_LOG_DEBUG(context->logger,
646+
" EMIT: v%u = SUB v%u, c%u (%u-bit)",
647+
context->instruction_count,
648+
(uint32_t)rn_ssa_index,
649+
(uint32_t)(value_const_index & ~BAL_IS_CONSTANT_BIT_POSITION),
650+
bit_width);
651+
652+
context->source_variables[rd].current_ssa_index = context->instruction_count;
653+
BAL_LOG_TRACE(context->logger, " SSA UPDATE: X%lu -> v%u", rd, context->instruction_count);
654+
655+
context->instruction_count++;
577656
}

tests/translation/test_sub.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
#include "setup.h"
2+
#include <cinttypes>
3+
#include <cstdio>
4+
#include <cstdlib>
5+
#include <gtest/gtest.h>
6+
7+
TEST(Translation, SubImmediate)
8+
{
9+
test_context_t context;
10+
test_setup(&context);
11+
constexpr bal_register_index_t rds[] = {
12+
BAL_REGISTER_X0,
13+
BAL_REGISTER_X1,
14+
BAL_REGISTER_X15,
15+
BAL_REGISTER_X30,
16+
};
17+
18+
constexpr uint8_t rn = BAL_REGISTER_X5;
19+
constexpr uint16_t immediates[] = { 0U, 1U, 0xAAAU, 0xFFFU };
20+
constexpr uint8_t shifts[] = { 0U, 1U };
21+
22+
constexpr size_t rds_count = std::size(rds);
23+
24+
for (const bal_register_index_t rd : rds)
25+
{
26+
for (const unsigned short immediate : immediates)
27+
{
28+
for (const unsigned char shift : shifts)
29+
{
30+
bal_emit_sub_immediate(&context.assembler, rd, rn, immediate, shift);
31+
}
32+
}
33+
}
34+
35+
bal_emit_ret(&context.assembler, BAL_REGISTER_X0);
36+
37+
bal_guest_address_t entry_point = 0x0;
38+
bal_engine_translate(&context.engine,
39+
&context.interface,
40+
&entry_point,
41+
context.assembler.offset * sizeof(uint32_t));
42+
43+
const bal_instruction_t *BAL_RESTRICT ir = context.engine.instructions;
44+
size_t ir_index = 0;
45+
46+
// First IR instruction: GET_REGISTER v0 = X5 (lazy load of Rn on its
47+
// first read by the first SUB).
48+
//
49+
{
50+
const auto opcode = static_cast<bal_opcode_t>(ir[ir_index] >> BAL_OPCODE_SHIFT_POSITION);
51+
52+
if (opcode != OPCODE_GET_REGISTER)
53+
{
54+
fprintf(
55+
stderr, "FAIL: IR Inst %zu is not GET_REGISTER (opcode=%d)\n", ir_index, opcode);
56+
GTEST_FAIL();
57+
}
58+
59+
++ir_index;
60+
}
61+
62+
constexpr uint32_t rn_ssa_expected = 0U;
63+
64+
for (size_t r = 0; r < rds_count; ++r)
65+
{
66+
for (const unsigned short immediate : immediates)
67+
{
68+
for (const unsigned char shift : shifts)
69+
{
70+
const bal_instruction_t inst = ir[ir_index];
71+
const auto opcode = static_cast<bal_opcode_t>(inst >> BAL_OPCODE_SHIFT_POSITION);
72+
73+
if (opcode != OPCODE_SUB)
74+
{
75+
fprintf(stderr, "FAIL: IR Inst %zu is not SUB (opcode=%d)\n", ir_index, opcode);
76+
GTEST_FAIL();
77+
}
78+
79+
const uint32_t src1
80+
= inst >> BAL_SOURCE1_SHIFT_POSITION & BAL_SOURCE_MASK_WITH_FLAG;
81+
82+
if (src1 & BAL_IS_CONSTANT_BIT_POSITION)
83+
{
84+
fprintf(stderr,
85+
"FAIL: IR Inst %zu src1 flagged as constant (expected SSA)\n",
86+
ir_index);
87+
GTEST_FAIL();
88+
}
89+
90+
if (src1 != rn_ssa_expected)
91+
{
92+
fprintf(stderr,
93+
"FAIL: IR Inst %zu src1 mismatch. Expected v%u, got v%u\n",
94+
ir_index,
95+
rn_ssa_expected,
96+
src1);
97+
GTEST_FAIL();
98+
}
99+
100+
const uint32_t src2_with_flag
101+
= inst >> BAL_SOURCE2_SHIFT_POSITION & BAL_SOURCE_MASK_WITH_FLAG;
102+
103+
if (!(src2_with_flag & BAL_IS_CONSTANT_BIT_POSITION))
104+
{
105+
fprintf(stderr, "FAIL: IR Inst %zu src2 not flagged as constant\n", ir_index);
106+
GTEST_FAIL();
107+
}
108+
109+
const uint32_t pool_index = src2_with_flag & BAL_SOURCE_MASK;
110+
const uint64_t expected_val = static_cast<uint64_t>(immediate) << (shift * 12U);
111+
const uint64_t actual_val = context.engine.constants[pool_index];
112+
113+
if (actual_val != expected_val)
114+
{
115+
fprintf(stderr,
116+
"FAIL: IR Inst %zu constant c%u mismatch. Expected 0x%" PRIX64
117+
", Got 0x%" PRIX64 "\n",
118+
ir_index,
119+
pool_index,
120+
expected_val,
121+
actual_val);
122+
GTEST_FAIL();
123+
}
124+
++ir_index;
125+
}
126+
}
127+
}
128+
129+
test_teardown(&context);
130+
}
131+
132+
/*** end of file ***/

0 commit comments

Comments
 (0)