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 |
---
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
Free forum by Nabble | Edit this page |