tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

binop-x64-ion-codegen.js (13165B)


      1 // |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration("x64") || getBuildConfiguration("simulator"); include:codegen-x64-test.js
      2 
      3 // There may be some redundant moves to set some operations up on x64 (that's
      4 // bug 1701164) so we avoid those with no_prefix/no_suffix flags when the issue
      5 // comes up.
      6 
      7 // Handle calling convention differences.
      8 function codegenTestX64_adhoc_call(module_text, export_name, expected, options = {}) {
      9    // Microsoft x64 calling convention passes the first four arguments in
     10    // registers rcx, rdx, r8, r9 (in that order).
     11    if (getBuildConfiguration("windows")) {
     12        // Arguments passed on the stack not yet supported.
     13        assertEq(
     14            expected.includes("%r8") || expected.includes("%r9"),
     15            false,
     16            "too many arguments"
     17        );
     18        expected = expected.replaceAll("%rcx", "%r9")
     19                           .replaceAll("%rdx", "%r8")
     20                           .replaceAll("%rsi", "%rdx")
     21                           .replaceAll("%rdi", "%rcx")
     22                           .replaceAll("%esi", "%edx")
     23                           .replaceAll("%edi", "%ecx");
     24    }
     25    codegenTestX64_adhoc(module_text, export_name, expected, options);
     26 }
     27 
     28 // Test that multiplication by -1 yields negation.
     29 
     30 let neg32 =
     31    `(module
     32       (func (export "f") (param i32) (result i32)
     33         (i32.mul (local.get 0) (i32.const -1))))`;
     34 codegenTestX64_adhoc(
     35    neg32,
     36    'f',
     37    'neg %eax', {no_prefix:true});
     38 assertEq(wasmEvalText(neg32).exports.f(-37), 37)
     39 assertEq(wasmEvalText(neg32).exports.f(42), -42)
     40 
     41 let neg64 =
     42    `(module
     43       (func (export "f") (param i64) (result i64)
     44         (i64.mul (local.get 0) (i64.const -1))))`;
     45 codegenTestX64_adhoc(
     46    neg64,
     47    'f',
     48    'neg %rax', {no_prefix:true});
     49 assertEq(wasmEvalText(neg64).exports.f(-37000000000n), 37000000000n)
     50 assertEq(wasmEvalText(neg64).exports.f(42000000000n), -42000000000n)
     51 
     52 // Test that multiplication by zero yields zero
     53 
     54 let zero32 =
     55    `(module
     56       (func (export "f") (param i32) (result i32)
     57         (i32.mul (local.get 0) (i32.const 0))))`;
     58 codegenTestX64_adhoc(
     59    zero32,
     60    'f',
     61    'xor %eax, %eax');
     62 assertEq(wasmEvalText(zero32).exports.f(-37), 0)
     63 assertEq(wasmEvalText(zero32).exports.f(42), 0)
     64 
     65 let zero64 = `(module
     66       (func (export "f") (param i64) (result i64)
     67         (i64.mul (local.get 0) (i64.const 0))))`
     68 codegenTestX64_adhoc(
     69    zero64,
     70    'f',
     71    'xor %rax, %rax');
     72 assertEq(wasmEvalText(zero64).exports.f(-37000000000n), 0n)
     73 assertEq(wasmEvalText(zero64).exports.f(42000000000n), 0n)
     74 
     75 // Test that multiplication by one yields no code
     76 
     77 let one32 =
     78    `(module
     79       (func (export "f") (param i32) (result i32)
     80         (i32.mul (local.get 0) (i32.const 1))))`;
     81 codegenTestX64_adhoc(
     82    one32,
     83    'f',
     84    '', {no_prefix:true});
     85 assertEq(wasmEvalText(one32).exports.f(-37), -37)
     86 assertEq(wasmEvalText(one32).exports.f(42), 42)
     87 
     88 let one64 = `(module
     89       (func (export "f") (param i64) (result i64)
     90         (i64.mul (local.get 0) (i64.const 1))))`
     91 codegenTestX64_adhoc(
     92    one64,
     93    'f',
     94    '', {no_prefix:true});
     95 assertEq(wasmEvalText(one64).exports.f(-37000000000n), -37000000000n)
     96 assertEq(wasmEvalText(one64).exports.f(42000000000n), 42000000000n)
     97 
     98 // Test that multiplication by two yields lea
     99 
    100 let double32 =
    101    `(module
    102       (func (export "f") (param i32) (result i32)
    103         (i32.mul (local.get 0) (i32.const 2))))`;
    104 codegenTestX64_adhoc_call(
    105    double32,
    106    'f',
    107    'lea \\(%rdi,%rdi,1\\), %eax');
    108 assertEq(wasmEvalText(double32).exports.f(-37), -74)
    109 assertEq(wasmEvalText(double32).exports.f(42), 84)
    110 
    111 let double64 = `(module
    112       (func (export "f") (param i64) (result i64)
    113         (i64.mul (local.get 0) (i64.const 2))))`
    114 codegenTestX64_adhoc_call(
    115    double64,
    116    'f',
    117    'lea \\(%rdi,%rdi,1\\), %rax');
    118 assertEq(wasmEvalText(double64).exports.f(-37000000000n), -74000000000n)
    119 assertEq(wasmEvalText(double64).exports.f(42000000000n), 84000000000n)
    120 
    121 // Test that multiplication by four yields lea
    122 
    123 let quad32 =
    124    `(module
    125       (func (export "f") (param i32) (result i32)
    126         (i32.mul (local.get 0) (i32.const 4))))`;
    127 codegenTestX64_adhoc_call(
    128    quad32,
    129    'f',
    130    'lea \\(,%rdi,4\\), %eax');
    131 assertEq(wasmEvalText(quad32).exports.f(-37), -148)
    132 assertEq(wasmEvalText(quad32).exports.f(42), 168)
    133 
    134 let quad64 = `(module
    135       (func (export "f") (param i64) (result i64)
    136         (i64.mul (local.get 0) (i64.const 4))))`
    137 codegenTestX64_adhoc_call(
    138    quad64,
    139    'f',
    140    'lea \\(,%rdi,4\\), %rax');
    141 assertEq(wasmEvalText(quad64).exports.f(-37000000000n), -148000000000n)
    142 assertEq(wasmEvalText(quad64).exports.f(42000000000n), 168000000000n)
    143 
    144 // Test that multiplication by five yields lea
    145 
    146 let quint32 =
    147    `(module
    148       (func (export "f") (param i32) (result i32)
    149         (i32.mul (local.get 0) (i32.const 5))))`;
    150 codegenTestX64_adhoc_call(
    151    quint32,
    152    'f',
    153    'lea \\(%rdi,%rdi,4\\), %eax');
    154 assertEq(wasmEvalText(quint32).exports.f(-37), -37*5)
    155 assertEq(wasmEvalText(quint32).exports.f(42), 42*5)
    156 
    157 let quint64 = `(module
    158       (func (export "f") (param i64) (result i64)
    159         (i64.mul (local.get 0) (i64.const 5))))`
    160 codegenTestX64_adhoc_call(
    161    quint64,
    162    'f',
    163    `lea \\(%rdi,%rdi,4\\), %rax`)
    164 assertEq(wasmEvalText(quint64).exports.f(-37000000000n), -37000000000n*5n)
    165 assertEq(wasmEvalText(quint64).exports.f(42000000000n), 42000000000n*5n)
    166 
    167 // Test that multiplication by six yields imul
    168 
    169 let sext32 =
    170    `(module
    171       (func (export "f") (param i32) (result i32)
    172         (i32.mul (local.get 0) (i32.const 6))))`;
    173 codegenTestX64_adhoc_call(
    174    sext32,
    175    'f',
    176    'imul \\$0x06, %edi, %eax');
    177 assertEq(wasmEvalText(sext32).exports.f(-37), -37*6)
    178 assertEq(wasmEvalText(sext32).exports.f(42), 42*6)
    179 
    180 let sext64 = `(module
    181       (func (export "f") (param i64) (result i64)
    182         (i64.mul (local.get 0) (i64.const 6))))`
    183 codegenTestX64_adhoc_call(
    184    sext64,
    185    'f',
    186    `imul \\$0x06, %rdi, %rax`)
    187 assertEq(wasmEvalText(sext64).exports.f(-37000000000n), -37000000000n*6n)
    188 assertEq(wasmEvalText(sext64).exports.f(42000000000n), 42000000000n*6n)
    189 
    190 // Test that multiplication by UINT32_MAX yields imul
    191 
    192 let uint32max64 = `(module
    193       (func (export "f") (param i64) (result i64)
    194         (i64.mul (local.get 0) (i64.const 0xffffffff))))`
    195 codegenTestX64_adhoc(
    196    uint32max64,
    197    'f',
    198    `mov \\$-0x01, %r11d
    199     imul %r11, %rax`, {no_prefix:true})
    200 assertEq(wasmEvalText(uint32max64).exports.f(-37000000000n), BigInt.asIntN(64, -37000000000n*0xffffffffn))
    201 assertEq(wasmEvalText(uint32max64).exports.f(42000000000n), BigInt.asIntN(64, 42000000000n*0xffffffffn))
    202 
    203 // Test that 0-n yields negation.
    204 
    205 let subneg32 =
    206    `(module
    207       (func (export "f") (param i32) (result i32)
    208         (i32.sub (i32.const 0) (local.get 0))))`
    209 codegenTestX64_adhoc(
    210    subneg32,
    211    'f',
    212    'neg %eax', {no_prefix:true});
    213 assertEq(wasmEvalText(subneg32).exports.f(-37), 37)
    214 assertEq(wasmEvalText(subneg32).exports.f(42), -42)
    215 
    216 let subneg64 =
    217    `(module
    218       (func (export "f") (param i64) (result i64)
    219         (i64.sub (i64.const 0) (local.get 0))))`
    220 codegenTestX64_adhoc(
    221    subneg64,
    222    'f',
    223    'neg %rax', {no_prefix:true});
    224 assertEq(wasmEvalText(subneg64).exports.f(-37000000000n), 37000000000n)
    225 assertEq(wasmEvalText(subneg64).exports.f(42000000000n), -42000000000n)
    226 
    227 // AND{32,64} followed by `== 0`: check the two operations are merged into a
    228 // single 'test' insn, and no 'and' insn.  The merging isn't done for
    229 // {OR,XOR}{32,64}.  This is for both arguments being non-constant.
    230 
    231 for ( [ty, expect_test] of
    232      [['i32',   'test %e.., %e..'],
    233       ['i64',   'test %r.., %r..']] ) {
    234   codegenTestX64_adhoc(
    235    `(module
    236       (func (export "f") (param $p1 ${ty}) (param $p2 ${ty}) (result i32)
    237         (local $x i32)
    238         (local.set $x (i32.const 0x4D2))
    239         (if (${ty}.eq (${ty}.and (local.get $p1) (local.get $p2))
    240                       (${ty}.const 0))
    241           (then (local.set $x (i32.const 0x11D7)))
    242         )
    243         (local.get $x)
    244       )
    245    )`,
    246    'f',
    247    `${expect_test}
    248     jnz 0x00000000000000..
    249     mov \\$0x11D7, %eax
    250     jmp 0x00000000000000..
    251     mov \\$0x4D2, %eax`
    252   );
    253 }
    254 
    255 // AND64 followed by `== 0`, with one of the args being a constant.  Depending
    256 // on the constant we can get one of three forms.
    257 
    258 for ( [imm, expect1, expect2] of
    259      [ // in signed-32 range => imm in insn
    260        ['0x17654321',
    261         'test \\$0x17654321, %e..', // edi or ecx
    262         ''],
    263        // in unsigned-32 range => imm in reg via movl
    264        ['0x87654321',
    265         'mov \\$-0x789ABCDF, %r11d',
    266         'test %r11, %r..'], // rdi or rcx
    267        // not in either range => imm in reg via mov(absq)
    268        ['0x187654321',
    269         'mov \\$0x187654321, %r11',
    270         'test %r11, %r..']] // rdi or rcx
    271      ) {
    272   codegenTestX64_adhoc(
    273    `(module
    274       (func (export "f") (param $p1 i64) (result i32)
    275         (local $x i32)
    276         (local.set $x (i32.const 0x4D2))
    277         (if (i64.eq (i64.and (i64.const ${imm}) (local.get $p1))
    278                     (i64.const 0))
    279           (then (local.set $x (i32.const 0x11D7)))
    280         )
    281         (local.get $x)
    282       )
    283    )`,
    284    'f',
    285    `${expect1}
    286     ${expect2}
    287     jnz 0x00000000000000..
    288     mov \\$0x11D7, %eax
    289     jmp 0x00000000000000..
    290     mov \\$0x4D2, %eax`
    291   );
    292 }
    293 
    294 // For integer comparison followed by select, check that the comparison result
    295 // isn't materialised into a register, for specific types.
    296 
    297 function cmpSel32vs64(cmpTy, cmpOp, selTy) {
    298    return `(module
    299              (func (export "f")
    300                    (param $p1 ${cmpTy}) (param $p2 ${cmpTy})
    301                    (param $p3 ${selTy}) (param $p4 ${selTy})
    302                    (result ${selTy})
    303                (select (local.get $p3)
    304                        (local.get $p4)
    305                        (${cmpTy}.${cmpOp} (local.get $p1) (local.get $p2)))
    306              )
    307            )`;
    308 }
    309 if (getBuildConfiguration("windows")) {
    310    for ( [cmpTy, cmpOp, selTy, insn1, insn2, insn3] of
    311          [ ['i32', 'le_s', 'i32',  'mov %ebx, %eax',
    312                                    'cmp %edx, %ecx',
    313                                    'cmovnle %r9d, %eax'],
    314            ['i32', 'lt_u', 'i64',  'mov %rbx, %rax',
    315                                    'cmp %edx, %ecx',
    316                                    'cmovnb %r9, %rax'],
    317            ['i64', 'le_s', 'i32',  'mov %ebx, %eax',
    318                                    'cmp %rdx, %rcx',
    319                                    'cmovnle %r9d, %eax'],
    320            ['i64', 'lt_u', 'i64',  'mov %rbx, %rax',
    321                                    'cmp %rdx, %rcx',
    322                                    'cmovnb %r9, %rax']
    323          ] ) {
    324        codegenTestX64_adhoc(cmpSel32vs64(cmpTy, cmpOp, selTy), 'f',
    325          `mov %r8.*, %.bx
    326           ${insn1}
    327           ${insn2}
    328           ${insn3}`
    329        );
    330    }
    331 } else {
    332    for ( [cmpTy, cmpOp, selTy, insn1, insn2, insn3] of
    333          [ ['i32', 'le_s', 'i32',  'mov %edx, %eax',
    334                                    'cmp %esi, %edi',
    335                                    'cmovnle %ecx, %eax'],
    336            ['i32', 'lt_u', 'i64',  'mov %rdx, %rax',
    337                                    'cmp %esi, %edi',
    338                                    'cmovnb %rcx, %rax'],
    339            ['i64', 'le_s', 'i32',  'mov %edx, %eax',
    340                                    'cmp %rsi, %rdi',
    341                                    'cmovnle %ecx, %eax'],
    342            ['i64', 'lt_u', 'i64',  'mov %rdx, %rax',
    343                                    'cmp %rsi, %rdi',
    344                                    'cmovnb %rcx, %rax']
    345          ] ) {
    346        codegenTestX64_adhoc(cmpSel32vs64(cmpTy, cmpOp, selTy), 'f',
    347          `${insn1}
    348           ${insn2}
    349           ${insn3}`
    350        );
    351    }
    352 }
    353 
    354 // For integer comparison followed by select, check correct use of operands in
    355 // registers vs memory.  At least for the 64-bit-cmp/64-bit-sel case.
    356 
    357 for ( [pAnyCmp, pAnySel, cmpArgL, cmovArgL ] of
    358      [ // r, r
    359        ['$pReg1', '$pReg2',
    360         '%r.+', '%r.+'],
    361        // r, m
    362        ['$pReg1', '$pMem2',
    363         '%r.+', '0x..\\(%rbp\\)'],
    364        // m, r
    365        ['$pMem1', '$pReg2',
    366         '0x..\\(%rbp\\)', '%r.+'],
    367        // m, m
    368        ['$pMem1', '$pMem2',
    369         '0x..\\(%rbp\\)', '0x..\\(%rbp\\)']
    370      ] ) {
    371   codegenTestX64_adhoc(
    372    `(module
    373       (func (export "f")
    374             (param $p1 i64)    (param $p2 i64)     ;; in regs for both ABIs
    375             (param $pReg1 i64) (param $pReg2 i64)  ;; in regs for both ABIs
    376             (param i64)        (param i64)         ;; ignored
    377             (param $pMem1 i64) (param $pMem2 i64)  ;; in mem for both ABIs
    378             (result i64)
    379         (select (local.get $p1)
    380                 (local.get ${pAnySel})
    381                 (i64.eq (local.get $p2) (local.get ${pAnyCmp})))
    382       )
    383    )`,
    384    'f',
    385    // On Linux we have an extra move
    386    (getBuildConfiguration("windows") ? '' : 'mov %r.+, %r.+\n') +
    387    // 'q*' because the disassembler shows 'q' only for the memory cases
    388    `mov %r.+, %r.+
    389     cmpq*    ${cmpArgL}, %r.+
    390     cmovnzq* ${cmovArgL}, %r.+`
    391   );
    392 }