aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--asm.peg25
-rw-r--r--main.c93
-rw-r--r--minias.h5
3 files changed, 93 insertions, 30 deletions
diff --git a/asm.peg b/asm.peg
index 1392fdd..6bd3553 100644
--- a/asm.peg
+++ b/asm.peg
@@ -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); }
diff --git a/main.c b/main.c
index d472e08..c3dbcb5 100644
--- a/main.c
+++ b/main.c
@@ -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);
diff --git a/minias.h b/minias.h
index 02b2b77..abf1f06 100644
--- a/minias.h
+++ b/minias.h
@@ -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;