[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Tinycc-devel] [PATCH] arm-asm: Add ldrh, ldrsh, ldrsb, strh
From: |
Danny Milosavljevic |
Subject: |
[Tinycc-devel] [PATCH] arm-asm: Add ldrh, ldrsh, ldrsb, strh |
Date: |
Thu, 7 Jan 2021 17:11:10 +0100 |
---
arm-asm.c | 142 +++++++++++++++++++++++++++++++++++++
arm-tok.h | 4 ++
tests/arm-asm-testsuite.sh | 2 +
3 files changed, 148 insertions(+)
diff --git a/arm-asm.c b/arm-asm.c
index 0f7340c..f753849 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -1116,6 +1116,142 @@ static void asm_single_data_transfer_opcode(TCCState
*s1, int token)
}
}
+static void asm_misc_single_data_transfer_opcode(TCCState *s1, int token)
+{
+ Operand ops[3];
+ int exclam = 0;
+ int closed_bracket = 0;
+ int op2_minus = 0;
+ uint32_t opcode = (1 << 7) | (1 << 4);
+
+ /* Note:
+ The argument syntax is exactly the same as in
arm_single_data_transfer_opcode, except that there's no STREX argument form.
+ The main difference between this function and
asm_misc_single_data_transfer_opcode is that the immediate values here must be
smaller.
+ Also, the combination (P=0, W=1) is unpredictable here.
+ The immediate flag has moved to bit index 22--and its meaning has
flipped.
+ The immediate value itself has been split into two parts: one at bits
11...8, one at bits 3...0
+ bit 26 (Load/Store instruction) is unset here.
+ bits 7 and 4 are set here. */
+
+ // Here: 0 0 0 P U I W L << 20
+ // [compare single data transfer: 0 1 I P U B W L << 20]
+
+ parse_operand(s1, &ops[0]);
+ if (ops[0].type == OP_REG32)
+ opcode |= ENCODE_RD(ops[0].reg);
+ else {
+ expect("(destination operand) register");
+ return;
+ }
+ if (tok != ',')
+ expect("at least two arguments");
+ else
+ next(); // skip ','
+
+ if (tok != '[')
+ expect("'['");
+ else
+ next(); // skip '['
+
+ parse_operand(s1, &ops[1]);
+ if (ops[1].type == OP_REG32)
+ opcode |= ENCODE_RN(ops[1].reg);
+ else {
+ 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]);
+ } else {
+ // end of input expression in brackets--assume 0 offset
+ ops[2].type = OP_IM8;
+ ops[2].e.v = 0;
+ opcode |= 1 << 24; // add offset before transfer
+ }
+ if (!closed_bracket) {
+ if (tok != ']')
+ expect("']'");
+ else
+ next(); // skip ']'
+ opcode |= 1 << 24; // add offset before transfer
+ if (tok == '!') {
+ exclam = 1;
+ next(); // skip '!'
+ }
+ }
+
+ if (exclam) {
+ if ((opcode & (1 << 24)) == 0) {
+ tcc_error("result would be unpredicable");
+ return;
+ }
+ opcode |= 1 << 21; // write offset back into register
+ }
+
+ if (ops[2].type == OP_IM32 || ops[2].type == OP_IM8 || ops[2].type ==
OP_IM8N) {
+ int v = ops[2].e.v;
+ if (op2_minus)
+ tcc_error("minus before '#' not supported for immediate values");
+ if (v >= 0) {
+ opcode |= 1 << 23; // up
+ if (v >= 0x100)
+ tcc_error("offset out of range for '%s'", get_tok_str(token,
NULL));
+ else {
+ // bits 11...8: immediate hi nibble
+ // bits 3...0: immediate lo nibble
+ opcode |= (v & 0xF0) << 4;
+ opcode |= v & 0xF;
+ }
+ } else { // down
+ if (v <= -0x100)
+ tcc_error("offset out of range for '%s'", get_tok_str(token,
NULL));
+ else {
+ v = -v;
+ // bits 11...8: immediate hi nibble
+ // bits 3...0: immediate lo nibble
+ opcode |= (v & 0xF0) << 4;
+ opcode |= v & 0xF;
+ }
+ }
+ opcode |= 1 << 22; // not ENCODE_IMMEDIATE_FLAG;
+ } else if (ops[2].type == OP_REG32) {
+ if (!op2_minus)
+ opcode |= 1 << 23; // up
+ opcode |= ops[2].reg;
+ } else
+ expect("register");
+
+ switch (ARM_INSTRUCTION_GROUP(token)) {
+ case TOK_ASM_ldrsheq:
+ opcode |= 1 << 5; // halfword, not byte
+ /* fallthrough */
+ case TOK_ASM_ldrsbeq:
+ opcode |= 1 << 6; // sign extend
+ opcode |= 1 << 20; // L
+ asm_emit_opcode(token, opcode);
+ break;
+ case TOK_ASM_ldrheq:
+ opcode |= 1 << 5; // halfword, not byte
+ opcode |= 1 << 20; // L
+ asm_emit_opcode(token, opcode);
+ break;
+ case TOK_ASM_strheq:
+ opcode |= 1 << 5; // halfword, not byte
+ asm_emit_opcode(token, opcode);
+ break;
+ }
+}
+
/* Note: almost dupe of encbranch in arm-gen.c */
static uint32_t encbranchoffset(int pos, int addr, int fail)
{
@@ -1232,6 +1368,12 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
case TOK_ASM_strexbeq:
return asm_single_data_transfer_opcode(s1, token);
+ case TOK_ASM_ldrheq:
+ case TOK_ASM_ldrsheq:
+ case TOK_ASM_ldrsbeq:
+ case TOK_ASM_strheq:
+ return asm_misc_single_data_transfer_opcode(s1, token);
+
case TOK_ASM_andeq:
case TOK_ASM_eoreq:
case TOK_ASM_subeq:
diff --git a/arm-tok.h b/arm-tok.h
index be927cc..f73add1 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -97,6 +97,10 @@
DEF_ASM_CONDED(ldrexb)
DEF_ASM_CONDED(strex)
DEF_ASM_CONDED(strexb)
+ DEF_ASM_CONDED(ldrh)
+ DEF_ASM_CONDED(ldrsh)
+ DEF_ASM_CONDED(ldrsb)
+ DEF_ASM_CONDED(strh)
DEF_ASM_CONDED(stmda)
DEF_ASM_CONDED(ldmda)
diff --git a/tests/arm-asm-testsuite.sh b/tests/arm-asm-testsuite.sh
index 273111b..e997e07 100755
--- a/tests/arm-asm-testsuite.sh
+++ b/tests/arm-asm-testsuite.sh
@@ -65,6 +65,8 @@ do
"r2, r3, [r4]" \
"r2, [r3, #4]" \
"r2, [r3, #-4]" \
+ "r2, [r3, #0x45]" \
+ "r2, [r3, #-0x45]" \
"r2, r3, #4" \
"r2, r3, #-4" \
"r2, #4" \
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Tinycc-devel] [PATCH] arm-asm: Add ldrh, ldrsh, ldrsb, strh,
Danny Milosavljevic <=