From 007839597f1a10d1bcef1f9c0fb5df3ff24b6a0c Mon Sep 17 00:00:00 2001 From: Danny Milosavljevic Date: Tue, 12 Jan 2021 15:26:52 +0100 Subject: [PATCH] arm-asm: Implement ldr and str with shifted register offset Factor out asm_parse_optional_shift --- arm-asm.c | 102 +++++++++++++++++++++++-------------- tests/arm-asm-testsuite.sh | 11 ++++ 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/arm-asm.c b/arm-asm.c index e33422d1..7a3d2e22 100644 --- a/arm-asm.c +++ b/arm-asm.c @@ -433,6 +433,50 @@ static void asm_block_data_transfer_opcode(TCCState *s1, int token) } } +/* Parses shift directive and returns the parts that would have to be set in the opcode because of it. + Does not encode the actual shift amount. + It's not an error if there is no shift directive. + + NB_SHIFT: will be set to 1 iff SHIFT is filled. Note that for rrx, there's no need to fill SHIFT. + SHIFT: will be filled in with the shift operand to use, if any. */ +static uint32_t asm_parse_optional_shift(TCCState* s1, int* nb_shift, Operand* shift) +{ + uint32_t opcode = 0; + *nb_shift = 0; + switch (tok) { + case TOK_ASM_asl: + case TOK_ASM_lsl: + case TOK_ASM_asr: + case TOK_ASM_lsr: + case TOK_ASM_ror: + switch (tok) { + case TOK_ASM_asl: + /* fallthrough */ + case TOK_ASM_lsl: + opcode = ENCODE_BARREL_SHIFTER_MODE_LSL; + break; + case TOK_ASM_asr: + opcode = ENCODE_BARREL_SHIFTER_MODE_ASR; + break; + case TOK_ASM_lsr: + opcode = ENCODE_BARREL_SHIFTER_MODE_LSR; + break; + case TOK_ASM_ror: + opcode = ENCODE_BARREL_SHIFTER_MODE_ROR; + break; + } + next(); + parse_operand(s1, shift); + *nb_shift = 1; + break; + case TOK_ASM_rrx: + next(); + opcode = ENCODE_BARREL_SHIFTER_MODE_ROR; + break; + } + return opcode; +} + static uint32_t asm_encode_shift(Operand* shift) { uint64_t amount; @@ -482,37 +526,7 @@ static void asm_data_processing_opcode(TCCState *s1, int token) } if (tok == ',') next(); - switch (tok) { - case TOK_ASM_asl: - case TOK_ASM_lsl: - case TOK_ASM_asr: - case TOK_ASM_lsr: - case TOK_ASM_ror: - switch (tok) { - case TOK_ASM_asl: - /* fallthrough */ - case TOK_ASM_lsl: - operands |= ENCODE_BARREL_SHIFTER_MODE_LSL; - break; - case TOK_ASM_asr: - operands |= ENCODE_BARREL_SHIFTER_MODE_ASR; - break; - case TOK_ASM_lsr: - operands |= ENCODE_BARREL_SHIFTER_MODE_LSR; - break; - case TOK_ASM_ror: - operands |= ENCODE_BARREL_SHIFTER_MODE_ROR; - break; - } - next(); - parse_operand(s1, &shift); - nb_shift = 1; - break; - case TOK_ASM_rrx: - next(); - operands |= ENCODE_BARREL_SHIFTER_MODE_ROR; - break; - } + operands |= asm_parse_optional_shift(s1, &nb_shift, &shift); if (nb_ops < 2) expect("at least two operands"); else if (nb_ops == 2) { @@ -940,6 +954,8 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) { Operand ops[3]; Operand strex_operand; + Operand shift; + int nb_shift = 0; int exclam = 0; int closed_bracket = 0; int op2_minus = 0; @@ -999,6 +1015,14 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) next(); } parse_operand(s1, &ops[2]); + if (ops[2].type == OP_REG32) { + if (tok == ',') { + next(); + opcode |= asm_parse_optional_shift(s1, &nb_shift, &shift); + if (opcode == 0) + expect("shift directive, or no comma"); + } + } } else { // end of input expression in brackets--assume 0 offset ops[2].type = OP_IM8; @@ -1061,6 +1085,8 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) /* fallthrough */ case TOK_ASM_streq: opcode |= 1 << 26; // Load/Store + if (nb_shift) + opcode |= asm_encode_shift(&shift); asm_emit_opcode(token, opcode); break; case TOK_ASM_ldrbeq: @@ -1069,14 +1095,16 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) case TOK_ASM_ldreq: opcode |= 1 << 20; // L opcode |= 1 << 26; // Load/Store + if (nb_shift) + opcode |= asm_encode_shift(&shift); asm_emit_opcode(token, opcode); break; case TOK_ASM_strexbeq: opcode |= 1 << 22; // B /* fallthrough */ case TOK_ASM_strexeq: - if (opcode & 0xFFF) { - tcc_error("offset not allowed with 'strex'"); + if ((opcode & 0xFFF) || nb_shift) { + tcc_error("neither offset nor shift allowed with 'strex'"); return; } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate tcc_error("offset not allowed with 'strex'"); @@ -1087,7 +1115,7 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) return; } - opcode |= 0xf90; + opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL opcode |= strex_operand.reg; asm_emit_opcode(token, opcode); break; @@ -1095,8 +1123,8 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) opcode |= 1 << 22; // B /* fallthrough */ case TOK_ASM_ldrexeq: - if (opcode & 0xFFF) { - tcc_error("offset not allowed with 'ldrex'"); + if ((opcode & 0xFFF) || nb_shift) { + tcc_error("neither offset nor shift allowed with 'ldrex'"); return; } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate tcc_error("offset not allowed with 'ldrex'"); @@ -1108,7 +1136,7 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) } opcode |= 1 << 20; // L opcode |= 0x00f; - opcode |= 0xf90; + opcode |= 0xf90; // Used to mean: barrel shifter is enabled, barrel shift register is r15, mode is LSL asm_emit_opcode(token, opcode); break; default: diff --git a/tests/arm-asm-testsuite.sh b/tests/arm-asm-testsuite.sh index e997e07c..0180628e 100755 --- a/tests/arm-asm-testsuite.sh +++ b/tests/arm-asm-testsuite.sh @@ -61,6 +61,17 @@ do "r2, [r3, -r4]" \ "r2, [r3, -r4]!" \ "r2, [r3], r4" \ + "r2, [r3], -r4" \ + "r2, [r3]" \ + "r2, r3, [r4, lsl# 2]" \ + "r2, [r3, r4, lsr# 1]" \ + "r2, [r3, r4, lsr# 2]!" \ + "r2, [r3, -r4, ror# 3]" \ + "r2, [r3, -r4, lsl# 1]!" \ + "r2, [r3], r4, lsl# 3" \ + "r2, [r3], -r4, asr# 31" \ + "r2, [r3], -r4, asl# 1" \ + "r2, [r3], -r4, rrx" \ "r2, [r3]" \ "r2, r3, [r4]" \ "r2, [r3, #4]" \