diff options
| -rw-r--r-- | asm.peg | 22 | ||||
| -rw-r--r-- | main.c | 121 | ||||
| -rw-r--r-- | test/test.sh | 6 |
3 files changed, 104 insertions, 45 deletions
@@ -97,7 +97,7 @@ lea = ) { $$.instr.kind = ASM_LEA; } div = "div" ( - args:divmul-opargs { $$ = args; } + args:divmul-opargs { $$ = args; } ) { $$.instr.kind = ASM_DIV; } idiv = "idiv" ( @@ -105,18 +105,26 @@ idiv = "idiv" ( ) { $$.instr.kind = ASM_IDIV; } mul = "mul" ( - args:divmul-opargs { $$ = args; } + args:divmul-opargs { $$ = args; } ) { $$.instr.kind = ASM_MUL; } imul = "imul" ( - args:divmul-opargs { $$ = args; } + ( + 'w'? ws s:m ws? ',' ws? d:r16 { $$ = INSTR2(8, s, d); } + | 'l'? ws s:m ws? ',' ws? d:r32 { $$ = INSTR2(9, s, d); } + | 'q'? ws s:m ws? ',' ws? d:r64 { $$ = INSTR2(10, s, d); } + | 'w'? ws s:r16 ws? ',' ws? d:r16 { $$ = INSTR2(11, s, d); } + | 'l'? ws s:r32 ws? ',' ws? d:r32 { $$ = INSTR2(12, s, d); } + | 'q'? ws s:r64 ws? ',' ws? d:r64 { $$ = INSTR2(13, s, d); } + ) + | args:divmul-opargs { $$ = args; } # Must come last due to peg ordering. ) { $$.instr.kind = ASM_IMUL; } divmul-opargs = - 'b' ws a:m { $$ = INSTR1(0, a); } - | 'w' ws a:m { $$ = INSTR1(1, a); } - | 'l' ws a:m { $$ = INSTR1(2, a); } - | 'q' ws a:m { $$ = INSTR1(3, a); } + 'b' ws a:m { $$ = INSTR1(0, a); } + | 'w' ws a:m { $$ = INSTR1(1, a); } + | 'l' ws a:m { $$ = INSTR1(2, a); } + | 'q' ws a:m { $$ = INSTR1(3, a); } | 'b'? ws a:r8 { $$ = INSTR1(4, a); } | 'w'? ws a:r16 { $$ = INSTR1(5, a); } | 'l'? ws a:r32 { $$ = INSTR1(6, a); } @@ -320,17 +320,6 @@ static uint8_t sibbyte(uint8_t ss, uint8_t idx, uint8_t base) { return (((ss & 3) << 6) | ((idx & 7) << 3) | (base & 7)); } -/* Assemble op +rw | op + rd. */ -static void assembleplusr(uint8_t opcode, uint8_t rexw, AsmKind reg) { - uint8_t bits = regbits(reg); - uint8_t rex = rexbyte(rexw, 0, 0, bits & (1 << 3)); - if (isreg16(reg)) - sb(0x66); - if (rex != rexbyte(0, 0, 0, 0)) - sb(rex); - sb(opcode | (bits & 7)); -} - void assembleconstant(int64_t c, int nbytes) { switch (nbytes) { case 1: @@ -350,7 +339,44 @@ void assembleconstant(int64_t c, int nbytes) { } } -static void assemblemodregrm(uint8_t rexw, uint8_t opcode, uint8_t mod, +/* The Opcode type encodes a variadic opcode. + + The top byte is how many bytes we want less 1. + + The encoding is chosen such that the common case is + just the opcode value, the multibyte case is a bit more + complex. + + example: + + 02 03 encodes as 0x01000203 +*/ +typedef uint32_t Opcode; + +static void assembleopcode(Opcode opcode) { + int i, n; + uint8_t b, shift; + + n = ((opcode & 0xff000000) >> 24); + for (i = n; i >= 0; i--) { + shift = i * 8; + b = (opcode & (0xff << shift)) >> shift; + sb(b); + } +} + +/* Assemble op +rw | op + rd. */ +static void assembleplusr(Opcode opcode, uint8_t rexw, AsmKind reg) { + uint8_t bits = regbits(reg); + uint8_t rex = rexbyte(rexw, 0, 0, bits & (1 << 3)); + if (isreg16(reg)) + sb(0x66); + if (rex != rexbyte(0, 0, 0, 0)) + sb(rex); + assembleopcode(opcode | (bits & 7)); +} + +static void assemblemodregrm(uint8_t rexw, Opcode opcode, uint8_t mod, uint8_t reg, uint8_t rm, uint8_t opsz) { uint8_t rex; @@ -359,7 +385,8 @@ static void assemblemodregrm(uint8_t rexw, uint8_t opcode, uint8_t mod, rex = rexbyte(rexw, reg & (1 << 3), 0, rm & (1 << 3)); if (rex != rexbyte(0, 0, 0, 0)) sb(rex); - sb2(opcode, modregrm(mod, reg, rm)); + assembleopcode(opcode); + sb(modregrm(mod, reg, rm)); } /* Assemble a symbolic value. */ @@ -379,7 +406,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, uint8_t opcode, +static void assembleriprel(Memarg *memarg, uint8_t rexw, Opcode opcode, uint8_t reg, uint8_t opsz) { assemblemodregrm(rexw, opcode, 0x00, reg, 0x05, opsz); @@ -392,7 +419,7 @@ static void assembleriprel(Memarg *memarg, uint8_t rexw, uint8_t opcode, } /* Assemble a r <-> mem operation. */ -static void assemblemem(Memarg *memarg, uint8_t rexw, uint8_t opcode, +static void assemblemem(Memarg *memarg, uint8_t rexw, Opcode opcode, uint8_t reg, uint8_t opsz) { uint8_t rex, rm, sib; @@ -408,30 +435,32 @@ static void assemblemem(Memarg *memarg, uint8_t rexw, uint8_t opcode, if (rex != rexbyte(0, 0, 0, 0)) sb(rex); + assembleopcode(opcode); + if (memarg->c == 0 && memarg->l == NULL) { /* No offset cases, uses the smallest we can. */ if ((rm & 7) == 4) { /* Matches '(%rsp/%esp...)'. */ - sb3(opcode, modregrm(0, reg, 4), sibbyte(0, 4, 4)); + sb2(modregrm(0, reg, 4), sibbyte(0, 4, 4)); } else if ((rm & 7) == 5) { /* Matches '(%rbp/%ebp...)'. */ - sb3(opcode, modregrm(1, reg, 5), 0); + sb2(modregrm(1, reg, 5), 0); } else { - sb2(opcode, modregrm(0, reg, rm)); + sb(modregrm(0, reg, rm)); } } else { /* TODO choose smaller size if not label .*/ if ((rm & 7) == 4) { /* Matches '(%rsp/%esp...)'. */ - sb3(opcode, modregrm(2, reg, 4), sibbyte(0, 4, 4)); + sb2(modregrm(2, reg, 4), sibbyte(0, 4, 4)); } else if ((rm & 7) == 5) { /* Matches '(%rbp/%ebp...)'. */ - sb2(opcode, modregrm(2, reg, 5)); + sb(modregrm(2, reg, 5)); } else { - sb2(opcode, modregrm(2, reg, rm)); + sb(modregrm(2, reg, rm)); } assemblereloc(memarg->l, memarg->c, 4, R_X86_64_32); } } /* Assemble op + imm -> r/m. */ -static void assembleimmrm(Instr *instr, uint8_t opcode, uint8_t immreg, +static void assembleimmrm(Instr *instr, Opcode opcode, uint8_t immreg, uint8_t opsz) { Imm *imm; @@ -449,7 +478,7 @@ static void assembleimmrm(Instr *instr, uint8_t opcode, uint8_t immreg, } /* Assemble op + r <-> r/m. */ -static void assemblerrm(Instr *instr, uint8_t opcode, uint8_t opsz) { +static void assemblerrm(Instr *instr, Opcode opcode, uint8_t opsz) { Memarg *memarg; AsmKind regarg; @@ -469,7 +498,7 @@ static void assemblerrm(Instr *instr, uint8_t opcode, uint8_t opsz) { } /* Assemble a 'basic op' which is just a repeated op pattern we have named. */ -static void assemblebasicop(Instr *instr, uint8_t opcode, uint8_t immreg) { +static void assemblebasicop(Instr *instr, Opcode opcode, uint8_t immreg) { Imm *imm; uint8_t opsz = 1 << (instr->variant % 4); if (instr->variant < 4) { @@ -477,7 +506,7 @@ static void assemblebasicop(Instr *instr, uint8_t opcode, uint8_t immreg) { sb(0x66); if (instr->arg2->kind == ASM_RAX) sb(rexbyte(1, 0, 0, 0)); - sb(opcode); + assembleopcode(opcode); imm = &instr->arg1->imm; assemblereloc(imm->l, imm->c, imm->nbytes, R_X86_64_32); } else if (instr->variant < 12) { @@ -503,7 +532,8 @@ static void assemblexchg(Instr *xchg) { static void assemblemov(Instr *mov) { Imm *imm; - uint8_t opcode, rex, mod, rm; + Opcode opcode; + uint8_t rex, mod, rm; static uint8_t variant2op[20] = { 0xc6, 0xc7, 0xc7, 0xc7, 0xb0, 0xb8, 0xb8, 0xc7, 0x8a, 0x8b, @@ -524,17 +554,17 @@ static void assemblemov(Instr *mov) { } } -static void assembledivmul(Instr *div, uint8_t reg) { - uint8_t opcode, opsz, rex, mod, rm; +static void assembledivmul(Instr *instr, uint8_t reg, uint8_t opsz) { + Opcode opcode; + uint8_t rex, mod, rm; - opsz = 1 << (div->variant % 4); opcode = opsz == 1 ? 0xf6 : 0xf7; - if (div->variant < 4) { - assemblemem(&div->arg1->memarg, opsz == 8, opcode, reg, opsz); + if (instr->variant < 4) { + assemblemem(&instr->arg1->memarg, opsz == 8, opcode, reg, opsz); } else { - assemblemodregrm(isreg64(div->arg1->kind), opcode, 0x03, reg, - regbits(div->arg1->kind), opsz); + assemblemodregrm(isreg64(instr->arg1->kind), opcode, 0x03, reg, + regbits(instr->arg1->kind), opsz); } } @@ -680,22 +710,37 @@ static void assemble(void) { break; } case ASM_DIV: { - assembledivmul(&v->instr, 0x06); + uint8_t opsz; + opsz = 1 << (v->instr.variant % 4); + assembledivmul(&v->instr, 0x06, opsz); break; } case ASM_IDIV: { - assembledivmul(&v->instr, 0x07); + uint8_t opsz; + opsz = 1 << (v->instr.variant % 4); + assembledivmul(&v->instr, 0x07, opsz); break; } case ASM_MUL: { - assembledivmul(&v->instr, 0x04); + uint8_t opsz; + opsz = 1 << (v->instr.variant % 4); + assembledivmul(&v->instr, 0x04, opsz); break; } case ASM_IMUL: { + Opcode opcode; + uint8_t opsz; + if (v->instr.variant < 8) { - assembledivmul(&v->instr, 0x05); + uint8_t opsz; + opsz = 1 << (v->instr.variant % 4); + assembledivmul(&v->instr, 0x05, opsz); + } else if (v->instr.variant < 14) { + opcode = 0x01000faf; // 0f af variable length opcode. + opsz = 1 << (1 + ((v->instr.variant - 8) % 3)); + assemblerrm(&v->instr, opcode, opsz); } else { - fatal("TODO..."); + unreachable(); } break; } diff --git a/test/test.sh b/test/test.sh index 71e13b5..7251e76 100644 --- a/test/test.sh +++ b/test/test.sh @@ -39,10 +39,16 @@ t "idivq (%rip)" t "mul %rax" t "mulq (%rax)" t "mulq (%rip)" + t "imul %rax" t "imulq (%rax)" t "imulq (%rip)" +t "imul %rax, %rax" +t "imulq (%rax), %rax" +t "imul %eax, %eax" +t "imull (%rax), %eax" +t "imul %eax, %eax" t "pushq (%r9)" t "pushq %r9" |
