Skip to content

Commit 826005c

Browse files
committed
Add support for inline thumb assembly.
1 parent 5bfb759 commit 826005c

12 files changed

Lines changed: 488 additions & 112 deletions

File tree

py/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ SRC = \
1919
emitx64.c \
2020
emitthumb.c \
2121
asmthumb.c \
22+
emitinlinethumb.c \
2223
runtime.c \
2324
vm.c \
2425
main.c \

py/asmthumb.c

Lines changed: 81 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ struct _asm_thumb_t {
2020
byte *code_base;
2121
byte dummy_data[8];
2222

23-
int next_label;
2423
int max_num_labels;
2524
int *label_offsets;
2625
int num_locals;
@@ -65,7 +64,6 @@ void asm_thumb_free(asm_thumb_t *as, bool free_code) {
6564
void asm_thumb_start_pass(asm_thumb_t *as, int pass) {
6665
as->pass = pass;
6766
as->code_offset = 0;
68-
as->next_label = 1;
6967
if (pass == ASM_THUMB_PASS_2) {
7068
memset(as->label_offsets, -1, as->max_num_labels * sizeof(int));
7169
}
@@ -212,10 +210,6 @@ void asm_thumb_exit(asm_thumb_t *as) {
212210
asm_thumb_write_op16(as, OP_POP_RLIST_PC(as->push_reglist));
213211
}
214212

215-
int asm_thumb_label_new(asm_thumb_t *as) {
216-
return as->next_label++;
217-
}
218-
219213
void asm_thumb_label_assign(asm_thumb_t *as, int label) {
220214
assert(label < as->max_num_labels);
221215
if (as->pass == ASM_THUMB_PASS_2) {
@@ -234,43 +228,33 @@ static int get_label_dest(asm_thumb_t *as, int label) {
234228
return as->label_offsets[label];
235229
}
236230

237-
// the i8 value will be zero extended into the r32 register!
238-
void asm_thumb_mov_reg_i8(asm_thumb_t *as, uint rlo_dest, int i8) {
231+
#define OP_MOVS_RLO_I8(rlo_dest, i8_src) (0x2000 | ((rlo_dest) << 8) | (i8_src))
232+
233+
// the i8_src value will be zero extended into the r32 register!
234+
void asm_thumb_movs_rlo_i8(asm_thumb_t *as, uint rlo_dest, int i8_src) {
239235
assert(rlo_dest < REG_R8);
240-
// movs rlo_dest, #i8
241-
asm_thumb_write_op16(as, 0x2000 | (rlo_dest << 8) | i8);
236+
// movs rlo_dest, #i8_src
237+
asm_thumb_write_op16(as, OP_MOVS_RLO_I8(rlo_dest, i8_src));
242238
}
243239

244-
// if loading lo half, the i16 value will be zero extended into the r32 register!
245-
void asm_thumb_mov_i16_to_reg(asm_thumb_t *as, int i16, uint reg_dest, bool load_hi_half) {
240+
#define OP_MOVW (0xf240)
241+
#define OP_MOVT (0xf2c0)
242+
243+
// if loading lo half with movw, the i16 value will be zero extended into the r32 register!
244+
static void asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src) {
246245
assert(reg_dest < REG_R15);
247-
uint op;
248-
if (load_hi_half) {
249-
// movt reg_dest, #i16
250-
op = 0xf2c0;
251-
} else {
252-
// movw reg_dest, #i16
253-
op = 0xf240;
254-
}
255-
asm_thumb_write_op32(as, op | ((i16 >> 1) & 0x0400) | ((i16 >> 12) & 0xf), ((i16 << 4) & 0x7000) | (reg_dest << 8) | (i16 & 0xff));
246+
// mov[wt] reg_dest, #i16_src
247+
asm_thumb_write_op32(as, mov_op | ((i16_src >> 1) & 0x0400) | ((i16_src >> 12) & 0xf), ((i16_src << 4) & 0x7000) | (reg_dest << 8) | (i16_src & 0xff));
256248
}
257249

258-
void asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, machine_uint_t i32) {
259-
// movw, movt does it in 8 bytes
260-
// ldr [pc, #], dw does it in 6 bytes, but we might not reach to end of code for dw
261-
262-
asm_thumb_mov_i16_to_reg(as, i32, reg_dest, false);
263-
asm_thumb_mov_i16_to_reg(as, i32 >> 16, reg_dest, true);
250+
// the i16_src value will be zero extended into the r32 register!
251+
void asm_thumb_movw_reg_i16(asm_thumb_t *as, uint reg_dest, int i16_src) {
252+
asm_thumb_mov_reg_i16(as, OP_MOVW, reg_dest, i16_src);
264253
}
265254

266-
void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32) {
267-
if (reg_dest < 8 && UNSIGNED_FIT8(i32)) {
268-
asm_thumb_mov_reg_i8(as, reg_dest, i32);
269-
} else if (UNSIGNED_FIT16(i32)) {
270-
asm_thumb_mov_i16_to_reg(as, i32, reg_dest, false);
271-
} else {
272-
asm_thumb_mov_reg_i32(as, reg_dest, i32);
273-
}
255+
// the i16_src value will be zero extended into the r32 register!
256+
void asm_thumb_movt_reg_i16(asm_thumb_t *as, uint reg_dest, int i16_src) {
257+
asm_thumb_mov_reg_i16(as, OP_MOVT, reg_dest, i16_src);
274258
}
275259

276260
void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src) {
@@ -285,9 +269,69 @@ void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src) {
285269
} else {
286270
op_lo |= 0x80 | (reg_dest - 8);
287271
}
272+
// mov reg_dest, reg_src
288273
asm_thumb_write_op16(as, 0x4600 | op_lo);
289274
}
290275

276+
#define OP_SUBS_RLO_RLO_I3(rlo_dest, rlo_src, i3_src) (0x1e00 | ((i3_src) << 6) | ((rlo_src) << 3) | (rlo_dest))
277+
278+
void asm_thumb_subs_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint rlo_src, int i3_src) {
279+
assert(rlo_dest < REG_R8);
280+
assert(rlo_src < REG_R8);
281+
asm_thumb_write_op16(as, OP_SUBS_RLO_RLO_I3(rlo_dest, rlo_src, i3_src));
282+
}
283+
284+
#define OP_CMP_RLO_I8(rlo, i8) (0x2800 | ((rlo) << 8) | (i8))
285+
286+
void asm_thumb_cmp_rlo_i8(asm_thumb_t *as, uint rlo, int i8) {
287+
assert(rlo < REG_R8);
288+
asm_thumb_write_op16(as, OP_CMP_RLO_I8(rlo, i8));
289+
}
290+
291+
#define OP_BEQ_N(byte_offset) (0xd000 | (((byte_offset) >> 1) & 0x00ff))
292+
#define OP_BNE_N(byte_offset) (0xd100 | (((byte_offset) >> 1) & 0x00ff))
293+
#define OP_BCS_N(byte_offset) (0xd200 | (((byte_offset) >> 1) & 0x00ff))
294+
#define OP_BCC_N(byte_offset) (0xd300 | (((byte_offset) >> 1) & 0x00ff))
295+
#define OP_BMI_N(byte_offset) (0xd400 | (((byte_offset) >> 1) & 0x00ff))
296+
#define OP_BPL_N(byte_offset) (0xd500 | (((byte_offset) >> 1) & 0x00ff))
297+
#define OP_BVS_N(byte_offset) (0xd600 | (((byte_offset) >> 1) & 0x00ff))
298+
#define OP_BVC_N(byte_offset) (0xd700 | (((byte_offset) >> 1) & 0x00ff))
299+
#define OP_BHI_N(byte_offset) (0xd800 | (((byte_offset) >> 1) & 0x00ff))
300+
#define OP_BLS_N(byte_offset) (0xd900 | (((byte_offset) >> 1) & 0x00ff))
301+
#define OP_BGE_N(byte_offset) (0xda00 | (((byte_offset) >> 1) & 0x00ff))
302+
#define OP_BLT_N(byte_offset) (0xdb00 | (((byte_offset) >> 1) & 0x00ff))
303+
#define OP_BGT_N(byte_offset) (0xdc00 | (((byte_offset) >> 1) & 0x00ff))
304+
#define OP_BLE_N(byte_offset) (0xdd00 | (((byte_offset) >> 1) & 0x00ff))
305+
306+
void asm_thumb_bgt_n(asm_thumb_t *as, int label) {
307+
int dest = get_label_dest(as, label);
308+
int rel = dest - as->code_offset;
309+
rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
310+
if (SIGNED_FIT9(rel)) {
311+
asm_thumb_write_op16(as, OP_BGT_N(rel));
312+
} else {
313+
printf("asm_thumb_bgt: branch does not fit in 9 bits\n");
314+
}
315+
}
316+
317+
void asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, machine_uint_t i32) {
318+
// movw, movt does it in 8 bytes
319+
// ldr [pc, #], dw does it in 6 bytes, but we might not reach to end of code for dw
320+
321+
asm_thumb_mov_reg_i16(as, OP_MOVW, reg_dest, i32);
322+
asm_thumb_mov_reg_i16(as, OP_MOVT, reg_dest, i32 >> 16);
323+
}
324+
325+
void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32) {
326+
if (reg_dest < 8 && UNSIGNED_FIT8(i32)) {
327+
asm_thumb_movs_rlo_i8(as, reg_dest, i32);
328+
} else if (UNSIGNED_FIT16(i32)) {
329+
asm_thumb_mov_reg_i16(as, OP_MOVW, reg_dest, i32);
330+
} else {
331+
asm_thumb_mov_reg_i32(as, reg_dest, i32);
332+
}
333+
}
334+
291335
#define OP_STR_TO_SP_OFFSET(rlo_dest, word_offset) (0x9000 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff))
292336
#define OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset) (0x9800 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff))
293337

@@ -351,7 +395,6 @@ void asm_thumb_b_label(asm_thumb_t *as, int label) {
351395
}
352396
}
353397

354-
#define OP_CMP_REG_IMM(rlo, i8) (0x2800 | ((rlo) << 8) | (i8))
355398
// all these bit arithmetics need coverage testing!
356399
#define OP_BEQ(byte_offset) (0xd000 | (((byte_offset) >> 1) & 0x00ff))
357400
#define OP_BEQW_HI(byte_offset) (0xf000 | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 14) & 0x003f))
@@ -361,15 +404,15 @@ void asm_thumb_cmp_reg_bz_label(asm_thumb_t *as, uint rlo, int label) {
361404
assert(rlo < REG_R8);
362405

363406
// compare reg with 0
364-
asm_thumb_write_op16(as, OP_CMP_REG_IMM(rlo, 0));
407+
asm_thumb_write_op16(as, OP_CMP_RLO_I8(rlo, 0));
365408

366409
// branch if equal
367410
int dest = get_label_dest(as, label);
368411
int rel = dest - as->code_offset;
369412
rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
370413
if (dest >= 0 && rel <= -4) {
371414
// is a backwards jump, so we know the size of the jump on the first pass
372-
// calculate rel assuming 12 bit relative jump
415+
// calculate rel assuming 9 bit relative jump
373416
if (SIGNED_FIT9(rel)) {
374417
asm_thumb_write_op16(as, OP_BEQ(rel));
375418
} else {

py/asmthumb.h

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,30 @@ void *asm_thumb_get_code(asm_thumb_t *as);
3838
void asm_thumb_entry(asm_thumb_t *as, int num_locals);
3939
void asm_thumb_exit(asm_thumb_t *as);
4040

41-
int asm_thumb_label_new(asm_thumb_t *as);
4241
void asm_thumb_label_assign(asm_thumb_t *as, int label);
4342

4443
// argument order follows ARM, in general dest is first
44+
// note there is a difference between movw and mov.w, and many others!
4545

46-
void asm_thumb_mov_reg_i8(asm_thumb_t *as, uint rlo_dest, int i8_src);
47-
void asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, machine_uint_t i32_src);
48-
void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32_src);
46+
void asm_thumb_movs_rlo_i8(asm_thumb_t *as, uint rlo_dest, int i8_src);
47+
void asm_thumb_movw_reg_i16(asm_thumb_t *as, uint reg_dest, int i16_src);
48+
void asm_thumb_movt_reg_i16(asm_thumb_t *as, uint reg_dest, int i16_src);
4949
void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src);
50-
void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num_dest, uint rlo_src);
51-
void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num);
52-
void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint reg_dest, int local_num);
50+
void asm_thumb_subs_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint rlo_src, int i3_src);
51+
void asm_thumb_cmp_rlo_i8(asm_thumb_t *as, uint rlo, int i8);
52+
void asm_thumb_bgt_n(asm_thumb_t *as, int label);
5353

54-
void asm_thumb_add_reg_reg_reg(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, uint rlo_src_b);
55-
void asm_thumb_cmp_reg_reg(asm_thumb_t *as, uint rlo_a, uint rlo_b);
56-
void asm_thumb_ite_ge(asm_thumb_t *as);
54+
void asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, machine_uint_t i32_src); // convenience
55+
void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32_src); // convenience
56+
void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num_dest, uint rlo_src); // convenience
57+
void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num); // convenience
58+
void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint reg_dest, int local_num); // convenience
59+
60+
void asm_thumb_add_reg_reg_reg(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, uint rlo_src_b); // convenience ?
61+
void asm_thumb_cmp_reg_reg(asm_thumb_t *as, uint rlo_a, uint rlo_b); // convenience ?
62+
void asm_thumb_ite_ge(asm_thumb_t *as); // convenience ?
63+
64+
void asm_thumb_b_label(asm_thumb_t *as, int label); // convenience ?
65+
void asm_thumb_cmp_reg_bz_label(asm_thumb_t *as, uint rlo, int label); // convenience ?
66+
void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp); // convenience ?
5767

58-
void asm_thumb_b_label(asm_thumb_t *as, int label);
59-
void asm_thumb_cmp_reg_bz_label(asm_thumb_t *as, uint rlo, int label);
60-
void asm_thumb_bl_ind(asm_thumb_t *as, void *fun_ptr, uint fun_id, uint reg_temp);

0 commit comments

Comments
 (0)