[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Tinycc-devel] [PATCH v2 4/8] arm-asm: Add ldc, ldcl, stc, stcl
From: |
Danny Milosavljevic |
Subject: |
[Tinycc-devel] [PATCH v2 4/8] arm-asm: Add ldc, ldcl, stc, stcl |
Date: |
Fri, 15 Jan 2021 17:26:42 +0100 |
---
arm-asm.c | 178 ++++++++++++++++++++++++++++++++++++-
arm-tok.h | 5 ++
tests/arm-asm-testsuite.sh | 6 ++
3 files changed, 188 insertions(+), 1 deletion(-)
diff --git a/arm-asm.c b/arm-asm.c
index 0e6e503..1f0bac9 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -165,6 +165,10 @@ static void asm_emit_opcode(int token, uint32_t opcode) {
gen_le32((condition_code_of_token(token) << 28) | opcode);
}
+static void asm_emit_unconditional_opcode(uint32_t opcode) {
+ gen_le32(opcode);
+}
+
static void asm_emit_coprocessor_opcode(uint32_t high_nibble, uint8_t
cp_number, uint8_t cp_opcode, uint8_t cp_destination_register, uint8_t
cp_n_operand_register, uint8_t cp_m_operand_register, uint8_t cp_opcode2, int
inter_processor_transfer)
{
uint32_t opcode = 0xe000000;
@@ -182,7 +186,7 @@ static void asm_emit_coprocessor_opcode(uint32_t
high_nibble, uint8_t cp_number,
opcode |= cp_opcode2 << 5;
//assert(cp_m_operand_register < 16);
opcode |= cp_m_operand_register;
- gen_le32((high_nibble << 28) | opcode);
+ asm_emit_unconditional_opcode((high_nibble << 28) | opcode);
}
static void asm_nullary_opcode(int token)
@@ -1259,6 +1263,171 @@ static void asm_single_data_transfer_opcode(TCCState
*s1, int token)
}
}
+static void asm_emit_coprocessor_data_transfer(uint32_t high_nibble, uint8_t
cp_number, uint8_t CRd, const Operand* Rn, const Operand* offset, int
offset_minus, int preincrement, int writeback, int long_transfer, int load) {
+ uint32_t opcode = 0x0;
+ opcode |= 1 << 26; // Load/Store
+ opcode |= 1 << 27; // coprocessor
+
+ if (long_transfer)
+ opcode |= 1 << 22; // long transfer
+
+ if (load)
+ opcode |= 1 << 20; // L
+
+ opcode |= cp_number << 8;
+
+ opcode |= ENCODE_RD(CRd);
+
+ if (Rn->type != OP_REG32) {
+ expect("register");
+ return;
+ }
+ opcode |= ENCODE_RN(Rn->reg);
+ if (preincrement)
+ opcode |= 1 << 24; // add offset before transfer
+
+ if (writeback)
+ opcode |= 1 << 21; // write offset back into register
+
+ if (offset->type == OP_IM8 || offset->type == OP_IM8N || offset->type ==
OP_IM32) {
+ int v = offset->e.v;
+ if (offset_minus)
+ tcc_error("minus before '#' not supported for immediate values");
+ if (offset->type == OP_IM8N || v < 0)
+ v = -v;
+ else
+ opcode |= 1 << 23; // up
+ if (v & 3) {
+ tcc_error("immediate offset must be a multiple of 4");
+ return;
+ }
+ v >>= 2;
+ if (v > 255) {
+ tcc_error("immediate offset must be between -1020 and 1020");
+ return;
+ }
+ opcode |= v;
+ } else if (offset->type == OP_REG32) {
+ if (!offset_minus)
+ opcode |= 1 << 23; // up
+ opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT
immediate */
+ opcode |= offset->reg;
+ tcc_error("Using register offset to register address is not possible
here");
+ return;
+ } else
+ expect("immediate or register");
+
+ asm_emit_unconditional_opcode((high_nibble << 28) | opcode);
+}
+
+// Almost exactly the same as asm_single_data_transfer_opcode.
+// Difference: Offsets are smaller and multiples of 4; no shifts, no STREX,
ENCODE_IMMEDIATE_FLAG is inverted again.
+static void asm_coprocessor_data_transfer_opcode(TCCState *s1, int token)
+{
+ Operand ops[3];
+ uint8_t coprocessor;
+ uint8_t coprocessor_destination_register;
+ int preincrement = 0;
+ int exclam = 0;
+ int closed_bracket = 0;
+ int op2_minus = 0;
+ int long_transfer = 0;
+ // Note: ldc p1, c0, [r4, #4] ; simple offset: r0 = *(int*)(r4+4); r4
unchanged
+ // Note: ldc p2, c0, [r4, #4]! ; pre-indexed: r0 = *(int*)(r4+4); r4 =
r4+4
+ // Note: ldc p3, c0, [r4], #4 ; post-indexed: r0 = *(int*)(r4+0); r4 =
r4+4
+
+ if (tok >= TOK_ASM_p0 && tok <= TOK_ASM_p15) {
+ coprocessor = tok - TOK_ASM_p0;
+ next();
+ } else {
+ expect("'c<number>'");
+ return;
+ }
+
+ if (tok == ',')
+ next();
+ else
+ expect("','");
+
+ if (tok >= TOK_ASM_c0 && tok <= TOK_ASM_c15) {
+ coprocessor_destination_register = tok - TOK_ASM_c0;
+ next();
+ } else {
+ expect("'c<number>'");
+ return;
+ }
+
+ if (tok == ',')
+ next();
+ else
+ expect("','");
+
+ if (tok != '[')
+ expect("'['");
+ else
+ next(); // skip '['
+
+ parse_operand(s1, &ops[1]);
+ if (ops[1].type != OP_REG32) {
+ expect("(first source operand) register");
+ return;
+ }
+ if (tok == ']') {
+ next();
+ closed_bracket = 1;
+ // exclam = 1; // implicit in hardware; don't do it in software
+ }
+ if (tok == ',') {
+ next(); // skip ','
+ if (tok == '-') {
+ op2_minus = 1;
+ next();
+ }
+ parse_operand(s1, &ops[2]);
+ if (ops[2].type == OP_REG32) {
+ if (ops[2].reg == 15) {
+ tcc_error("Using 'pc' for register offset in '%s' is not
implemented by ARM", get_tok_str(token, NULL));
+ return;
+ }
+ }
+ } else {
+ // end of input expression in brackets--assume 0 offset
+ ops[2].type = OP_IM8;
+ ops[2].e.v = 0;
+ preincrement = 1; // add offset before transfer
+ }
+ if (!closed_bracket) {
+ if (tok != ']')
+ expect("']'");
+ else
+ next(); // skip ']'
+ preincrement = 1; // add offset before transfer
+ if (tok == '!') {
+ exclam = 1;
+ next(); // skip '!'
+ }
+ }
+
+ // TODO: Support options.
+
+ switch (ARM_INSTRUCTION_GROUP(token)) {
+ case TOK_ASM_stcleq:
+ long_transfer = 1;
+ /* fallthrough */
+ case TOK_ASM_stceq:
+ asm_emit_coprocessor_data_transfer(condition_code_of_token(token),
coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus,
preincrement, exclam, long_transfer, 0);
+ break;
+ case TOK_ASM_ldcleq:
+ long_transfer = 1;
+ /* fallthrough */
+ case TOK_ASM_ldceq:
+ asm_emit_coprocessor_data_transfer(condition_code_of_token(token),
coprocessor, coprocessor_destination_register, &ops[1], &ops[2], op2_minus,
preincrement, exclam, long_transfer, 1);
+ break;
+ default:
+ expect("coprocessor data transfer instruction");
+ }
+}
+
static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token)
{
Operand ops[3];
@@ -1588,6 +1757,13 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
case TOK_ASM_mcreq:
case TOK_ASM_mrceq:
return asm_coprocessor_opcode(s1, token);
+
+ case TOK_ASM_ldceq:
+ case TOK_ASM_ldcleq:
+ case TOK_ASM_stceq:
+ case TOK_ASM_stcleq:
+ return asm_coprocessor_data_transfer_opcode(s1, token);
+
default:
expect("known instruction");
}
diff --git a/arm-tok.h b/arm-tok.h
index 7f15b73..66f7178 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -155,6 +155,11 @@
DEF_ASM_CONDED(stmib)
DEF_ASM_CONDED(ldmib)
+ DEF_ASM_CONDED(ldc)
+ DEF_ASM_CONDED(ldcl)
+ DEF_ASM_CONDED(stc)
+ DEF_ASM_CONDED(stcl)
+
/* instruction macros */
DEF_ASM_CONDED(push)
diff --git a/tests/arm-asm-testsuite.sh b/tests/arm-asm-testsuite.sh
index cbf4b7c..59e667a 100755
--- a/tests/arm-asm-testsuite.sh
+++ b/tests/arm-asm-testsuite.sh
@@ -95,6 +95,12 @@ do
"p10, #7, r2, c0, c1, #4" \
"#4" \
"#-4" \
+ "p5, c2, [r3]" \
+ "p5, c3, [r4]" \
+ "p5, c2, [r3, #4]" \
+ "p5, c2, [r3, #-4]" \
+ "p5, c2, [r3, #0x45]" \
+ "p5, c2, [r3, #-0x45]" \
""
do
#echo ".syntax unified" > a.s
- [Tinycc-devel] [PATCH v2 0/8] Implement ARM VFP in ARM inline assembler, Danny Milosavljevic, 2021/01/15
- [Tinycc-devel] [PATCH v2 2/8] arm-asm: Add cdp2, Danny Milosavljevic, 2021/01/15
- [Tinycc-devel] [PATCH v2 3/8] arm-asm: Add mcr, mrc, Danny Milosavljevic, 2021/01/15
- [Tinycc-devel] [PATCH v2 1/8] arm-asm: Add cdp, Danny Milosavljevic, 2021/01/15
- [Tinycc-devel] [PATCH v2 4/8] arm-asm: Add ldc, ldcl, stc, stcl,
Danny Milosavljevic <=
- [Tinycc-devel] [PATCH v2 7/8] arm-asm: Add vmla, vmls, vnmls, vnmla, vmul, vnmul, vadd, vsub, vdiv, Danny Milosavljevic, 2021/01/15
- [Tinycc-devel] [PATCH v2 8/8] arm-asm: Add vneg, vabs, vsqrt, vcmp, vcmpe, Danny Milosavljevic, 2021/01/15
[Tinycc-devel] [PATCH v2 5/8] arm-asm: Add ldc2, ldc2l, stc2, stc2l, Danny Milosavljevic, 2021/01/15
[Tinycc-devel] [PATCH v2 6/8] arm-asm: Add vldr, vstr, Danny Milosavljevic, 2021/01/15
[Tinycc-devel] Assembly instructions like "foo.bar", Danny Milosavljevic, 2021/01/21