From 82663d33bb9a366e007c118d9249fc096c96909f Mon Sep 17 00:00:00 2001 From: Danny Milosavljevic Date: Sat, 26 Dec 2020 16:55:12 +0100 Subject: [PATCH] arm-asm: Add mul, mla, smull, umull, smlal, umlal --- arm-asm.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ arm-tok.h | 15 +++++ 2 files changed, 190 insertions(+) diff --git a/arm-asm.c b/arm-asm.c index c2598a83..792b9144 100644 --- a/arm-asm.c +++ b/arm-asm.c @@ -286,6 +286,165 @@ static void asm_block_data_transfer_opcode(TCCState *s1, int token) } } +static void asm_multiplication_opcode(TCCState *s1, int token) +{ + Operand ops[4]; + int nb_ops = 0; + uint32_t opcode = 0x90; + + 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"); + else if (nb_ops == 2) { + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_mulseq: + case TOK_ASM_muleq: + memcpy(&ops[2], &ops[0], sizeof(ops[1])); // ARM is actually like this! + break; + 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; + } + + // multiply (special case): + // operands: + // Rd: bits 19...16 + // Rm: bits 3...0 + // Rs: bits 11...8 + // Rn: bits 15...12 + + if (ops[0].type == OP_REG32) + opcode |= ops[0].reg << 16; + else + expect("(destination operand) register"); + if (ops[1].type == OP_REG32) + opcode |= ops[1].reg; + else + expect("(first source operand) register"); + if (ops[2].type == OP_REG32) + opcode |= ops[2].reg << 8; + else + expect("(second source operand) register"); + if (nb_ops > 3) { + if (ops[3].type == OP_REG32) + opcode |= ops[3].reg << 12; + else + expect("(third source operand) register"); + } + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_mulseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_muleq: + if (nb_ops != 3) + expect("three operands"); + else { + asm_emit_opcode(token, opcode); + } + break; + case TOK_ASM_mlaseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_mlaeq: + if (nb_ops != 4) + expect("four operands"); + else { + opcode |= 1 << 21; // Accumulate + asm_emit_opcode(token, opcode); + } + break; + default: + expect("known multiplication instruction"); + } +} + +static void asm_long_multiplication_opcode(TCCState *s1, int token) +{ + Operand ops[4]; + int nb_ops = 0; + uint32_t opcode = 0x90 | (1 << 23); + + 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 != 4) { + expect("four operands"); + return; + } + + // long multiply (special case): + // operands: + // RdLo: bits 15...12 + // RdHi: bits 19...16 + // Rs: bits 11...8 + // Rm: bits 3...0 + + if (ops[0].type == OP_REG32) + opcode |= ops[0].reg << 12; + else + expect("(destination lo accumulator) register"); + if (ops[1].type == OP_REG32) + opcode |= ops[1].reg << 16; + else + expect("(destination hi accumulator) register"); + if (ops[2].type == OP_REG32) + opcode |= ops[2].reg; + else + expect("(first source operand) register"); + if (ops[3].type == OP_REG32) + opcode |= ops[3].reg << 8; + else + expect("(second source operand) register"); + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_smullseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_smulleq: + opcode |= 1 << 22; // signed + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_umullseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_umulleq: + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_smlalseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_smlaleq: + opcode |= 1 << 22; // signed + opcode |= 1 << 21; // Accumulate + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_umlalseq: + opcode |= 1 << 20; // Status + /* fallthrough */ + case TOK_ASM_umlaleq: + opcode |= 1 << 21; // Accumulate + asm_emit_opcode(token, opcode); + break; + default: + expect("known long multiplication instruction"); + } +} + ST_FUNC void asm_opcode(TCCState *s1, int token) { while (token == TOK_LINEFEED) { @@ -315,6 +474,22 @@ ST_FUNC void asm_opcode(TCCState *s1, int token) case TOK_ASM_uxtbeq: case TOK_ASM_uxtheq: return asm_binary_opcode(s1, token); + + case TOK_ASM_muleq: + case TOK_ASM_mulseq: + case TOK_ASM_mlaeq: + case TOK_ASM_mlaseq: + return asm_multiplication_opcode(s1, token); + + case TOK_ASM_smulleq: + case TOK_ASM_smullseq: + case TOK_ASM_umulleq: + case TOK_ASM_umullseq: + case TOK_ASM_smlaleq: + case TOK_ASM_smlalseq: + case TOK_ASM_umlaleq: + case TOK_ASM_umlalseq: + return asm_long_multiplication_opcode(s1, token); default: expect("known instruction"); } diff --git a/arm-tok.h b/arm-tok.h index 06cee7a2..5106250e 100644 --- a/arm-tok.h +++ b/arm-tok.h @@ -66,6 +66,21 @@ DEF_ASM_CONDED(uxtb) DEF_ASM_CONDED(uxth) + /* multiplication */ + + DEF_ASM_CONDED(mul) + DEF_ASM_CONDED(muls) + DEF_ASM_CONDED(mla) + DEF_ASM_CONDED(mlas) + DEF_ASM_CONDED(smull) + DEF_ASM_CONDED(smulls) + DEF_ASM_CONDED(umull) + DEF_ASM_CONDED(umulls) + DEF_ASM_CONDED(smlal) + DEF_ASM_CONDED(smlals) + DEF_ASM_CONDED(umlal) + DEF_ASM_CONDED(umlals) + /* instruction macros */ DEF_ASM_CONDED(push)