diff --git a/arm-asm.c b/arm-asm.c index ce3ac80e..6dc24c04 100644 --- a/arm-asm.c +++ b/arm-asm.c @@ -248,6 +248,14 @@ static void asm_binary_opcode(TCCState *s1, int token) Note: For single data transfer instructions, "0" means immediate. */ #define ENCODE_IMMEDIATE_FLAG (1 << 25) +#define ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER (1 << 4) +#define ENCODE_BARREL_SHIFTER_MODE_LSL (0 << 5) +#define ENCODE_BARREL_SHIFTER_MODE_LSR (1 << 5) +#define ENCODE_BARREL_SHIFTER_MODE_ASR (2 << 5) +#define ENCODE_BARREL_SHIFTER_MODE_ROR (3 << 5) +#define ENCODE_BARREL_SHIFTER_REGISTER(register_index) ((register_index) << 8) +#define ENCODE_BARREL_SHIFTER_IMMEDIATE(value) ((value) << 7) + static void asm_block_data_transfer_opcode(TCCState *s1, int token) { uint32_t opcode; @@ -355,6 +363,52 @@ static void asm_block_data_transfer_opcode(TCCState *s1, int token) } } +static uint32_t asm_encode_rotation(Operand* rotation) +{ + uint64_t amount; + switch (rotation->type) { + case OP_REG32: + tcc_error("cannot rotate immediate value by register"); + return 0; + case OP_IM8: + amount = rotation->e.v; + if (amount >= 0 && amount < 32 && (amount & 1) == 0) + return (amount >> 1) << 8; + else + tcc_error("rotating is only possible by a multiple of 2"); + break; + default: + tcc_error("unknown rotation amount"); + return 0; + } +} + +static uint32_t asm_encode_shift(Operand* shift) +{ + uint64_t amount; + uint32_t operands = 0; + switch (shift->type) { + case OP_REG32: + if (shift->reg == 15) + tcc_error("r15 cannot be used as a shift count"); + else { + operands = ENCODE_BARREL_SHIFTER_SHIFT_BY_REGISTER; + operands |= ENCODE_BARREL_SHIFTER_REGISTER(shift->reg); + } + break; + case OP_IM8: + amount = shift->e.v; + if (amount > 0 && amount < 32) + operands = ENCODE_BARREL_SHIFTER_IMMEDIATE(amount); + else + tcc_error("shift count out of range"); + break; + default: + tcc_error("unknown shift amount"); + } + return operands; +} + static void asm_data_processing_opcode(TCCState *s1, int token) { Operand ops[3]; @@ -486,6 +540,102 @@ static void asm_data_processing_opcode(TCCState *s1, int token) } } +static void asm_shift_opcode(TCCState *s1, int token) +{ + Operand ops[3]; + int nb_ops; + uint32_t opcode = 0xd << 21; // MOV + uint32_t operands = 0; + + for (nb_ops = 0; nb_ops < sizeof(ops)/sizeof(ops[0]); ++nb_ops) { + parse_operand(s1, &ops[nb_ops]); + if (tok != ',') { + ++nb_ops; + break; + } + next(); // skip ',' + } + if (nb_ops < 2) { + expect("at least two operands"); + return; + } + + if (ops[0].type != OP_REG32) + expect("(destination operand) register"); + else + operands |= ENCODE_RD(ops[0].reg); + + if (nb_ops == 2) { + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_rrxseq: + opcode |= ENCODE_SET_CONDITION_CODES; + /* fallthrough */ + case TOK_ASM_rrxeq: + if (ops[1].type == OP_REG32) { + operands |= ops[1].reg; + operands |= ENCODE_BARREL_SHIFTER_MODE_ROR; + asm_emit_opcode(token, opcode | operands); + } else + tcc_error("(first source operand) register"); + return; + default: + memcpy(&ops[2], &ops[1], sizeof(ops[1])); // move ops[2] + memcpy(&ops[1], &ops[0], sizeof(ops[0])); // ops[1] was implicit + nb_ops = 3; + } + } + if (nb_ops != 3) { + expect("two or three operands"); + return; + } + + switch (ops[1].type) { + case OP_REG32: + operands |= ops[1].reg; + break; + case OP_IM8: + operands |= ENCODE_IMMEDIATE_FLAG; + operands |= ops[1].e.v; + break; + } + + if (operands & ENCODE_IMMEDIATE_FLAG) + operands |= asm_encode_rotation(&ops[2]); + else + operands |= asm_encode_shift(&ops[2]); + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_lslseq: + opcode |= ENCODE_SET_CONDITION_CODES; + /* fallthrough */ + case TOK_ASM_lsleq: + operands |= ENCODE_BARREL_SHIFTER_MODE_LSL; + break; + case TOK_ASM_lsrseq: + opcode |= ENCODE_SET_CONDITION_CODES; + /* fallthrough */ + case TOK_ASM_lsreq: + operands |= ENCODE_BARREL_SHIFTER_MODE_LSR; + break; + case TOK_ASM_asrseq: + opcode |= ENCODE_SET_CONDITION_CODES; + /* fallthrough */ + case TOK_ASM_asreq: + operands |= ENCODE_BARREL_SHIFTER_MODE_ASR; + break; + case TOK_ASM_rorseq: + opcode |= ENCODE_SET_CONDITION_CODES; + /* fallthrough */ + case TOK_ASM_roreq: + operands |= ENCODE_BARREL_SHIFTER_MODE_ROR; + break; + default: + expect("shift instruction"); + return; + } + asm_emit_opcode(token, opcode | operands); +} + static void asm_multiplication_opcode(TCCState *s1, int token) { Operand ops[4]; @@ -902,6 +1052,18 @@ ST_FUNC void asm_opcode(TCCState *s1, int token) case TOK_ASM_mvnseq: return asm_data_processing_opcode(s1, token); + case TOK_ASM_lsleq: + case TOK_ASM_lslseq: + case TOK_ASM_lsreq: + case TOK_ASM_lsrseq: + case TOK_ASM_asreq: + case TOK_ASM_asrseq: + case TOK_ASM_roreq: + case TOK_ASM_rorseq: + case TOK_ASM_rrxseq: + case TOK_ASM_rrxeq: + return asm_shift_opcode(s1, token); + case TOK_ASM_muleq: case TOK_ASM_mulseq: case TOK_ASM_mlaeq: diff --git a/arm-tok.h b/arm-tok.h index 2f1798be..a1fb1582 100644 --- a/arm-tok.h +++ b/arm-tok.h @@ -145,3 +145,14 @@ DEF_ASM_CONDED(bics) DEF_ASM_CONDED(mvn) DEF_ASM_CONDED(mvns) + + DEF_ASM_CONDED(lsl) + DEF_ASM_CONDED(lsls) + DEF_ASM_CONDED(lsr) + DEF_ASM_CONDED(lsrs) + DEF_ASM_CONDED(asr) + DEF_ASM_CONDED(asrs) + DEF_ASM_CONDED(ror) + DEF_ASM_CONDED(rors) + DEF_ASM_CONDED(rrx) + DEF_ASM_CONDED(rrxs)