line = ws? s:stmt { yy->v = s; } | eol { yy->v.kind = ASM_BLANK; } | . { yy->v.kind = ASM_SYNTAX_ERROR; } ws = ([ \t]+ | comment)+ comment = "/*" ( ! "\n" ! "*/" . )* "*/" # No support for multiline comments for now as they break our line numbers. eolcomment = '#' (! "\n" .)+ eol = ws? eolcomment? "\n" stmt = '.' d:directive eol {$$ = d;} | i:instr eol { $$ = i; } | l:label eol { $$ = l; } directive = "glob" 'o'? 'l' ws i:ident { $$.globl = (Globl){.kind = ASM_DIR_GLOBL, .name = i.charptr }; } | "ascii" <'z'?> ws s:string { s.kind = *yytext ? ASM_DIR_ASCII : ASM_DIR_ASCIIZ ; $$ = s; } | "data" { $$.kind = ASM_DIR_DATA; } | "text" { $$.kind = ASM_DIR_TEXT; } | "balign" ws n:number { $$.balign = (Balign){.kind = ASM_DIR_BALIGN, .align = n.i64 }; } | "byte" ws v:value { $$.dirbyte = (Byte){.kind = ASM_DIR_BYTE, .value = v.value }; } | "short" ws v:value { $$.dirshort = (Short){.kind = ASM_DIR_SHORT, .value = v.value }; } | "int" ws v:value { $$.dirint = (Int){.kind = ASM_DIR_INT, .value = v.value }; } | "quad" ws v:value { $$.dirquad = (Quad){.kind = ASM_DIR_QUAD, .value = v.value }; } | fd:fill-directive { $$ = fd; } | sd:section-directive { $$ = sd; } fill-directive = "fill" ws r:number ws? "," ws? s:number ws? "," ws? v:number { $$.fill = (Fill){ .kind=ASM_DIR_FILL, .repeat = r.i64, .size = s.i64, .value = v.i64 }; } section-directive = "section" ws? n:section-name ( ws? ',' ws? f:section-flags ws? ',' ws? t:section-type {$$.section = (DirSection){.kind=ASM_DIR_SECTION, .name=n.charptr, .flags=f.charptr, .type=t.i64}; } | ws? ',' ws? f:section-flags {$$.section = (DirSection){.kind=ASM_DIR_SECTION, .name=n.charptr, .flags=f.charptr, .type=SHT_PROGBITS}; } | {$$.section = (DirSection){.kind=ASM_DIR_SECTION, .name=n.charptr, .flags="", .type=SHT_PROGBITS}; } ) section-name = <[.a-zA-Z0-9\-]+> { $$.charptr = internstring(yytext); } section-flags = '"' <[awx]*> '"' { $$.charptr = internstring(yytext); } section-type = "@nobits" { $$.i64 = SHT_NOBITS; } | "@progbits" { $$.i64 = SHT_PROGBITS; } label = i:ident ':' { $$.label = (Label){.kind = ASM_LABEL, .name = i.charptr}; } instr = # Ordered by instruction frequency for performance. # e.g. movs are very common, so they come first. # The & operator means check without consuming input. (& 'm' ( i:mov { $$ = i; } | i:movsx { $$ = i; } | i:movzx { $$ = i; } | i:mul { $$ = i; } | i:movaps { $$ = i; } | i:movq { $$ = i; } | i:movsd { $$ = i; } | i:movss { $$ = i; } | i:mulsd { $$ = i; } | i:mulss { $$ = i; })) | (& 'a' ( i:add { $$ = i; } | i:and { $$ = i; } | i:addss { $$ = i; } | i:addsd { $$ = i; } )) | (& 'c' ( i:cmp { $$ = i; } | i:call { $$ = i; } | i:cvtsi2sd { $$ = i; } | i:cvtsi2ss { $$ = i; } | i:cvtss2sd { $$ = i; } | i:cvtsd2ss { $$ = i; } | i:cvttsd2si { $$ = i; } | i:cvttss2si { $$ = i; } | i:cltd { $$ = i; } | i:cqto { $$ = i; })) | (& 's' ( i:set { $$ = i; } | i:sub { $$ = i; } | i:sal { $$ = i; } | i:sar { $$ = i; } | i:shl { $$ = i; } | i:shr { $$ = i; } | i:subsd { $$ = i; } | i:subss { $$ = i; })) | i:or { $$ = i; } | i:leave { $$ = i; } | i:ret { $$ = i; } | i:push { $$ = i; } | i:pop { $$ = i; } | i:jmp { $$ = i; } | i:div { $$ = i; } | i:idiv { $$ = i; } | i:lea { $$ = i; } | i:imul { $$ = i; } | i:neg { $$ = i; } | i:test { $$ = i; } | i:xchg { $$ = i; } | i:xor { $$ = i; } # Floating point is less common, so check last. | i:divss { $$ = i; } | i:divsd { $$ = i; } | i:pxor { $$ = i; } | i:xorpd { $$ = i; } | i:xorps { $$ = i; } | i:ucomisd { $$ = i; } | i:ucomiss { $$ = i; } | i:nop { $$ = i; } cltd = "cltd" { $$ = (Parsev){ .kind=ASM_CLTD }; } cqto = "cqto" { $$ = (Parsev){ .kind=ASM_CQTO }; } leave = "leave" { $$ = (Parsev){ .kind=ASM_LEAVE }; } nop = "nop" { $$ = (Parsev){ .kind=ASM_NOP }; } ret = "ret" { $$ = (Parsev){ .kind=ASM_RET }; } push = "push" ( 'q'? ws s:r64 { $$ = INSTR1(0, s); } | 'q' ws s:mem { $$ = INSTR1(1, s); } ) { $$.instr.kind = ASM_PUSH; } pop = "pop" ( 'q'? ws d:r64 { $$ = INSTR1(0, d); } | 'q' ws d:mem { $$ = INSTR1(1, d); } ) { $$.instr.kind = ASM_POP; } call = "call" 'q'? ws ( '*' t:mem { $$.call = (Call){ .kind = ASM_CALL, .target.indirect=internparsev(&t), .indirect=1 } ; } | '*' t:r64 { $$.call = (Call){ .kind = ASM_CALL, .target.indirect=internparsev(&t), .indirect=1 } ; } | t:value { $$.call = (Call){ .kind = ASM_CALL, .target.direct=t.value, .indirect=0 } ; } ) jmp = 'j' v:jmp-variant ws t:ident { $$.jmp = (Jmp) {.kind = ASM_JMP, .variant=v.i64, .target=t.charptr}; } jmp-variant = "mp" { $$.i64 = 0; } | cc:condition-code { $$.i64 = cc.i64 + 1;} condition-code = "z" { $$.i64 = 0; } | "s" { $$.i64 = 1; } | "po" { $$.i64 = 2; } | "pe" { $$.i64 = 3; } | "p" { $$.i64 = 4; } | "o" { $$.i64 = 5; } | ("n" ( "z" { $$.i64 = 6; } | "s" { $$.i64 = 7; } | "p" { $$.i64 = 8; } | "o" { $$.i64 = 9; } | "le" { $$.i64 = 10; } | "l" { $$.i64 = 11; } | "ge" { $$.i64 = 12; } | "g" { $$.i64 = 13; } | "e" { $$.i64 = 14; } | "c" { $$.i64 = 15; } | "be" { $$.i64 = 16; } | "b" { $$.i64 = 17; } | "ae" { $$.i64 = 18; } | "a" { $$.i64 = 19; })) | "le" { $$.i64 = 20; } | "l" { $$.i64 = 21; } | "ge" { $$.i64 = 22; } | "g" { $$.i64 = 23; } | "e" { $$.i64 = 24; } | "c" { $$.i64 = 25; } | "be" { $$.i64 = 26; } | "b" { $$.i64 = 27; } | "ae" { $$.i64 = 28; } | "a" { $$.i64 = 29; } div = "div" ( 'b' ws a:mem { $$ = INSTR1(0, a); } | 'w' ws a:mem { $$ = INSTR1(1, a); } | 'l' ws a:mem { $$ = INSTR1(2, a); } | 'q' ws a:mem { $$ = INSTR1(3, a); } | 'b'? ws a:r8 { $$ = INSTR1(4, a); } | 'w'? ws a:r16 { $$ = INSTR1(5, a); } | 'l'? ws a:r32 { $$ = INSTR1(6, a); } | 'q'? ws a:r64 { $$ = INSTR1(7, a); } ) { $$.instr.kind = ASM_DIV; } idiv = "idiv" ( 'b' ws a:mem { $$ = INSTR1(0, a); } | 'w' ws a:mem { $$ = INSTR1(1, a); } | 'l' ws a:mem { $$ = INSTR1(2, a); } | 'q' ws a:mem { $$ = INSTR1(3, a); } | 'b'? ws a:r8 { $$ = INSTR1(4, a); } | 'w'? ws a:r16 { $$ = INSTR1(5, a); } | 'l'? ws a:r32 { $$ = INSTR1(6, a); } | 'q'? ws a:r64 { $$ = INSTR1(7, a); } ) { $$.instr.kind = ASM_IDIV; } mul = "mul" ( 'b' ws a:mem { $$ = INSTR1(0, a); } | 'w' ws a:mem { $$ = INSTR1(1, a); } | 'l' ws a:mem { $$ = INSTR1(2, a); } | 'q' ws a:mem { $$ = INSTR1(3, a); } | 'b'? ws a:r8 { $$ = INSTR1(4, a); } | 'w'? ws a:r16 { $$ = INSTR1(5, a); } | 'l'? ws a:r32 { $$ = INSTR1(6, a); } | 'q'? ws a:r64 { $$ = INSTR1(7, a); } ) { $$.instr.kind = ASM_MUL; } neg = "neg" ( 'b' ws a:mem { $$ = INSTR1(0, a); } | 'w' ws a:mem { $$ = INSTR1(1, a); } | 'l' ws a:mem { $$ = INSTR1(2, a); } | 'q' ws a:mem { $$ = INSTR1(3, a); } | 'b'? ws a:r8 { $$ = INSTR1(4, a); } | 'w'? ws a:r16 { $$ = INSTR1(5, a); } | 'l'? ws a:r32 { $$ = INSTR1(6, a); } | 'q'? ws a:r64 { $$ = INSTR1(7, a); } ) { $$.instr.kind = ASM_NEG; } imul = "imul" ( 'w'? ws s:mem ws? ',' ws? d:r16 { $$ = INSTR2(8, s, d); } | 'l'? ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(9, s, d); } | 'q'? ws s:mem 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); } | 'w'? ws i:imm16 ws? ',' ws? s:mem ws? ',' ws? d:r16 { $$ = INSTR3(14, s, d, i); } | 'l'? ws i:imm32 ws? ',' ws? s:mem ws? ',' ws? d:r32 { $$ = INSTR3(15, s, d, i); } | 'q'? ws i:imm32 ws? ',' ws? s:mem ws? ',' ws? d:r64 { $$ = INSTR3(16, s, d, i); } | 'w'? ws i:imm16 ws? ',' ws? s:r16 ws? ',' ws? d:r16 { $$ = INSTR3(17, s, d, i); } | 'l'? ws i:imm32 ws? ',' ws? s:r32 ws? ',' ws? d:r32 { $$ = INSTR3(18, s, d, i); } | 'q'? ws i:imm32 ws? ',' ws? s:r64 ws? ',' ws? d:r64 { $$ = INSTR3(19, s, d, i); } # Must come last due to peg ordering. | 'b' ws a:mem { $$ = INSTR1(0, a); } | 'w' ws a:mem { $$ = INSTR1(1, a); } | 'l' ws a:mem { $$ = INSTR1(2, a); } | 'q' ws a:mem { $$ = INSTR1(3, a); } | 'b'? ws a:r8 { $$ = INSTR1(4, a); } | 'w'? ws a:r16 { $$ = INSTR1(5, a); } | 'l'? ws a:r32 { $$ = INSTR1(6, a); } | 'q'? ws a:r64 { $$ = INSTR1(7, a); } ) { $$.instr.kind = ASM_IMUL; } lea = "lea" ( 'w'? ws s:mem ws? ',' ws? d:r16 { $$ = INSTR2(0, s, d); } | 'l'? ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(1, s, d); } | 'q'? ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(2, s, d); } ) { $$.instr.kind = ASM_LEA; } mov = "mov" ( 'b'? ws s:r8 ws? ',' ws? d:r8 { $$ = INSTR2(0, s, d); } | 'w'? ws s:r16 ws? ',' ws? d:r16 { $$ = INSTR2(1, s, d); } | 'l'? ws s:r32 ws? ',' ws? d:r32 { $$ = INSTR2(2, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:r64 { $$ = INSTR2(3, s, d); } | 'b'? ws s:r8 ws? ',' ws? d:mem { $$ = INSTR2(4, s, d); } | 'w'? ws s:r16 ws? ',' ws? d:mem { $$ = INSTR2(5, s, d); } | 'l'? ws s:r32 ws? ',' ws? d:mem { $$ = INSTR2(6, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:mem { $$ = INSTR2(7, s, d); } | 'b'? ws s:mem ws? ',' ws? d:r8 { $$ = INSTR2(8, s, d); } | 'w'? ws s:mem ws? ',' ws? d:r16 { $$ = INSTR2(9, s, d); } | 'l'? ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(10, s, d); } | 'q'? ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(11, s, d); } | 'b' ws s:imm8 ws? ',' ws? d:mem { $$ = INSTR2(12, s, d); } | 'w' ws s:imm16 ws? ',' ws? d:mem { $$ = INSTR2(13, s, d); } | 'l' ws s:imm32 ws? ',' ws? d:mem { $$ = INSTR2(14, s, d); } | 'q' ws s:imm32 ws? ',' ws? d:mem { $$ = INSTR2(15, s, d); } | 'b'? ws s:imm8 ws? ',' ws? d:r8 { $$ = INSTR2(16, s, d); } | 'w'? ws s:imm16 ws? ',' ws? d:r16 { $$ = INSTR2(17, s, d); } | 'l'? ws s:imm32 ws? ',' ws? d:r32 { $$ = INSTR2(18, s, d); } | 'q'? ws s:imm ws? ',' ws? d:r64 { $$ = INSTR2(19, s, d); } ) { $$.instr.kind = ASM_MOV; } movsx = "movs" ( 'bw' ws s:mem ws? ',' ws? d:r16 { $$ = INSTR2(0, s, d); } | 'bl' ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(1, s, d); } | 'bq' ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(2, s, d); } | 'wl' ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(3, s, d); } | 'wq' ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(4, s, d); } | 'bw' ws s:r8 ws? ',' ws? d:r16 { $$ = INSTR2(5, s, d); } | 'bl' ws s:r8 ws? ',' ws? d:r32 { $$ = INSTR2(6, s, d); } | 'bq' ws s:r8 ws? ',' ws? d:r64 { $$ = INSTR2(7, s, d); } | 'wl' ws s:r16 ws? ',' ws? d:r32 { $$ = INSTR2(8, s, d); } | 'wq' ws s:r16 ws? ',' ws? d:r64 { $$ = INSTR2(9, s, d); } | 'lq' ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(10, s, d); } | 'lq' ws s:r32 ws? ',' ws? d:r64 { $$ = INSTR2(11, s, d); } ) { $$.instr.kind = ASM_MOVSX; } movzx = "movz" ( 'bw' ws s:mem ws? ',' ws? d:r16 { $$ = INSTR2(0, s, d); } | 'bl' ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(1, s, d); } | 'bq' ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(2, s, d); } | 'wl' ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(3, s, d); } | 'wq' ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(4, s, d); } | 'bw' ws s:r8 ws? ',' ws? d:r16 { $$ = INSTR2(5, s, d); } | 'bl' ws s:r8 ws? ',' ws? d:r32 { $$ = INSTR2(6, s, d); } | 'bq' ws s:r8 ws? ',' ws? d:r64 { $$ = INSTR2(7, s, d); } | 'wl' ws s:r16 ws? ',' ws? d:r32 { $$ = INSTR2(8, s, d); } | 'wq' ws s:r16 ws? ',' ws? d:r64 { $$ = INSTR2(9, s, d); } ) { $$.instr.kind = ASM_MOVZX; } xchg = 'xchg' ( 'w'? ws s:ax ws? ',' ws? d:r16 { $$ = INSTR2(0, s, d); } | 'w'? ws s:r16 ws? ',' ws? d:ax { $$ = INSTR2(1, s, d); } | 'l'? ws s:eax ws? ',' ws? d:r32 { $$ = INSTR2(2, s, d); } | 'l'? ws s:r32 ws? ',' ws? d:eax { $$ = INSTR2(3, s, d); } | 'q'? ws s:rax ws? ',' ws? d:r64 { $$ = INSTR2(4, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:rax { $$ = INSTR2(5, s, d); } | 'b'? ws s:mem ws? ',' ws? d:r8 { $$ = INSTR2(6, s, d); } | 'w'? ws s:mem ws? ',' ws? d:r16 { $$ = INSTR2(7, s, d); } | 'l'? ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(8, s, d); } | 'q'? ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(9, s, d); } | 'b'? ws s:r8 ws? ',' ws? d:mem { $$ = INSTR2(10, s, d); } | 'w'? ws s:r16 ws? ',' ws? d:mem { $$ = INSTR2(11, s, d); } | 'l'? ws s:r32 ws? ',' ws? d:mem { $$ = INSTR2(12, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:mem { $$ = INSTR2(13, s, d); } | 'b'? ws s:r8 ws? ',' ws? d:r8 { $$ = INSTR2(14, s, d); } | 'w'? ws s:r16 ws? ',' ws? d:r16 { $$ = INSTR2(15, s, d); } | 'l'? ws s:r32 ws? ',' ws? d:r32 { $$ = INSTR2(16, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:r64 { $$ = INSTR2(17, s, d); } ) { $$.instr.kind = ASM_XCHG; } add = "add" a:basic-op-args { a.instr.kind = ASM_ADD; $$ = a; } cmp = "cmp" a:basic-op-args { a.instr.kind = ASM_CMP; $$ = a; } and = "and" a:basic-op-args { a.instr.kind = ASM_AND; $$ = a; } or = "or" a:basic-op-args { a.instr.kind = ASM_OR; $$ = a; } sub = "sub" a:basic-op-args { a.instr.kind = ASM_SUB; $$ = a; } xor = "xor" a:basic-op-args { a.instr.kind = ASM_XOR; $$ = a; } basic-op-args = 'b'? ws s:imm8 ws? ',' ws? d:al { $$ = INSTR2(0, s, d); } | 'w'? ws s:imm16 ws? ',' ws? d:ax { $$ = INSTR2(1, s, d); } | 'l'? ws s:imm32 ws? ',' ws? d:eax { $$ = INSTR2(2, s, d); } | 'q'? ws s:imm32 ws? ',' ws? d:rax { $$ = INSTR2(3, s, d); } | 'b' ws s:imm8 ws? ',' ws? d:mem { $$ = INSTR2(4, s, d); } | 'w' ws s:imm16 ws? ',' ws? d:mem { $$ = INSTR2(5, s, d); } | 'l' ws s:imm32 ws? ',' ws? d:mem { $$ = INSTR2(6, s, d); } | 'q' ws s:imm32 ws? ',' ws? d:mem { $$ = INSTR2(7, s, d); } | 'b'? ws s:imm8 ws? ',' ws? d:r8 { $$ = INSTR2(8, s, d); } | 'w'? ws s:imm16 ws? ',' ws? d:r16 { $$ = INSTR2(9, s, d); } | 'l'? ws s:imm32 ws? ',' ws? d:r32 { $$ = INSTR2(10, s, d); } | 'q'? ws s:imm32 ws? ',' ws? d:r64 { $$ = INSTR2(11, s, d); } | 'b'? ws s:mem ws? ',' ws? d:r8 { $$ = INSTR2(12, s, d); } | 'w'? ws s:mem ws? ',' ws? d:r16 { $$ = INSTR2(13, s, d); } | 'l'? ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(14, s, d); } | 'q'? ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(15, s, d); } | 'b'? ws s:r8 ws? ',' ws? d:mem { $$ = INSTR2(16, s, d); } | 'w'? ws s:r16 ws? ',' ws? d:mem { $$ = INSTR2(17, s, d); } | 'l'? ws s:r32 ws? ',' ws? d:mem { $$ = INSTR2(18, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:mem { $$ = INSTR2(19, s, d); } | 'b'? ws s:r8 ws? ',' ws? d:r8 { $$ = INSTR2(20, s, d); } | 'w'? ws s:r16 ws? ',' ws? d:r16 { $$ = INSTR2(21, s, d); } | 'l'? ws s:r32 ws? ',' ws? d:r32 { $$ = INSTR2(22, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:r64 { $$ = INSTR2(23, s, d); } set = "set" cc:condition-code ( 'b'? ws a:mem{ $$ = INSTR1(0, a); $$.instr.variant = cc.i64; } | 'b'? ws a:r8 { $$ = INSTR1(0, a); $$.instr.variant = 31 + cc.i64; } ) { $$.instr.kind = ASM_SET } sal = "sal" args:shift-args {$$ = args; $$.instr.kind = ASM_SAL} sar = "sar" args:shift-args {$$ = args; $$.instr.kind = ASM_SAR} shl = "shl" args:shift-args {$$ = args; $$.instr.kind = ASM_SHL} shr = "shr" args:shift-args {$$ = args; $$.instr.kind = ASM_SHR} shift-args = 'w' ws c:cl ws? ',' ws? d:mem { $$ = INSTR2(0, c, d); } | 'l' ws c:cl ws? ',' ws? d:mem { $$ = INSTR2(1, c, d); } | 'q' ws c:cl ws? ',' ws? d:mem { $$ = INSTR2(2, c, d); } | 'w'? ws c:cl ws? ',' ws? d:r16 { $$ = INSTR2(3, c, d); } | 'l'? ws c:cl ws? ',' ws? d:r32 { $$ = INSTR2(4, c, d); } | 'q'? ws c:cl ws? ',' ws? d:r64 { $$ = INSTR2(5, c, d); } | 'w' ws i:imm8 ws? ',' ws? d:mem { $$ = INSTR2(6, i, d); } | 'l' ws i:imm8 ws? ',' ws? d:mem { $$ = INSTR2(7, i, d); } | 'q' ws i:imm8 ws? ',' ws? d:mem { $$ = INSTR2(8, i, d); } | 'w'? ws i:imm8 ws? ',' ws? d:r16 { $$ = INSTR2(9, i, d); } | 'l'? ws i:imm8 ws? ',' ws? d:r32 { $$ = INSTR2(10, i, d); } | 'q'? ws i:imm8 ws? ',' ws? d:r64 { $$ = INSTR2(11, i, d); } test = "test" ( 'b'? ws s:imm8 ws? ',' ws? d:al { $$ = INSTR2(0, s, d); } | 'w'? ws s:imm16 ws? ',' ws? d:ax { $$ = INSTR2(1, s, d); } | 'l'? ws s:imm32 ws? ',' ws? d:eax { $$ = INSTR2(2, s, d); } | 'q'? ws s:imm32 ws? ',' ws? d:rax { $$ = INSTR2(3, s, d); } | 'b' ws s:imm8 ws? ',' ws? d:mem { $$ = INSTR2(4, s, d); } | 'w' ws s:imm16 ws? ',' ws? d:mem { $$ = INSTR2(5, s, d); } | 'l' ws s:imm32 ws? ',' ws? d:mem { $$ = INSTR2(6, s, d); } | 'q' ws s:imm32 ws? ',' ws? d:mem { $$ = INSTR2(7, s, d); } | 'b'? ws s:imm8 ws? ',' ws? d:r8 { $$ = INSTR2(8, s, d); } | 'w'? ws s:imm16 ws? ',' ws? d:r16 { $$ = INSTR2(9, s, d); } | 'l'? ws s:imm32 ws? ',' ws? d:r32 { $$ = INSTR2(10, s, d); } | 'q'? ws s:imm32 ws? ',' ws? d:r64 { $$ = INSTR2(11, s, d); } | 'b'? ws s:r8 ws? ',' ws? d:mem { $$ = INSTR2(12, s, d); } | 'w'? ws s:r16 ws? ',' ws? d:mem { $$ = INSTR2(13, s, d); } | 'l'? ws s:r32 ws? ',' ws? d:mem { $$ = INSTR2(14, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:mem { $$ = INSTR2(15, s, d); } | 'b'? ws s:r8 ws? ',' ws? d:r8 { $$ = INSTR2(16, s, d); } | 'w'? ws s:r16 ws? ',' ws? d:r16 { $$ = INSTR2(17, s, d); } | 'l'? ws s:r32 ws? ',' ws? d:r32 { $$ = INSTR2(18, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:r64 { $$ = INSTR2(19, s, d); } ) { $$.instr.kind = ASM_TEST; } # Floating point instructions. addsd = "addsd" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_ADDSD; } addss = "addss" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_ADDSS; } subsd = "subsd" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_SUBSD; } subss = "subss" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_SUBSS; } cvtsi2sd = "cvtsi2sd" ( ws s:r32 ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } | ws s:r64 ws? ',' ws? d:xmm { $$ = INSTR2(2, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(3, s, d); } ) { $$.instr.kind = ASM_CVTSI2SD; } cvtsi2ss = "cvtsi2ss" ( ws s:r32 ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } | ws s:r64 ws? ',' ws? d:xmm { $$ = INSTR2(2, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(3, s, d); } ) { $$.instr.kind = ASM_CVTSI2SS; } cvtss2sd = "cvtss2sd" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_CVTSS2SD; } cvtsd2ss = "cvtsd2ss" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_CVTSD2SS; } cvttss2si = "cvttss2si" ( 'l'? ws s:xmm ws? ',' ws? d:r32 { $$ = INSTR2(0, s, d); } | 'q'? ws s:xmm ws? ',' ws? d:r64 { $$ = INSTR2(1, s, d); } | 'l' ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(2, s, d); } | 'q' ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(3, s, d); } ) { $$.instr.kind = ASM_CVTTSS2SI; } cvttsd2si = "cvttsd2si" ( 'l'? ws s:xmm ws? ',' ws? d:r32 { $$ = INSTR2(0, s, d); } | 'q'? ws s:xmm ws? ',' ws? d:r64 { $$ = INSTR2(1, s, d); } | 'l' ws s:mem ws? ',' ws? d:r32 { $$ = INSTR2(2, s, d); } | 'q' ws s:mem ws? ',' ws? d:r64 { $$ = INSTR2(3, s, d); } ) { $$.instr.kind = ASM_CVTTSD2SI; } divsd = "divsd" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_DIVSD; } divss = "divss" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_DIVSS; } movaps = "movaps" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } | ws s:xmm ws? ',' ws? d:mem { $$ = INSTR2(2, s, d); } ) { $$.instr.kind = ASM_MOVAPS; } mulsd = "mulsd" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_MULSD; } mulss = "mulss" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_MULSS; } movss = "movss" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } | ws s:xmm ws? ',' ws? d:mem { $$ = INSTR2(2, s, d); } ) { $$.instr.kind = ASM_MOVSS; } movsd = "movsd" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } | ws s:xmm ws? ',' ws? d:mem { $$ = INSTR2(2, s, d); } ) { $$.instr.kind = ASM_MOVSD; } movq = "mov" ( 'q'? ws s:xmm ws? ',' ws? d:r64 { $$ = INSTR2(0, s, d); } | 'q'? ws s:r64 ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } | 'q' ws s:xmm ws? ',' ws? d:mem { $$ = INSTR2(2, s, d); } | 'q' ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(3, s, d); } ) { $$.instr.kind = ASM_MOVQ; } ucomiss = "ucomiss" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_UCOMISS; } ucomisd = "ucomisd" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_UCOMISD; } pxor = "pxor" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_PXOR; } xorpd = "xorpd" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_XORPD; } xorps = "xorps" ( ws s:xmm ws? ',' ws? d:xmm { $$ = INSTR2(0, s, d); } | ws s:mem ws? ',' ws? d:xmm { $$ = INSTR2(1, s, d); } ) { $$.instr.kind = ASM_XORPS; } r64-or-rip = ( r:r64 | r:rip ) { $$ = r; } mem = d:value ws? sib:opt-scale-index-base { $$ = (Parsev){ .memarg=sib.memarg }; $$.memarg.disp = d.value; } | sib:scale-index-base { $$ = (Parsev){ .memarg=sib.memarg }; } opt-scale-index-base = sib:scale-index-base { $$ = sib; } | { $$.memarg = (Memarg){.kind=ASM_MEMARG, .scale = 0, .index=ASM_NO_REG, .base = ASM_NO_REG, .disp = {0} }; } scale-index-base = '(' ws? b:r64-or-rip ws? ')' { $$.memarg = (Memarg){.kind=ASM_MEMARG, .scale = 0, .index=ASM_NO_REG, .base = b.kind, .disp = {0} }; } | '(' 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, .disp = {0} }; } | '(' ws? b:r64 ws? ',' ws? i:r64 ')' { $$.memarg = (Memarg){.kind=ASM_MEMARG, .scale = 1, .index=i.kind, .base = b.kind, .disp = {0} }; } 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? val:value { $$ = (Parsev) { .imm = (Imm){ .kind = ASM_IMM, .v = val.value, .nbytes = 0} }; } al = "%al" { $$ = REG(AL); } cl = "%cl" { $$ = REG(CL); } ax = "%ax" { $$ = REG(AX); } eax = "%eax" { $$ = REG(EAX); } rax = "%rax" { $$ = REG(RAX); } rip = "%rip" { $$ = REG(RIP); } r8 = '%' ( "al" { $$ = REG(AL); } | "cl" { $$ = REG(CL); } | "dl" { $$ = REG(DL); } | "bl" { $$ = REG(BL); } | "spl" { $$ = REG(SPL); } | "bpl" { $$ = REG(BPL); } | "sil" { $$ = REG(SIL); } | "dil" { $$ = REG(DIL); } | "r8b" { $$ = REG(R8B); } | "r9b" { $$ = REG(R9B); } | "r10b" { $$ = REG(R10B); } | "r11b" { $$ = REG(R11B); } | "r12b" { $$ = REG(R12B); } | "r13b" { $$ = REG(R13B); } | "r14b" { $$ = REG(R14B); } | "r15b" { $$ = REG(R15B); } ) r16 = '%' ( "ax" { $$ = REG(AX); } | "cx" { $$ = REG(CX); } | "dx" { $$ = REG(DX); } | "bx" { $$ = REG(BX); } | "sp" { $$ = REG(SP); } | "bp" { $$ = REG(BP); } | "si" { $$ = REG(SI); } | "di" { $$ = REG(DI); } | "r8w" { $$ = REG(R8W); } | "r9w" { $$ = REG(R9W); } | "r10w" { $$ = REG(R10W); } | "r11w" { $$ = REG(R11W); } | "r12w" { $$ = REG(R12W); } | "r13w" { $$ = REG(R13W); } | "r14w" { $$ = REG(R14W); } | "r15w" { $$ = REG(R15W); } ) r32 = '%' ( "eax" { $$ = REG(EAX); } | "ecx" { $$ = REG(ECX); } | "edx" { $$ = REG(EDX); } | "ebx" { $$ = REG(EBX); } | "esp" { $$ = REG(ESP); } | "ebp" { $$ = REG(EBP); } | "esi" { $$ = REG(ESI); } | "edi" { $$ = REG(EDI); } | "r8d" { $$ = REG(R8D); } | "r9d" { $$ = REG(R9D); } | "r10d" { $$ = REG(R10D); } | "r11d" { $$ = REG(R11D); } | "r12d" { $$ = REG(R12D); } | "r13d" { $$ = REG(R13D); } | "r14d" { $$ = REG(R14D); } | "r15d" { $$ = REG(R15D); } ) r64 = "%r" ( "ax" { $$ = REG(RAX); } | "cx" { $$ = REG(RCX); } | "dx" { $$ = REG(RDX); } | "bx" { $$ = REG(RBX); } | "sp" { $$ = REG(RSP); } | "bp" { $$ = REG(RBP); } | "si" { $$ = REG(RSI); } | "di" { $$ = REG(RDI); } | "8" ![lwb] { $$ = REG(R8); } | "9" ![lwb] { $$ = REG(R9); } | "10" ![lwb] { $$ = REG(R10); } | "11" ![lwb] { $$ = REG(R11); } | "12" ![lwb] { $$ = REG(R12); } | "13" ![lwb] { $$ = REG(R13); } | "14" ![lwb] { $$ = REG(R14); } | "15" ![lwb] { $$ = REG(R15); } ) xmm = "%xmm" ( # Reverse order due to peg ordering. "15" { $$ = REG(XMM15); } | "14" { $$ = REG(XMM14); } | "13" { $$ = REG(XMM13); } | "12" { $$ = REG(XMM12); } | "11" { $$ = REG(XMM11); } | "10" { $$ = REG(XMM10); } | "9" { $$ = REG(XMM7); } | "8" { $$ = REG(XMM7); } | "7" { $$ = REG(XMM7); } | "6" { $$ = REG(XMM6); } | "5" { $$ = REG(XMM5); } | "4" { $$ = REG(XMM4); } | "3" { $$ = REG(XMM3); } | "2" { $$ = REG(XMM2); } | "1" { $$ = REG(XMM1); } | "0" { $$ = REG(XMM0); } ) # We disallow newlines in our strings, it is simpler for lineno tracking. string = '"' <(string-escape | ( ! '\n' ! '\\' !'"' .))*> '"' { $$.string = decodestring(yytext); } string-escape = '\\' ( '\\' | '"' | [nrt] | 'x' [0-9A-Fa-f]+ | [0-7][0-7][0-7] ) value = n:number { $$.value = (Value){ .l = NULL, .c = n.i64 }; } | i:ident ws? ( '+' ws? n:number { $$.value = (Value){ .c = n.i64 }; } | &'-' n:number { $$.value = (Value){ .c = n.i64 }; } | { $$.value = (Value){ .c = 0 }; } ) { $$.value.l = i.charptr; } ident = <[._a-zA-Z][._a-zA-Z0-9]*> { $$.charptr = internstring(yytext); } number = '-' ws? n:unsigned-number { $$.i64 = -n.i64; } | n:unsigned-number { $$.i64 = n.i64; } unsigned-number = '0' [xX] <[0-9a-fA-F]+> { $$.i64 = (int64_t)strtoull(yytext, NULL, 16); } | <[0-9]+> { $$.i64 = (int64_t)strtoull(yytext, NULL, 10); }