This patchset adds some more improvements to the ARM inline assembler.
1. Bugfix for branch instruction so it can actually branch to (past) labels. 2. Add ldrex, strex instructions. 3. Allow implicit offset 0, for example: ldr r1, [r2] Danny Milosavljevic (3): arm-asm: Implement branch to label arm-asm: Add ldrex, ldrexb, strex, strexb arm-asm: Allow implicit offset 0 in input of asm_single_data_transfer_opcode arm-asm.c | 119 +++++++++++++++++++++++++++++++++++++++++++----------- arm-tok.h | 4 ++ 2 files changed, 99 insertions(+), 24 deletions(-) _______________________________________________ Tinycc-devel mailing list [hidden email] https://lists.nongnu.org/mailman/listinfo/tinycc-devel |
---
arm-asm.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/arm-asm.c b/arm-asm.c index fc92898..27f396b 100644 --- a/arm-asm.c +++ b/arm-asm.c @@ -1058,9 +1058,9 @@ 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(addr>=0x7fffff || addr<-0x800000) { if(fail) - tcc_error("function bigger than 32MB"); + tcc_error("branch offset is too far"); return 0; } return /*not 0x0A000000|*/(addr&0xffffff); @@ -1070,26 +1070,30 @@ 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"); + ExprValue e; + ElfSym *esym; + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_beq: + case TOK_ASM_bleq: + asm_expr(s1, &e); + esym = elfsym(e.sym); + if (!esym || esym->st_shndx != cur_text_section->sh_num) { + tcc_error("invalid branch target"); return; } + jmp_disp = encbranchoffset(ind, e.v + esym->st_value, 1); + break; + default: + parse_operand(s1, &op); + break; } 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"); + asm_emit_opcode(token, (0xa << 24) | (jmp_disp & 0xffffff)); 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"); + asm_emit_opcode(token, (0xb << 24) | (jmp_disp & 0xffffff)); break; case TOK_ASM_bxeq: if (op.type != OP_REG32) _______________________________________________ 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 | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- arm-tok.h | 4 ++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/arm-asm.c b/arm-asm.c index 27f396b..5b64fa6 100644 --- a/arm-asm.c +++ b/arm-asm.c @@ -939,10 +939,11 @@ static void asm_long_multiplication_opcode(TCCState *s1, int token) static void asm_single_data_transfer_opcode(TCCState *s1, int token) { Operand ops[3]; + Operand strex_operand; int exclam = 0; int closed_bracket = 0; int op2_minus = 0; - uint32_t opcode = 1 << 26; + uint32_t opcode = 0; // 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 @@ -955,9 +956,25 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) return; } if (tok != ',') - expect("two arguments"); + expect("at least two arguments"); else next(); // skip ',' + + switch (ARM_INSTRUCTION_GROUP(token)) { + case TOK_ASM_strexbeq: + case TOK_ASM_strexeq: + parse_operand(s1, &strex_operand); + if (strex_operand.type != OP_REG32) { + expect("register"); + return; + } + if (tok != ',') + expect("at least three arguments"); + else + next(); // skip ',' + break; + } + if (tok != '[') expect("'['"); else @@ -1039,6 +1056,7 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) opcode |= 1 << 22; // B /* fallthrough */ case TOK_ASM_streq: + opcode |= 1 << 26; // Load/Store asm_emit_opcode(token, opcode); break; case TOK_ASM_ldrbeq: @@ -1046,6 +1064,47 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) /* fallthrough */ case TOK_ASM_ldreq: opcode |= 1 << 20; // L + opcode |= 1 << 26; // Load/Store + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_strexbeq: + opcode |= 1 << 22; // B + /* fallthrough */ + case TOK_ASM_strexeq: + if (opcode & 0xFFF) { + tcc_error("offset not allowed with 'strex'"); + return; + } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate + tcc_error("offset not allowed with 'strex'"); + return; + } + if ((opcode & (1 << 24)) == 0) { // add offset after transfer + tcc_error("adding offset after transfer not allowed with 'strex'"); + return; + } + + opcode |= 0xf90; + opcode |= strex_operand.reg; + asm_emit_opcode(token, opcode); + break; + case TOK_ASM_ldrexbeq: + opcode |= 1 << 22; // B + /* fallthrough */ + case TOK_ASM_ldrexeq: + if (opcode & 0xFFF) { + tcc_error("offset not allowed with 'ldrex'"); + return; + } else if (opcode & ENCODE_IMMEDIATE_FLAG) { // if set, it means it's NOT immediate + tcc_error("offset not allowed with 'ldrex'"); + return; + } + if ((opcode & (1 << 24)) == 0) { // add offset after transfer + tcc_error("adding offset after transfer not allowed with 'ldrex'"); + return; + } + opcode |= 1 << 20; // L + opcode |= 0x00f; + opcode |= 0xf90; asm_emit_opcode(token, opcode); break; default: @@ -1163,6 +1222,10 @@ ST_FUNC void asm_opcode(TCCState *s1, int token) case TOK_ASM_ldrbeq: case TOK_ASM_streq: case TOK_ASM_strbeq: + case TOK_ASM_ldrexeq: + case TOK_ASM_ldrexbeq: + case TOK_ASM_strexeq: + case TOK_ASM_strexbeq: return asm_single_data_transfer_opcode(s1, token); case TOK_ASM_andeq: diff --git a/arm-tok.h b/arm-tok.h index 7c1a202..be927cc 100644 --- a/arm-tok.h +++ b/arm-tok.h @@ -93,6 +93,10 @@ DEF_ASM_CONDED(ldrb) DEF_ASM_CONDED(str) DEF_ASM_CONDED(strb) + DEF_ASM_CONDED(ldrex) + DEF_ASM_CONDED(ldrexb) + DEF_ASM_CONDED(strex) + DEF_ASM_CONDED(strexb) DEF_ASM_CONDED(stmda) DEF_ASM_CONDED(ldmda) _______________________________________________ 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 | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/arm-asm.c b/arm-asm.c index 5b64fa6..0f7340c 100644 --- a/arm-asm.c +++ b/arm-asm.c @@ -992,15 +992,19 @@ static void asm_single_data_transfer_opcode(TCCState *s1, int token) closed_bracket = 1; // exclam = 1; // implicit in hardware; don't do it in software } - if (tok != ',') - expect("','"); - else + if (tok == ',') { next(); // skip ',' - if (tok == '-') { - op2_minus = 1; - next(); + if (tok == '-') { + op2_minus = 1; + next(); + } + parse_operand(s1, &ops[2]); + } else { + // end of input expression in brackets--assume 0 offset + ops[2].type = OP_IM8; + ops[2].e.v = 0; + opcode |= 1 << 24; // add offset before transfer } - parse_operand(s1, &ops[2]); if (!closed_bracket) { if (tok != ']') expect("']'"); _______________________________________________ Tinycc-devel mailing list [hidden email] https://lists.nongnu.org/mailman/listinfo/tinycc-devel |
In reply to this post by Danny Milosavljevic
Some tests which cannot be automatically generated:
1. __asm__(".a:\n\t" "mov r0, #1\n\t" "bne .a"); 2. __asm__("mov r0, #1\n\t" "bne L0\n\t" "L0:\n\t"); 3. __asm__("mov r1, #2\n\t" ".L0:\n\t" "mov r0, #1\n\t" "bne .L0"); So maybe we should have a directory with manual assembly tests, probably at least one for arm and one for x86. What do you think? _______________________________________________ Tinycc-devel mailing list [hidden email] https://lists.nongnu.org/mailman/listinfo/tinycc-devel |
Hello,
On Tue, 5 Jan 2021, Danny Milosavljevic wrote: > Some tests which cannot be automatically generated: > > 1. > > __asm__(".a:\n\t" > "mov r0, #1\n\t" > "bne .a"); > > 2. > > __asm__("mov r0, #1\n\t" > "bne L0\n\t" > "L0:\n\t"); > > 3. > > __asm__("mov r1, #2\n\t" > ".L0:\n\t" > "mov r0, #1\n\t" > "bne .L0"); > > So maybe we should have a directory with manual assembly tests, probably > at least one for arm and one for x86. > > What do you think? Don't overcomplicate :) Make it one file and add it as tests/asm-arm.c (or .s or .S). Ciao, Michael. _______________________________________________ Tinycc-devel mailing list [hidden email] https://lists.nongnu.org/mailman/listinfo/tinycc-devel |
Free forum by Nabble | Edit this page |