[PATCH 00/16] Add ARM inline assembler

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH 00/16] Add ARM inline assembler

Danny Milosavljevic
This patchset adds an ARM inline assembler.

Patch 1 just exports g, gen_le16, gen_le32.  It seems that this was an
oversight since at least gen_le16 is already called by tccasm.c and thus
needs the prototype for gen_le16.

Patch 2 adds tokens for the ARM registers.
Patch 3 updates the copyright header.
Patch 4 removes asm_error.
Patch 5 adds the first instruction, a nullary instruction (nop).  It also
sets up some infrastructure to be able to easily generate instruction tokens
for all the condition code variants of each instruction.
Patch 6 adds some more nullary instructions: wfe and wfi.
Patch 7 adds parse_operand and Operand.  I've had to change tccpp.c slightly
in order to let the '#' through since ARM assembly usually needs the '#' for
constants.

Example:

   mov r3, #5

Here, "5" refers to the constant 5--it's not a comment.

In any case, I also support '$'--but that's not really common on ARM.

Patch 8 adds the first block data transfer instructions: push and pop.

tcctok.h also has push and pop, so I make sure not to define the token twice.

Patch 9 adds the first unary instruction: swi.
Patch 10 adds these binary instructions: clz, sxtb, sxth, uxtb, uxth.
Patch 11 adds the multiplication instructions.
Patch 12 adds some more block data transfer instructions: stmda, ldmda, stm,
ldm, stmia, ldmia,  stmdb, ldmdb, stmib, ldmib.
Patch 13 adds some single data transfer instructions: ldr, ldrb, str, strb.
Patch 14 adds some data processing instructions: and, eor, sub, rsb, add, adc,
sbc, rsc, tst, teq, cmp, cmn, orr, mov, bic, mvn.

Patch 15 adds some branch instructions: b, bl, bx, blx.

I copied encbranchoffset from arm-gen.c and adapted it slightly.

Patch 16 optimizes the memory allocation of gen_le32.

Danny Milosavljevic (16):
  arm-asm: Publish g, gen_le16, gen_le32 in tcc.h
  arm-asm: Implement asm_parse_regvar and asm_clobber
  arm-asm: Update copyright header
  arm-asm: Remove asm_error
  arm-asm: Add nop
  arm-asm: Add wfe, wfi
  arm-asm: Add parse_operand, Operand
  arm-asm: Add push, pop
  arm-asm: Add swi
  arm-asm: Add clz, sxtb, sxth, uxtb, uxth
  arm-asm: Add mul, mla, smull, umull, smlal, umlal
  arm-asm: Add stmda, ldmda, stm, ldm, stmia, ldmia, stmdb, ldmdb,
    stmib, ldmib
  arm-asm: Add ldr, ldrb, str, strb
  arm-asm: Add and, eor, sub, rsb, add, adc, sbc, rsc, tst, teq, cmp,
    cmn, orr, mov, bic, mvn
  arm-asm: Add b, bl, bx, blx
  arm-asm: Optimize gen_le32

 Makefile  |   2 +-
 arm-asm.c | 916 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 arm-tok.h | 147 +++++++++
 tcc.h     |   4 +-
 tccpp.c   |  12 +-
 tcctok.h  |   5 +-
 6 files changed, 1069 insertions(+), 17 deletions(-)
 create mode 100644 arm-tok.h


_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 01/16] arm-asm: Publish g, gen_le16, gen_le32 in tcc.h

Danny Milosavljevic
---
 tcc.h | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tcc.h b/tcc.h
index ce6407e..241be12 100644
--- a/tcc.h
+++ b/tcc.h
@@ -1675,10 +1675,12 @@ static inline void add64le(unsigned char *p, int64_t x) {
 }
 
 /* ------------ i386-gen.c ------------ */
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
+#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 || defined TCC_TARGET_ARM
 ST_FUNC void g(int c);
 ST_FUNC void gen_le16(int c);
 ST_FUNC void gen_le32(int c);
+#endif
+#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
 ST_FUNC void gen_addr32(int r, Sym *sym, int c);
 ST_FUNC void gen_addrpc32(int r, Sym *sym, int c);
 ST_FUNC void gen_cvt_csti(int t);

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 02/16] arm-asm: Implement asm_parse_regvar and asm_clobber

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 Makefile  |  2 +-
 arm-asm.c | 35 ++++++++++++++++++++++++++++++++---
 arm-tok.h | 29 +++++++++++++++++++++++++++++
 tcctok.h  |  3 +++
 4 files changed, 65 insertions(+), 4 deletions(-)
 create mode 100644 arm-tok.h

diff --git a/Makefile b/Makefile
index d41b81a..fcdc550 100644
--- a/Makefile
+++ b/Makefile
@@ -179,7 +179,7 @@ i386-win32_FILES = $(i386_FILES) tccpe.c
 x86_64_FILES = $(CORE_FILES) x86_64-gen.c x86_64-link.c i386-asm.c x86_64-asm.h
 x86_64-win32_FILES = $(x86_64_FILES) tccpe.c
 x86_64-osx_FILES = $(x86_64_FILES) tccmacho.c
-arm_FILES = $(CORE_FILES) arm-gen.c arm-link.c arm-asm.c
+arm_FILES = $(CORE_FILES) arm-gen.c arm-link.c arm-asm.c arm-tok.h
 arm-wince_FILES = $(arm_FILES) tccpe.c
 arm-eabihf_FILES = $(arm_FILES)
 arm-fpa_FILES     = $(arm_FILES)
diff --git a/arm-asm.c b/arm-asm.c
index f09f003..6015d55 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -81,13 +81,42 @@ ST_FUNC void asm_compute_constraints(ASMOperand *operands,
 
 ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
 {
-    asm_error();
+    int reg;
+    TokenSym *ts;
+
+    if (!strcmp(str, "memory") ||
+        !strcmp(str, "cc") ||
+        !strcmp(str, "flags"))
+        return;
+    ts = tok_alloc(str, strlen(str));
+    reg = asm_parse_regvar(ts->tok);
+    if (reg == -1) {
+        tcc_error("invalid clobber register '%s'", str);
+    }
+    clobber_regs[reg] = 1;
 }
 
+/* If T refers to a register then return the register number and type.
+   Otherwise return -1.  */
 ST_FUNC int asm_parse_regvar (int t)
 {
-    asm_error();
-    return -1;
+    if (t >= TOK_ASM_r0 && t <= TOK_ASM_pc) { /* register name */
+        switch (t) {
+            case TOK_ASM_fp:
+                return TOK_ASM_r11 - TOK_ASM_r0;
+            case TOK_ASM_ip:
+                return TOK_ASM_r12 - TOK_ASM_r0;
+            case TOK_ASM_sp:
+                return TOK_ASM_r13 - TOK_ASM_r0;
+            case TOK_ASM_lr:
+                return TOK_ASM_r14 - TOK_ASM_r0;
+            case TOK_ASM_pc:
+                return TOK_ASM_r15 - TOK_ASM_r0;
+            default:
+                return t - TOK_ASM_r0;
+        }
+    } else
+        return -1;
 }
 
 /*************************************************************/
diff --git a/arm-tok.h b/arm-tok.h
new file mode 100644
index 0000000..5523299
--- /dev/null
+++ b/arm-tok.h
@@ -0,0 +1,29 @@
+/* ------------------------------------------------------------------ */
+/* WARNING: relative order of tokens is important.                    */
+
+/* register */
+
+ DEF_ASM(r0)
+ DEF_ASM(r1)
+ DEF_ASM(r2)
+ DEF_ASM(r3)
+ DEF_ASM(r4)
+ DEF_ASM(r5)
+ DEF_ASM(r6)
+ DEF_ASM(r7)
+ DEF_ASM(r8)
+ DEF_ASM(r9)
+ DEF_ASM(r10)
+ DEF_ASM(r11) /* fp */
+ DEF_ASM(r12) /* ip[c] */
+ DEF_ASM(r13) /* sp */
+ DEF_ASM(r14) /* lr */
+ DEF_ASM(r15) /* pc */
+
+/* register macros */
+
+ DEF_ASM(fp) /* alias for r11 */
+ DEF_ASM(ip) /* alias for r12 */
+ DEF_ASM(sp) /* alias for r13 */
+ DEF_ASM(lr) /* alias for r14 */
+ DEF_ASM(pc) /* alias for r15 */
diff --git a/tcctok.h b/tcctok.h
index af8aa9b..aeac1cd 100644
--- a/tcctok.h
+++ b/tcctok.h
@@ -370,3 +370,6 @@
 #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
 #include "i386-tok.h"
 #endif
+#if defined TCC_TARGET_ARM
+#include "arm-tok.h"
+#endif

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 03/16] arm-asm: Update copyright header

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/arm-asm.c b/arm-asm.c
index 6015d55..812dad6 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -1,7 +1,22 @@
-/*************************************************************/
 /*
- *  ARM dummy assembler for TCC
+ *  ARM specific functions for TCC assembler
+ *
+ *  Copyright (c) 2001, 2002 Fabrice Bellard
+ *  Copyright (c) 2020 Danny Milosavljevic
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #ifdef TARGET_DEFS_ONLY

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 04/16] arm-asm: Remove asm_error

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/arm-asm.c b/arm-asm.c
index 812dad6..a15cd75 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -34,11 +34,6 @@ ST_FUNC void gen_le32(int c);
 #define USING_GLOBALS
 #include "tcc.h"
 
-static void asm_error(void)
-{
-    tcc_error("ARM asm not implemented.");
-}
-
 /* XXX: make it faster ? */
 ST_FUNC void g(int c)
 {
@@ -71,12 +66,12 @@ ST_FUNC void gen_expr32(ExprValue *pe)
 
 ST_FUNC void asm_opcode(TCCState *s1, int opcode)
 {
-    asm_error();
+    tcc_error("internal error: asm_opcode not implemented");
 }
 
 ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier)
 {
-    asm_error();
+    tcc_error("internal error: subst_asm_operand not implemented");
 }
 
 /* generate prolog and epilog code for asm statement */

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 05/16] arm-asm: Add nop

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
 arm-tok.h | 25 +++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/arm-asm.c b/arm-asm.c
index a15cd75..d4d4930 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -31,6 +31,7 @@ ST_FUNC void gen_le32(int c);
 /*************************************************************/
 #else
 /*************************************************************/
+
 #define USING_GLOBALS
 #include "tcc.h"
 
@@ -64,9 +65,48 @@ ST_FUNC void gen_expr32(ExprValue *pe)
     gen_le32(pe->v);
 }
 
-ST_FUNC void asm_opcode(TCCState *s1, int opcode)
+static uint32_t condition_code_of_token(int token) {
+    if (token < TOK_ASM_nopeq) {
+        expect("instruction");
+        return 0;
+    } else
+        return (token - TOK_ASM_nopeq) & 15;
+}
+
+static void asm_emit_opcode(int token, uint32_t opcode) {
+    gen_le32((condition_code_of_token(token) << 28) | opcode);
+}
+
+static void asm_nullary_opcode(int token)
+{
+    switch (ARM_INSTRUCTION_GROUP(token)) {
+    case TOK_ASM_nopeq:
+        asm_emit_opcode(token, 0xd << 21); // mov r0, r0
+        break;
+    default:
+        expect("nullary instruction");
+    }
+}
+
+ST_FUNC void asm_opcode(TCCState *s1, int token)
 {
-    tcc_error("internal error: asm_opcode not implemented");
+    while (token == TOK_LINEFEED) {
+        next();
+        token = tok;
+    }
+    if (token == TOK_EOF)
+        return;
+    if (token < TOK_ASM_nopeq) {
+        expect("instruction");
+        return;
+    }
+
+    switch (ARM_INSTRUCTION_GROUP(token)) {
+    case TOK_ASM_nopeq:
+        return asm_nullary_opcode(token);
+    default:
+        expect("known instruction");
+    }
 }
 
 ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier)
diff --git a/arm-tok.h b/arm-tok.h
index 5523299..bcbabe3 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -27,3 +27,28 @@
  DEF_ASM(sp) /* alias for r13 */
  DEF_ASM(lr) /* alias for r14 */
  DEF_ASM(pc) /* alias for r15 */
+
+#define ARM_INSTRUCTION_GROUP(tok) ((((tok) - TOK_ASM_nopeq) & 0xFFFFFFF0) + TOK_ASM_nopeq)
+
+/* Note: condition code is 4 bits */
+#define DEF_ASM_CONDED(x) \
+  DEF(TOK_ASM_ ## x ## eq, #x "eq") \
+  DEF(TOK_ASM_ ## x ## ne, #x "ne") \
+  DEF(TOK_ASM_ ## x ## cs, #x "cs") \
+  DEF(TOK_ASM_ ## x ## cc, #x "cc") \
+  DEF(TOK_ASM_ ## x ## mi, #x "mi") \
+  DEF(TOK_ASM_ ## x ## pl, #x "pl") \
+  DEF(TOK_ASM_ ## x ## vs, #x "vs") \
+  DEF(TOK_ASM_ ## x ## vc, #x "vc") \
+  DEF(TOK_ASM_ ## x ## hi, #x "hi") \
+  DEF(TOK_ASM_ ## x ## ls, #x "ls") \
+  DEF(TOK_ASM_ ## x ## ge, #x "ge") \
+  DEF(TOK_ASM_ ## x ## lt, #x "lt") \
+  DEF(TOK_ASM_ ## x ## gt, #x "gt") \
+  DEF(TOK_ASM_ ## x ## le, #x "le") \
+  DEF(TOK_ASM_ ## x, #x) \
+  DEF(TOK_ASM_ ## x ## rsvd, #x "rsvd")
+
+/* Note: add new tokens after nop (MUST always use DEF_ASM_CONDED) */
+
+ DEF_ASM_CONDED(nop)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 06/16] arm-asm: Add wfe, wfi

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 7 +++++++
 arm-tok.h | 2 ++
 2 files changed, 9 insertions(+)

diff --git a/arm-asm.c b/arm-asm.c
index d4d4930..4c663c3 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -83,6 +83,11 @@ static void asm_nullary_opcode(int token)
     case TOK_ASM_nopeq:
         asm_emit_opcode(token, 0xd << 21); // mov r0, r0
         break;
+    case TOK_ASM_wfeeq:
+        asm_emit_opcode(token, 0x320f002);
+    case TOK_ASM_wfieq:
+        asm_emit_opcode(token, 0x320f003);
+        break;
     default:
         expect("nullary instruction");
     }
@@ -103,6 +108,8 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
 
     switch (ARM_INSTRUCTION_GROUP(token)) {
     case TOK_ASM_nopeq:
+    case TOK_ASM_wfeeq:
+    case TOK_ASM_wfieq:
         return asm_nullary_opcode(token);
     default:
         expect("known instruction");
diff --git a/arm-tok.h b/arm-tok.h
index bcbabe3..6d9c596 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -52,3 +52,5 @@
 /* Note: add new tokens after nop (MUST always use DEF_ASM_CONDED) */
 
  DEF_ASM_CONDED(nop)
+ DEF_ASM_CONDED(wfe)
+ DEF_ASM_CONDED(wfi)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 07/16] arm-asm: Add parse_operand, Operand

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
tccpp: Allow '#' token to reach the assembler.
---
 arm-asm.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 tccpp.c   | 12 +++++++--
 2 files changed, 88 insertions(+), 2 deletions(-)

diff --git a/arm-asm.c b/arm-asm.c
index 4c663c3..aa37709 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -35,6 +35,84 @@ ST_FUNC void gen_le32(int c);
 #define USING_GLOBALS
 #include "tcc.h"
 
+enum {
+    OPT_REG32,
+    OPT_REGSET32,
+    OPT_IM8,
+    OPT_IM8N,
+    OPT_IM32,
+};
+#define OP_REG32  (1 << OPT_REG32)
+#define OP_REG    (OP_REG32)
+#define OP_IM32   (1 << OPT_IM32)
+#define OP_IM8   (1 << OPT_IM8)
+#define OP_IM8N   (1 << OPT_IM8N)
+#define OP_REGSET32  (1 << OPT_REGSET32)
+
+typedef struct Operand {
+    uint32_t type;
+    union {
+        uint8_t reg;
+        uint16_t regset;
+        ExprValue e;
+    };
+} Operand;
+
+/* Parse a text containing operand and store the result in OP */
+static void parse_operand(TCCState *s1, Operand *op)
+{
+    ExprValue e;
+    int8_t reg;
+    uint16_t regset = 0;
+
+    op->type = 0;
+
+    if (tok == '{') { // regset literal
+        next(); // skip '{'
+        while (tok != '}' && tok != TOK_EOF) {
+            reg = asm_parse_regvar(tok);
+            if (reg == -1) {
+                expect("register");
+                return;
+            } else
+                next(); // skip register name
+
+            regset |= 1 << reg;
+            if (tok != ',')
+                break;
+            next(); // skip ','
+        }
+        if (tok != '}')
+            expect("'}'");
+        next(); // skip '}'
+        if (regset == 0) {
+            // ARM instructions don't support empty regset.
+            tcc_error("empty register list is not supported");
+        } else {
+            op->type = OP_REGSET32;
+            op->regset = regset;
+        }
+    } else if (tok == '#' || tok == '$') {
+        /* constant value */
+        next(); // skip '#' or '$'
+        asm_expr(s1, &e);
+        op->type = OP_IM32;
+        op->e = e;
+        if (!op->e.sym) {
+            if ((int) op->e.v < 0 && (int) op->e.v >= -255)
+                op->type = OP_IM8N;
+            else if (op->e.v == (uint8_t)op->e.v)
+                op->type = OP_IM8;
+        } else
+            expect("constant");
+    } else if ((reg = asm_parse_regvar(tok)) != -1) {
+        next(); // skip register name
+        op->type = OP_REG32;
+        op->reg = (uint8_t) reg;
+    } else
+        expect("operand");
+}
+
 /* XXX: make it faster ? */
 ST_FUNC void g(int c)
 {
diff --git a/tccpp.c b/tccpp.c
index 449035d..d2e8933 100644
--- a/tccpp.c
+++ b/tccpp.c
@@ -1009,8 +1009,13 @@ redo_start:
                     goto redo_start;
                 else if (parse_flags & PARSE_FLAG_ASM_FILE)
                     p = parse_line_comment(p - 1);
-            } else if (parse_flags & PARSE_FLAG_ASM_FILE)
+            }
+#if !defined(TCC_TARGET_ARM)
+            else if (parse_flags & PARSE_FLAG_ASM_FILE)
                 p = parse_line_comment(p - 1);
+#else
+            /* ARM assembly uses '#' for constants */
+#endif
             break;
 _default:
         default:
@@ -2705,10 +2710,13 @@ maybe_newline:
                 p++;
                 tok = TOK_TWOSHARPS;
             } else {
+#if !defined(TCC_TARGET_ARM)
                 if (parse_flags & PARSE_FLAG_ASM_FILE) {
                     p = parse_line_comment(p - 1);
                     goto redo_no_start;
-                } else {
+                } else
+#endif
+                {
                     tok = '#';
                 }
             }

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 08/16] arm-asm: Add push, pop

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
Also edited tcctok.h to not redefine push, pop
---
 arm-asm.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 arm-tok.h |  5 +++++
 tcctok.h  |  2 +-
 3 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/arm-asm.c b/arm-asm.c
index aa37709..d79bf58 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -171,6 +171,61 @@ static void asm_nullary_opcode(int token)
     }
 }
 
+static void asm_block_data_transfer_opcode(TCCState *s1, int token)
+{
+    uint32_t opcode;
+    int op0_exclam;
+    Operand ops[2];
+    int nb_ops = 1;
+    parse_operand(s1, &ops[0]);
+    if (tok == '!') {
+        op0_exclam = 1;
+        next(); // skip '!'
+    }
+    if (tok == ',') {
+        next(); // skip comma
+        parse_operand(s1, &ops[1]);
+        ++nb_ops;
+    }
+    if (nb_ops < 1) {
+        expect("at least one operand");
+        return;
+    } else if (ops[nb_ops - 1].type != OP_REGSET32) {
+        expect("(last operand) register list");
+        return;
+    }
+
+    // block data transfer: 1 0 0 P U S W L << 20 (general case):
+    // operands:
+    //   Rn: bits 19...16 base register
+    //   Register List: bits 15...0
+
+    switch (ARM_INSTRUCTION_GROUP(token)) {
+    case TOK_ASM_pusheq: // TODO: Optimize 1-register case to: str ?, [sp, #-4]!
+        // Instruction: 1 I=0 P=1 U=0 S=0 W=1 L=0 << 20, op 1101
+        //   operands:
+        //      Rn: base register
+        //      Register List: bits 15...0
+        if (nb_ops != 1)
+            expect("exactly one operand");
+        else
+            asm_emit_opcode(token, (0x92d << 16) | ops[0].regset); // TODO: base register ?
+        break;
+    case TOK_ASM_popeq: // TODO: Optimize 1-register case to: ldr ?, [sp], #4
+        // Instruction: 1 I=0 P=0 U=1 S=0 W=0 L=1 << 20, op 1101
+        //   operands:
+        //      Rn: base register
+        //      Register List: bits 15...0
+        if (nb_ops != 1)
+            expect("exactly one operand");
+        else
+            asm_emit_opcode(token, (0x8bd << 16) | ops[0].regset); // TODO: base register ?
+        break;
+    default:
+        expect("block data transfer instruction");
+    }
+}
+
 ST_FUNC void asm_opcode(TCCState *s1, int token)
 {
     while (token == TOK_LINEFEED) {
@@ -185,6 +240,9 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
     }
 
     switch (ARM_INSTRUCTION_GROUP(token)) {
+    case TOK_ASM_pusheq:
+    case TOK_ASM_popeq:
+        return asm_block_data_transfer_opcode(s1, token);
     case TOK_ASM_nopeq:
     case TOK_ASM_wfeeq:
     case TOK_ASM_wfieq:
diff --git a/arm-tok.h b/arm-tok.h
index 6d9c596..82cb3dd 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -54,3 +54,8 @@
  DEF_ASM_CONDED(nop)
  DEF_ASM_CONDED(wfe)
  DEF_ASM_CONDED(wfi)
+
+ /* instruction macros */
+
+ DEF_ASM_CONDED(push)
+ DEF_ASM_CONDED(pop)
diff --git a/tcctok.h b/tcctok.h
index aeac1cd..4d2ce18 100644
--- a/tcctok.h
+++ b/tcctok.h
@@ -171,7 +171,7 @@
 
 /* pragma */
      DEF(TOK_pack, "pack")
-#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_X86_64)
+#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM)
      /* already defined for assembler */
      DEF(TOK_ASM_push, "push")
      DEF(TOK_ASM_pop, "pop")

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 09/16] arm-asm: Add swi

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 21 +++++++++++++++++++++
 arm-tok.h |  1 +
 2 files changed, 22 insertions(+)

diff --git a/arm-asm.c b/arm-asm.c
index d79bf58..b6dfafe 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -171,6 +171,25 @@ static void asm_nullary_opcode(int token)
     }
 }
 
+static void asm_unary_opcode(TCCState *s1, int token)
+{
+    Operand op;
+    parse_operand(s1, &op);
+
+    switch (ARM_INSTRUCTION_GROUP(token)) {
+    case TOK_ASM_swieq:
+        if (op.type != OP_IM8)
+            expect("immediate 8-bit unsigned integer");
+        else {
+            /* Note: Dummy operand (ignored by processor): ARM ref documented 0...255, ARM instruction set documented 24 bit */
+            asm_emit_opcode(token, (0xf << 24) | op.e.v);
+        }
+        break;
+    default:
+        expect("unary instruction");
+    }
+}
+
 static void asm_block_data_transfer_opcode(TCCState *s1, int token)
 {
     uint32_t opcode;
@@ -247,6 +266,8 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
     case TOK_ASM_wfeeq:
     case TOK_ASM_wfieq:
         return asm_nullary_opcode(token);
+    case TOK_ASM_swieq:
+        return asm_unary_opcode(s1, token);
     default:
         expect("known instruction");
     }
diff --git a/arm-tok.h b/arm-tok.h
index 82cb3dd..db1935f 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -54,6 +54,7 @@
  DEF_ASM_CONDED(nop)
  DEF_ASM_CONDED(wfe)
  DEF_ASM_CONDED(wfi)
+ DEF_ASM_CONDED(swi)
 
  /* instruction macros */
 

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 10/16] arm-asm: Add clz, sxtb, sxth, uxtb, uxth

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 arm-tok.h | 10 ++++++++++
 2 files changed, 57 insertions(+)

diff --git a/arm-asm.c b/arm-asm.c
index b6dfafe..c2598a8 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -190,6 +190,47 @@ static void asm_unary_opcode(TCCState *s1, int token)
     }
 }
 
+static void asm_binary_opcode(TCCState *s1, int token)
+{
+    Operand ops[2];
+    parse_operand(s1, &ops[0]);
+    if (tok == ',')
+        next();
+    else
+        expect("','");
+    parse_operand(s1, &ops[1]);
+    if (ops[0].type != OP_REG32) {
+        expect("(destination operand) register");
+        return;
+    }
+
+    if (ops[1].type != OP_REG32)
+        expect("(source operand) register");
+    else switch (ARM_INSTRUCTION_GROUP(token)) {
+    case TOK_ASM_clzeq:
+        asm_emit_opcode(token, 0x16f0f10 | (ops[0].reg << 12) | ops[1].reg);
+        break;
+    case TOK_ASM_sxtbeq:
+        /* TODO: optional ROR (8|16|24) */
+        asm_emit_opcode(token, 0x6af0070 | (ops[0].reg << 12) | ops[1].reg);
+        break;
+    case TOK_ASM_sxtheq:
+        /* TODO: optional ROR (8|16|24) */
+        asm_emit_opcode(token, 0x6bf0070 | (ops[0].reg << 12) | ops[1].reg);
+        break;
+    case TOK_ASM_uxtbeq:
+        /* TODO: optional ROR (8|16|24) */
+        asm_emit_opcode(token, 0x6ef0070 | (ops[0].reg << 12) | ops[1].reg);
+        break;
+    case TOK_ASM_uxtheq:
+        /* TODO: optional ROR (8|16|24) */
+        asm_emit_opcode(token, 0x6ff0070 | (ops[0].reg << 12) | ops[1].reg);
+        break;
+    default:
+        expect("binary instruction");
+    }
+}
+
 static void asm_block_data_transfer_opcode(TCCState *s1, int token)
 {
     uint32_t opcode;
@@ -268,6 +309,12 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
         return asm_nullary_opcode(token);
     case TOK_ASM_swieq:
         return asm_unary_opcode(s1, token);
+    case TOK_ASM_clzeq:
+    case TOK_ASM_sxtbeq:
+    case TOK_ASM_sxtheq:
+    case TOK_ASM_uxtbeq:
+    case TOK_ASM_uxtheq:
+        return asm_binary_opcode(s1, token);
     default:
         expect("known instruction");
     }
diff --git a/arm-tok.h b/arm-tok.h
index db1935f..06cee7a 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -56,6 +56,16 @@
  DEF_ASM_CONDED(wfi)
  DEF_ASM_CONDED(swi)
 
+ /* misc */
+ DEF_ASM_CONDED(clz)
+
+ /* size conversion */
+
+ DEF_ASM_CONDED(sxtb)
+ DEF_ASM_CONDED(sxth)
+ DEF_ASM_CONDED(uxtb)
+ DEF_ASM_CONDED(uxth)
+
  /* instruction macros */
 
  DEF_ASM_CONDED(push)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 11/16] arm-asm: Add mul, mla, smull, umull, smlal, umlal

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 arm-tok.h |  15 +++++
 2 files changed, 190 insertions(+)

diff --git a/arm-asm.c b/arm-asm.c
index c2598a8..792b914 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 06cee7a..5106250 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)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 12/16] arm-asm: Add stmda, ldmda, stm, ldm, stmia, ldmia, stmdb, ldmdb, stmib, ldmib

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 arm-tok.h | 13 ++++++++++
 2 files changed, 84 insertions(+)

diff --git a/arm-asm.c b/arm-asm.c
index 792b914..d62b9c3 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -231,6 +231,15 @@ static void asm_binary_opcode(TCCState *s1, int token)
     }
 }
 
+/* data processing and single data transfer instructions only */
+#define ENCODE_RN(register_index) ((register_index) << 16)
+#define ENCODE_RD(register_index) ((register_index) << 12)
+#define ENCODE_SET_CONDITION_CODES (1 << 20)
+
+/* Note: For data processing instructions, "1" means immediate.
+   Note: For single data transfer instructions, "0" means immediate. */
+#define ENCODE_IMMEDIATE_FLAG (1 << 25)
+
 static void asm_block_data_transfer_opcode(TCCState *s1, int token)
 {
     uint32_t opcode;
@@ -281,6 +290,58 @@ static void asm_block_data_transfer_opcode(TCCState *s1, int token)
         else
             asm_emit_opcode(token, (0x8bd << 16) | ops[0].regset); // TODO: base register ?
         break;
+    case TOK_ASM_stmdaeq:
+    case TOK_ASM_ldmdaeq:
+    case TOK_ASM_stmeq:
+    case TOK_ASM_ldmeq:
+    case TOK_ASM_stmiaeq:
+    case TOK_ASM_ldmiaeq:
+    case TOK_ASM_stmdbeq:
+    case TOK_ASM_ldmdbeq:
+    case TOK_ASM_stmibeq:
+    case TOK_ASM_ldmibeq:
+        switch (ARM_INSTRUCTION_GROUP(token)) {
+        case TOK_ASM_stmdaeq: // post-decrement store
+            opcode = 0x82 << 20;
+            break;
+        case TOK_ASM_ldmdaeq: // post-decrement load
+            opcode = 0x83 << 20;
+            break;
+        case TOK_ASM_stmeq: // post-increment store
+        case TOK_ASM_stmiaeq: // post-increment store
+            opcode = 0x8a << 20;
+            break;
+        case TOK_ASM_ldmeq: // post-increment load
+        case TOK_ASM_ldmiaeq: // post-increment load
+            opcode = 0x8b << 20;
+            break;
+        case TOK_ASM_stmdbeq: // pre-decrement store
+            opcode = 0x92 << 20;
+            break;
+        case TOK_ASM_ldmdbeq: // pre-decrement load
+            opcode = 0x93 << 20;
+            break;
+        case TOK_ASM_stmibeq: // pre-increment store
+            opcode = 0x9a << 20;
+            break;
+        case TOK_ASM_ldmibeq: // pre-increment load
+            opcode = 0x9b << 20;
+            break;
+        default:
+            tcc_error("internal error: This place should not be reached (fallback in asm_block_data_transfer_opcode)");
+        }
+        // operands:
+        //    Rn: first operand
+        //    Register List: lower bits
+        if (nb_ops != 2)
+            expect("exactly two operands");
+        else if (ops[0].type != OP_REG32)
+            expect("(first operand) register");
+        else if (!op0_exclam)
+            tcc_error("first operand of '%s' should have an exclamation mark", get_tok_str(token, NULL));
+        else
+            asm_emit_opcode(token, opcode | ENCODE_RN(ops[0].reg) | ops[1].regset);
+        break;
     default:
         expect("block data transfer instruction");
     }
@@ -461,6 +522,16 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
     switch (ARM_INSTRUCTION_GROUP(token)) {
     case TOK_ASM_pusheq:
     case TOK_ASM_popeq:
+    case TOK_ASM_stmdaeq:
+    case TOK_ASM_ldmdaeq:
+    case TOK_ASM_stmeq:
+    case TOK_ASM_ldmeq:
+    case TOK_ASM_stmiaeq:
+    case TOK_ASM_ldmiaeq:
+    case TOK_ASM_stmdbeq:
+    case TOK_ASM_ldmdbeq:
+    case TOK_ASM_stmibeq:
+    case TOK_ASM_ldmibeq:
         return asm_block_data_transfer_opcode(s1, token);
     case TOK_ASM_nopeq:
     case TOK_ASM_wfeeq:
diff --git a/arm-tok.h b/arm-tok.h
index 5106250..3860177 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -81,6 +81,19 @@
  DEF_ASM_CONDED(umlal)
  DEF_ASM_CONDED(umlals)
 
+ /* load/store */
+
+ DEF_ASM_CONDED(stmda)
+ DEF_ASM_CONDED(ldmda)
+ DEF_ASM_CONDED(stm)
+ DEF_ASM_CONDED(ldm)
+ DEF_ASM_CONDED(stmia)
+ DEF_ASM_CONDED(ldmia)
+ DEF_ASM_CONDED(stmdb)
+ DEF_ASM_CONDED(ldmdb)
+ DEF_ASM_CONDED(stmib)
+ DEF_ASM_CONDED(ldmib)
+
  /* instruction macros */
 
  DEF_ASM_CONDED(push)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 13/16] arm-asm: Add ldr, ldrb, str, strb

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 arm-tok.h |   5 +++
 2 files changed, 128 insertions(+)

diff --git a/arm-asm.c b/arm-asm.c
index d62b9c3..9e9fb93 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -506,6 +506,123 @@ static void asm_long_multiplication_opcode(TCCState *s1, int token)
     }
 }
 
+static void asm_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 << 26;
+    // Note: ldr r0, [r4, #4]  ; simple offset: r0 = *(int*)(r4+4); r4 unchanged
+    // Note: ldr r0, [r4, #4]! ; pre-indexed:   r0 = *(int*)(r4+4); r4 = r4+4
+    // Note: ldr r0, [r4], #4  ; post-indexed:  r0 = *(int*)(r4+0); r4 = r4+4
+
+    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("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 != ',')
+        expect("','");
+    else
+        next(); // skip ','
+    if (tok == '-') {
+        op2_minus = 1;
+        next();
+    }
+    parse_operand(s1, &ops[2]);
+    if (!closed_bracket) {
+        if (tok != ']')
+            expect("']'");
+        else
+            next(); // skip ']'
+        opcode |= 1 << 24; // add offset before transfer
+        if (tok == '!') {
+            exclam = 1;
+            next(); // skip '!'
+        }
+    }
+
+    // single data transfer: 0 1 I P U B W L << 20 (general case):
+    // operands:
+    //    Rd: destination operand [ok]
+    //    Rn: first source operand [ok]
+    //    Operand2: bits 11...0 [ok]
+    // I: immediate operand? [ok]
+    // P: Pre/post indexing is PRE: Add offset before transfer [ok]
+    // U: Up/down is up? (*adds* offset to base) [ok]
+    // B: Byte/word is byte?  TODO
+    // W: Write address back into base? [ok]
+    // L: Load/store is load? [ok]
+    if (exclam)
+        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 >= 0x1000)
+                tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
+            else
+                opcode |= v;
+        } else { // down
+            if (v <= -0x1000)
+                tcc_error("offset out of range for '%s'", get_tok_str(token, NULL));
+            else
+                opcode |= -v;
+        }
+    } else if (ops[2].type == OP_REG32) {
+        if (!op2_minus)
+            opcode |= 1 << 23; // up
+        opcode |= ENCODE_IMMEDIATE_FLAG; /* if set, it means it's NOT immediate */
+        opcode |= ops[2].reg;
+    } else
+        expect("register");
+
+    switch (ARM_INSTRUCTION_GROUP(token)) {
+    case TOK_ASM_strbeq:
+        opcode |= 1 << 22; // B
+        /* fallthrough */
+    case TOK_ASM_streq:
+        asm_emit_opcode(token, opcode);
+        break;
+    case TOK_ASM_ldrbeq:
+        opcode |= 1 << 22; // B
+        /* fallthrough */
+    case TOK_ASM_ldreq:
+        opcode |= 1 << 20; // L
+        asm_emit_opcode(token, opcode);
+        break;
+    default:
+        expect("data transfer instruction");
+    }
+}
+
 ST_FUNC void asm_opcode(TCCState *s1, int token)
 {
     while (token == TOK_LINEFEED) {
@@ -546,6 +663,12 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
     case TOK_ASM_uxtheq:
         return asm_binary_opcode(s1, token);
 
+    case TOK_ASM_ldreq:
+    case TOK_ASM_ldrbeq:
+    case TOK_ASM_streq:
+    case TOK_ASM_strbeq:
+        return asm_single_data_transfer_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 3860177..a2a2f70 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -83,6 +83,11 @@
 
  /* load/store */
 
+ DEF_ASM_CONDED(ldr)
+ DEF_ASM_CONDED(ldrb)
+ DEF_ASM_CONDED(str)
+ DEF_ASM_CONDED(strb)
+
  DEF_ASM_CONDED(stmda)
  DEF_ASM_CONDED(ldmda)
  DEF_ASM_CONDED(stm)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 14/16] arm-asm: Add and, eor, sub, rsb, add, adc, sbc, rsc, tst, teq, cmp, cmn, orr, mov, bic, mvn

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 arm-tok.h |  35 ++++++++++++
 2 files changed, 200 insertions(+)

diff --git a/arm-asm.c b/arm-asm.c
index 9e9fb93..58853f6 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -347,6 +347,137 @@ static void asm_block_data_transfer_opcode(TCCState *s1, int token)
     }
 }
 
+static void asm_data_processing_opcode(TCCState *s1, int token)
+{
+    Operand ops[3];
+    int nb_ops;
+
+    /* 16 entries per instruction for the different condition codes */
+    uint32_t opcode_idx = (ARM_INSTRUCTION_GROUP(token) - TOK_ASM_andeq) >> 4;
+
+    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) {
+        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;
+    } else {
+        uint32_t opcode = 0;
+        uint32_t operands = 0;
+
+        // data processing (general case):
+        // operands:
+        //   Rn: bits 19...16 (first operand)
+        //   Rd: bits 15...12 (destination)
+        //   Operand2: bits 11...0 (second operand);  depending on I that's either a register or an immediate
+        // operator:
+        //   bits 24...21: "OpCode"--see below
+
+        /* operations in the token list are ordered by opcode */
+        opcode = (opcode_idx >> 1) << 21; // drop "s"
+        if (ops[0].type != OP_REG32)
+            expect("(destination operand) register");
+        else if (opcode == 0xa << 21 || opcode == 0xb << 21 || opcode == 0x8 << 21 || opcode == 0x9 << 21 ) // cmp, cmn, tst, teq
+            operands |= ENCODE_SET_CONDITION_CODES; // force S set, otherwise it's a completely different instruction.
+        else
+            operands |= ENCODE_RD(ops[0].reg);
+        if (ops[1].type != OP_REG32)
+            expect("(first source operand) register");
+        else if (!(opcode == 0xd << 21 || opcode == 0xf << 21)) // not: mov, mvn (those have only one source operand)
+            operands |= ENCODE_RN(ops[1].reg);
+        switch (ops[2].type) {
+        case OP_REG32:
+            // TODO: Parse and encode shift.
+            operands |= ops[2].reg;
+            break;
+        case OP_IM8:
+            // TODO: Parse and encode rotation.
+            operands |= ENCODE_IMMEDIATE_FLAG;
+            operands |= ops[2].e.v;
+            break;
+        case OP_IM8N: // immediate negative value
+            // TODO: Parse and encode rotation.
+            operands |= ENCODE_IMMEDIATE_FLAG;
+            /* Instruction swapping:
+               0001 = EOR - Rd:= Op1 EOR Op2     -> difficult
+               0011 = RSB - Rd:= Op2 - Op1       -> difficult
+               0111 = RSC - Rd:= Op2 - Op1 + C   -> difficult
+               1000 = TST - CC on: Op1 AND Op2   -> difficult
+               1001 = TEQ - CC on: Op1 EOR Op2   -> difficult
+               1100 = ORR - Rd:= Op1 OR Op2      -> difficult
+            */
+            switch (opcode_idx >> 1) { // "OpCode" in ARM docs
+#if 1
+            case 0x0: // AND - Rd:= Op1 AND Op2
+                opcode = 0xe << 21; // BIC
+                operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
+                break;
+            case 0x2: // SUB - Rd:= Op1 - Op2
+                opcode = 0x4 << 21; // ADD
+                operands |= (-ops[2].e.v) & 0xFF;
+                break;
+            case 0x4: // ADD - Rd:= Op1 + Op2
+                opcode = 0x2 << 21; // SUB
+                operands |= (-ops[2].e.v) & 0xFF;
+                break;
+            case 0x5: // ADC - Rd:= Op1 + Op2 + C
+                opcode = 0x6 << 21; // SBC
+                operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
+                break;
+            case 0x6: // SBC - Rd:= Op1 - Op2 + C
+                opcode = 0x5 << 21; // ADC
+                operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
+                break;
+#endif
+            case 0xa: // CMP - CC on: Op1 - Op2
+                opcode = 0xb << 21; // CMN
+                operands |= (-ops[2].e.v) & 0xFF;
+                break;
+            case 0xb: // CMN - CC on: Op1 + Op2
+                opcode = 0xa << 21; // CMP
+                operands |= (-ops[2].e.v) & 0xFF;
+                break;
+            // moveq r1, r3: 0x01a01003; mov Rd, Op2
+            case 0xd: // MOV - Rd:= Op2
+                opcode = 0xf << 21; // MVN
+                operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
+                break;
+#if 1
+            case 0xe: // BIC - Rd:= Op1 AND NOT Op2
+                opcode = 0x0 << 21; // AND
+                operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
+                break;
+#endif
+            case 0xf: // MVN - Rd:= NOT Op2
+                opcode = 0xd << 21; // MOV
+                operands |= (ops[2].e.v ^ 0xFF) & 0xFF;
+                break;
+            default:
+                tcc_error("cannot use '%s' with a negative immediate value", get_tok_str(token, NULL));
+            }
+            break;
+        default:
+            expect("(second source operand) register or immediate value");
+        }
+
+        /* S=0 and S=1 entries alternate one after another, in that order */
+        opcode |= (opcode_idx & 1) ? ENCODE_SET_CONDITION_CODES : 0;
+        asm_emit_opcode(token, opcode | operands);
+    }
+}
+
 static void asm_multiplication_opcode(TCCState *s1, int token)
 {
     Operand ops[4];
@@ -669,6 +800,40 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
     case TOK_ASM_strbeq:
         return asm_single_data_transfer_opcode(s1, token);
 
+    case TOK_ASM_andeq:
+    case TOK_ASM_eoreq:
+    case TOK_ASM_subeq:
+    case TOK_ASM_rsbeq:
+    case TOK_ASM_addeq:
+    case TOK_ASM_adceq:
+    case TOK_ASM_sbceq:
+    case TOK_ASM_rsceq:
+    case TOK_ASM_tsteq:
+    case TOK_ASM_teqeq:
+    case TOK_ASM_cmpeq:
+    case TOK_ASM_cmneq:
+    case TOK_ASM_orreq:
+    case TOK_ASM_moveq:
+    case TOK_ASM_biceq:
+    case TOK_ASM_mvneq:
+    case TOK_ASM_andseq:
+    case TOK_ASM_eorseq:
+    case TOK_ASM_subseq:
+    case TOK_ASM_rsbseq:
+    case TOK_ASM_addseq:
+    case TOK_ASM_adcseq:
+    case TOK_ASM_sbcseq:
+    case TOK_ASM_rscseq:
+//  case TOK_ASM_tstseq:
+//  case TOK_ASM_teqseq:
+//  case TOK_ASM_cmpseq:
+//  case TOK_ASM_cmnseq:
+    case TOK_ASM_orrseq:
+    case TOK_ASM_movseq:
+    case TOK_ASM_bicseq:
+    case TOK_ASM_mvnseq:
+        return asm_data_processing_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 a2a2f70..023b01a 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -103,3 +103,38 @@
 
  DEF_ASM_CONDED(push)
  DEF_ASM_CONDED(pop)
+
+ /* data processing instructions; order is important */
+
+ DEF_ASM_CONDED(and)
+ DEF_ASM_CONDED(ands)
+ DEF_ASM_CONDED(eor)
+ DEF_ASM_CONDED(eors)
+ DEF_ASM_CONDED(sub)
+ DEF_ASM_CONDED(subs)
+ DEF_ASM_CONDED(rsb)
+ DEF_ASM_CONDED(rsbs)
+ DEF_ASM_CONDED(add)
+ DEF_ASM_CONDED(adds)
+ DEF_ASM_CONDED(adc)
+ DEF_ASM_CONDED(adcs)
+ DEF_ASM_CONDED(sbc)
+ DEF_ASM_CONDED(sbcs)
+ DEF_ASM_CONDED(rsc)
+ DEF_ASM_CONDED(rscs)
+ DEF_ASM_CONDED(tst)
+ DEF_ASM_CONDED(tsts) // necessary here--but not useful to the user
+ DEF_ASM_CONDED(teq)
+ DEF_ASM_CONDED(teqs) // necessary here--but not useful to the user
+ DEF_ASM_CONDED(cmp)
+ DEF_ASM_CONDED(cmps) // necessary here--but not useful to the user
+ DEF_ASM_CONDED(cmn)
+ DEF_ASM_CONDED(cmns) // necessary here--but not useful to the user
+ DEF_ASM_CONDED(orr)
+ DEF_ASM_CONDED(orrs)
+ DEF_ASM_CONDED(mov)
+ DEF_ASM_CONDED(movs)
+ DEF_ASM_CONDED(bic)
+ DEF_ASM_CONDED(bics)
+ DEF_ASM_CONDED(mvn)
+ DEF_ASM_CONDED(mvns)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 15/16] arm-asm: Add b, bl, bx, blx

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 arm-tok.h |  7 +++++++
 2 files changed, 67 insertions(+)

diff --git a/arm-asm.c b/arm-asm.c
index 58853f6..8c8a464 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -754,6 +754,61 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token)
     }
 }
 
+/* Note: almost dupe of encbranch in arm-gen.c */
+static uint32_t encbranchoffset(int pos, int addr, int fail)
+{
+  addr-=pos+8;
+  addr/=4;
+  if(addr>=0x1000000 || addr<-0x1000000) { // FIXME: Is that correct?
+    if(fail)
+      tcc_error("function bigger than 32MB");
+    return 0;
+  }
+  return /*not 0x0A000000|*/(addr&0xffffff);
+}
+
+static void asm_branch_opcode(TCCState *s1, int token)
+{
+    int jmp_disp = 0;
+    Operand op;
+    parse_operand(s1, &op);
+    if (op.type == OP_IM32 || op.type == OP_IM8 || op.type == OP_IM8N) {
+        jmp_disp = encbranchoffset(ind, op.e.v, 0);
+        if (jmp_disp < -0x800000 || jmp_disp > 0x7fffff) {
+            tcc_error("branch is too far");
+            return;
+        }
+    }
+    switch (ARM_INSTRUCTION_GROUP(token)) {
+    case TOK_ASM_beq:
+        if (op.type == OP_IM32 || op.type == OP_IM8 || op.type == OP_IM8N)
+            asm_emit_opcode(token, (0xa << 24) | (jmp_disp & 0xffffff));
+        else
+            expect("branch target");
+        break;
+    case TOK_ASM_bleq:
+        if (op.type == OP_IM32 || op.type == OP_IM8 || op.type == OP_IM8N)
+            asm_emit_opcode(token, (0xb << 24) | (jmp_disp & 0xffffff));
+        else
+            expect("branch target");
+        break;
+    case TOK_ASM_bxeq:
+        if (op.type != OP_REG32)
+            expect("register");
+        else
+            asm_emit_opcode(token, (0x12fff1 << 4) | op.reg);
+        break;
+    case TOK_ASM_blxeq:
+        if (op.type != OP_REG32)
+            expect("register");
+        else
+            asm_emit_opcode(token, (0x12fff3 << 4) | op.reg);
+        break;
+    default:
+        expect("branch instruction");
+    }
+}
+
 ST_FUNC void asm_opcode(TCCState *s1, int token)
 {
     while (token == TOK_LINEFEED) {
@@ -787,6 +842,11 @@ ST_FUNC void asm_opcode(TCCState *s1, int token)
         return asm_nullary_opcode(token);
     case TOK_ASM_swieq:
         return asm_unary_opcode(s1, token);
+    case TOK_ASM_beq:
+    case TOK_ASM_bleq:
+    case TOK_ASM_bxeq:
+    case TOK_ASM_blxeq:
+        return asm_branch_opcode(s1, token);
     case TOK_ASM_clzeq:
     case TOK_ASM_sxtbeq:
     case TOK_ASM_sxtheq:
diff --git a/arm-tok.h b/arm-tok.h
index 023b01a..2f1798b 100644
--- a/arm-tok.h
+++ b/arm-tok.h
@@ -104,6 +104,13 @@
  DEF_ASM_CONDED(push)
  DEF_ASM_CONDED(pop)
 
+ /* branches */
+
+ DEF_ASM_CONDED(b)
+ DEF_ASM_CONDED(bl)
+ DEF_ASM_CONDED(bx)
+ DEF_ASM_CONDED(blx)
+
  /* data processing instructions; order is important */
 
  DEF_ASM_CONDED(and)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH 16/16] arm-asm: Optimize gen_le32

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
---
 arm-asm.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/arm-asm.c b/arm-asm.c
index 8c8a464..2c57b27 100644
--- a/arm-asm.c
+++ b/arm-asm.c
@@ -134,8 +134,16 @@ ST_FUNC void gen_le16 (int i)
 
 ST_FUNC void gen_le32 (int i)
 {
-    gen_le16(i);
-    gen_le16(i>>16);
+    int ind1;
+    if (nocode_wanted)
+        return;
+    ind1 = ind + 4;
+    if (ind1 > cur_text_section->data_allocated)
+        section_realloc(cur_text_section, ind1);
+    cur_text_section->data[ind++] = i & 0xFF;
+    cur_text_section->data[ind++] = (i >> 8) & 0xFF;
+    cur_text_section->data[ind++] = (i >> 16) & 0xFF;
+    cur_text_section->data[ind++] = (i >> 24) & 0xFF;
 }
 
 ST_FUNC void gen_expr32(ExprValue *pe)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 00/16] Add ARM inline assembler

Danny Milosavljevic
In reply to this post by Danny Milosavljevic
Unit tests:

#!/bin/sh

set -e

cat arm-tok.h |grep DEF_ASM_CONDED |grep -v '#define' |grep -v '/[*]' |sed -e 's;DEF_ASM_CONDED.\(.*\).$;\1;'| grep -v 'not useful' >L
for s in $(cat L)
do
        ok=0
        for args in "r3, r4, r5, r6" \
                    "r3, r4, r5" \
                    "r3, r4" "r3" \
                    "{r3,r4,r5}" \
                    "r2!, {r3,r4,r5}" \
                    "r2, [r3, r4]" \
                    "r2, [r3, r4]!" \
                    "r2, [r3, -r4]" \
                    "r2, [r3, -r4]!" \
                    "r2, [r3], r4" \
                    "r2, [r3, #4]" \
                    "r2, [r3, #-4]" \
                    "r2, r3, #4" \
                    "r2, r3, #-4" \
                    "r2, #4" \
                    "r2, #-4" \
                    "#4" \
                    "#-4" \
                    ""
        do
                echo "$s $args" > a.s
                #cat a.s
                if as -o a.o a.s 2>/dev/null
                then
                        objdump -S a.o |grep "^[ ]*0:" >a.expected

                        echo '__asm__("'"$s ${args}"'");' > a.c
                        if ./tcc -o a.o -c a.c
                        then
                                objdump -S a.o |grep "^[ ]*0:" >a.got
                                diff -u a.got a.expected || {
                                        echo "warning: '$s $args' did not work correctly in tcc (see above)">&2
                                }
                        else
                                echo "warning: '$s $args' did not work in tcc">&2
                        fi
                        ok=1
                fi
        done
        if [ "${ok}" -eq "0" ]
        then
                echo "warning: $s could not be used.">&2
        fi
done

(This cannot test branches, though)

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

attachment0 (499 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 00/16] Add ARM inline assembler

Jan Nieuwenhuizen
In reply to this post by Danny Milosavljevic
Danny Milosavljevic writes:

Hello!

> This patchset adds an ARM inline assembler.

That's awesome!  This is going to make the ARM bootstrap of tinycc so
much nicer.

In other news, I finally got setjmp/longjmp to work with tcc.  Because
has a different function pre- and post-amble than gcc it needed some
$fp/$sp juggling.  With this we just may have tinycc bootstrap for ARM
done, or have a clearer view of the next problem.

Greetings,
Janneke

--
Jan Nieuwenhuizen <[hidden email]> | GNU LilyPond http://lilypond.org
Freelance IT http://JoyofSource.com | AvatarĀ® http://AvatarAcademy.com

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH 00/16] Add ARM inline assembler

Michael Matz-4
In reply to this post by Danny Milosavljevic
Hello Danny,

On Sat, 26 Dec 2020, Danny Milosavljevic wrote:

> This patchset adds an ARM inline assembler.

Wonderful!  I like the content and the form of presentation :)
Cool stuff.


Ciao,
Michael.

_______________________________________________
Tinycc-devel mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel