diff options
| -rw-r--r-- | asm.peg | 25 | ||||
| -rw-r--r-- | main.c | 93 | ||||
| -rw-r--r-- | minias.h | 5 |
3 files changed, 93 insertions, 30 deletions
@@ -336,21 +336,30 @@ r64-or-rip = ( | r:rip ) { $$ = r; } +scale-index-base = + '(' ws? b:r64 ws? ',' ws? i:r64 ws? ',' ws? s:number ws? ')' + { $$.memarg = (Memarg){.kind=ASM_MEMARG, .scale = s.i64, .index=i.kind, .base = b.kind, .c = 0, .l = NULL }; } + | '(' ws? b:r64 ws? ',' ws? i:r64 ')' + { $$.memarg = (Memarg){.kind=ASM_MEMARG, .scale = 1, .index=i.kind, .base = b.kind, .c = 0, .l = NULL }; } + | '(' ws? b:r64-or-rip ws? ')' + { $$.memarg = (Memarg){.kind=ASM_MEMARG, .scale = 0, .index=ASM_NO_REG, .base = b.kind, .c = 0, .l = NULL }; } + +# XXX There are more addressing modes. m = - '(' ws? r:r64-or-rip ws? ')' - { $$.memarg = (Memarg){ .kind = ASM_MEMARG, .c = 0, .l = NULL, .reg = r.kind }; } - | <'-'?[0-9]+> ws? '(' ws? r:r64-or-rip ws? ')' - { $$.memarg = (Memarg){ .kind = ASM_MEMARG, .c = strtoll(yytext, NULL, 10), .l = NULL, .reg = r.kind }; } - | i:ident ws? '(' ws? r:r64-or-rip ws? ')' - { $$.memarg = (Memarg){ .kind = ASM_MEMARG, .c = 0, .l = i.charptr, .reg = r.kind }; } + sib:scale-index-base + { $$ = sib; } + | disp:number ws? sib:scale-index-base + { sib.memarg.c = disp.i64; $$ = sib; } + | i:ident ws? sib:scale-index-base + { sib.memarg.l = i.charptr; $$ = sib; } imm8 = i:imm { i.imm.nbytes = 1; $$ = i; } imm16 = i:imm { i.imm.nbytes = 2; $$ = i; } imm32 = i:imm { i.imm.nbytes = 4; $$ = i; } imm = - '$' ws? <'-'?[0-9]+> - { $$.imm = (Imm){ .kind = ASM_IMM, .c = strtoll(yytext, NULL, 10), .l = NULL, .nbytes = 0}; } + '$' ws? n:number + { $$.imm = (Imm){ .kind = ASM_IMM, .c = n.i64, .l = NULL, .nbytes = 0}; } al = "%al" { $$ = REG(ASM_AL); } cl = "%cl" { $$ = REG(ASM_CL); } @@ -422,9 +422,7 @@ static void assemblereloc(const char *l, int64_t c, int nbytes, int type) { /* Assemble a r <-> (%rip) operation. */ static void assembleriprel(Memarg *memarg, uint8_t rexw, Opcode opcode, uint8_t reg, uint8_t opsz) { - assemblemodregrm(rexw, opcode, 0x00, reg, 0x05, opsz); - if (memarg->l) { assemblereloc(memarg->l, memarg->c - 4, 4, R_X86_64_PC32); } else { @@ -435,14 +433,15 @@ static void assembleriprel(Memarg *memarg, uint8_t rexw, Opcode opcode, /* Assemble a r <-> mem operation. */ static void assemblemem(Memarg *memarg, uint8_t rexw, Opcode opcode, uint8_t reg, uint8_t opsz) { - uint8_t rex, rm, sib; - if (memarg->reg == ASM_RIP) { + uint8_t rex, mod, rm, scale, index, base, sib; + + if (memarg->base == ASM_RIP) { assembleriprel(memarg, rexw, opcode, reg, opsz); return; } - rm = regbits(memarg->reg); + rm = regbits(memarg->base); if (opsz == 2) sb(0x66); rex = rexbyte(rexw, reg & (1 << 3), 0, rm & (1 << 3)); @@ -451,26 +450,79 @@ static void assemblemem(Memarg *memarg, uint8_t rexw, Opcode opcode, assembleopcode(opcode); - if (memarg->c == 0 && memarg->l == NULL) { - /* No offset cases, uses the smallest we can. */ - if ((rm & 7) == 4) { /* Matches '(%rsp/%esp...)'. */ - sb2(modregrm(0, reg, 4), sibbyte(0, 4, 4)); - } else if ((rm & 7) == 5) { /* Matches '(%rbp/%ebp...)'. */ - sb2(modregrm(1, reg, 5), 0); + /* Case when we don't need sib */ + if (memarg->index == ASM_NO_REG && memarg->scale == 0 && ((rm & 7) != 4)) { + + if (memarg->l == 0 && memarg->c == 0) { + if ((rm & 7) == 5) { + mod = 1; + } else { + mod = 0; + } } else { - sb(modregrm(0, reg, rm)); + mod = 2; + } + + sb(modregrm(mod, reg, rm)); + + if (mod == 1) { + assemblereloc(memarg->l, memarg->c, 1, R_X86_64_32); + } else if (mod == 2) { + assemblereloc(memarg->l, memarg->c, 4, R_X86_64_32); } + return; + } + + // TODO: if our disp fits in a +disp8, use that instead. + if (memarg->c == 0 && memarg->l == 0) + mod = 0; /* +0 */ + else + mod = 2; /* +disp32 */ + + /* Setup sib indexing. */ + base = rm; + rm = 4; + + if (memarg->index == ASM_NO_REG) { + index = 4; } else { - /* TODO choose smaller size if not label .*/ - if ((rm & 7) == 4) { /* Matches '(%rsp/%esp...)'. */ - sb2(modregrm(2, reg, 4), sibbyte(0, 4, 4)); - } else if ((rm & 7) == 5) { /* Matches '(%rbp/%ebp...)'. */ - sb(modregrm(2, reg, 5)); + index = regbits(memarg->index); + if ((index & 7) == 4) + lfatal("sp cannot be used as an index"); + } + + /* If our base is a bp register, we must use the index instead. */ + if ((base & 7) == 5) { + if (memarg->index == ASM_NO_REG) { + index = base; } else { - sb(modregrm(2, reg, rm)); + lfatal("bp cannot be used as an addressing base"); } - assemblereloc(memarg->l, memarg->c, 4, R_X86_64_32); } + + switch (memarg->scale) { + case 0: + case 1: + scale = 0; + break; + case 2: + scale = 1; + break; + case 4: + scale = 2; + break; + case 8: + scale = 3; + break; + default: + lfatal("invalid addressing scale"); + } + + sb2(modregrm(mod, reg, rm), sibbyte(scale, index, base)); + + /* If mod is set, or we are indexing bp we must output a displacement. */ + if (mod || ((base & 7) == 5)) + assemblereloc(memarg->l, memarg->c, 4, R_X86_64_32); } /* Assemble op + imm -> r/m. */ @@ -845,8 +897,7 @@ static void assemble(void) { }; opcode = 0x01000f00 | variant2op[v->instr.variant % 31]; if (v->instr.arg1->kind == ASM_MEMARG) { - assemblemem(&v->instr.arg1->memarg, 0, opcode, - regbits(v->instr.arg1->memarg.reg), 1); + assemblemem(&v->instr.arg1->memarg, 0, opcode, 0, 1); } else { assemblemodregrm(isreg64(v->instr.arg1->kind), opcode, 0x03, 0x00, regbits(v->instr.arg1->kind), 1); @@ -161,6 +161,7 @@ typedef enum { /* RIP is in a special class of its own. */ ASM_RIP, + ASM_NO_REG, ASM_REG_END, } AsmKind; @@ -213,7 +214,9 @@ typedef struct { typedef struct { AsmKind kind; - AsmKind reg; + AsmKind base; + AsmKind index; + uint8_t scale; const char *l; /* label */ int64_t c; /* constant */ } Memarg; |
