tor-browser

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

binop-arm64-ion-codegen.js (12978B)


      1 // |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || !getBuildConfiguration("arm64"); include:codegen-arm64-test.js
      2 
      3 // Basic constant folding tests
      4 
      5 for ( [op, lhs, rhs, expect] of
      6      [['add',   5,    8,    'mov     x0, #0xd'],
      7       ['sub',   4,    5,    'mov     x0, #0xffffffffffffffff'],
      8       ['mul',   8,    3,    'mov     x0, #0x18'],
      9       ['div_s', -8,   3,    'mov     x0, #0xfffffffffffffffe'],
     10       ['div_u', 8,    3,    'mov     x0, #0x2'],
     11       ['rem_s', 8,    5,    'mov     x0, #0x3'],
     12       ['rem_u', -7,   4,    'mov     x0, #0x1'],
     13       ['and',   0xfe, 0x77, 'mov     x0, #0x76'],
     14       ['or',    0xfe, 0x77, 'mov     x0, #0xff'],
     15       ['xor',   0xfe, 0x77, 'mov     x0, #0x89'],
     16       ['shl',   3,    4,    'mov     x0, #0x30'],
     17       ['shr_s', -8,   1,    'mov     x0, #0xfffffffffffffffc'],
     18       ['shr_u', -8,   1,    'mov     x0, #0x7ffffffffffffffc']] ) {
     19    codegenTestARM64_adhoc(`
     20 (module
     21  (func (export "f") (result i64)
     22    (i64.${op} (i64.const ${lhs}) (i64.const ${rhs}))))`,
     23                           'f',
     24                           expect);
     25 }
     26 
     27 // Basic tests that addition and multiplication identities are collapsed, use
     28 // arg 1 here to force an explicit move to be emitted.
     29 
     30 for ( [op, args, expect] of
     31      [['add',   '(local.get 1) (i64.const 0)', 'mov     x0, x1'],
     32       ['add',   '(i64.const 0) (local.get 1)', 'mov     x0, x1'],
     33       ['mul',   '(local.get 1) (i64.const 1)', 'mov     x0, x1'],
     34       ['mul',   '(i64.const 1) (local.get 1)', 'mov     x0, x1']] ) {
     35    codegenTestARM64_adhoc(`
     36 (module
     37  (func (export "f") (param i64) (param i64) (result i64)
     38    (i64.${op} ${args})))`,
     39                           'f',
     40                           expect);
     41 }
     42 
     43 // Test that multiplication by -1 yields negation.
     44 
     45 let neg32 =
     46    `(module
     47       (func (export "f") (param i32) (result i32)
     48         (i32.mul (local.get 0) (i32.const -1))))`;
     49 codegenTestARM64_adhoc(
     50    neg32,
     51    'f',
     52    'neg w0, w0');
     53 assertEq(wasmEvalText(neg32).exports.f(-37), 37)
     54 assertEq(wasmEvalText(neg32).exports.f(42), -42)
     55 
     56 let neg64 = `(module
     57       (func (export "f") (param i64) (result i64)
     58         (i64.mul (local.get 0) (i64.const -1))))`
     59 codegenTestARM64_adhoc(
     60    neg64,
     61    'f',
     62    'neg x0, x0');
     63 assertEq(wasmEvalText(neg64).exports.f(-37000000000n), 37000000000n)
     64 assertEq(wasmEvalText(neg64).exports.f(42000000000n), -42000000000n)
     65 
     66 // Test that multiplication by zero yields zero
     67 
     68 let zero32 =
     69    `(module
     70       (func (export "f") (param i32) (result i32)
     71         (i32.mul (local.get 0) (i32.const 0))))`;
     72 codegenTestARM64_adhoc(
     73    zero32,
     74    'f',
     75    'mov w0, wzr');
     76 assertEq(wasmEvalText(zero32).exports.f(-37), 0)
     77 assertEq(wasmEvalText(zero32).exports.f(42), 0)
     78 
     79 let zero64 = `(module
     80       (func (export "f") (param i64) (result i64)
     81         (i64.mul (local.get 0) (i64.const 0))))`
     82 codegenTestARM64_adhoc(
     83    zero64,
     84    'f',
     85    'mov x0, xzr');
     86 assertEq(wasmEvalText(zero64).exports.f(-37000000000n), 0n)
     87 assertEq(wasmEvalText(zero64).exports.f(42000000000n), 0n)
     88 
     89 // Test that multiplication by one yields no code (this optimization currently
     90 // exists both in constant folding and in lowering).
     91 
     92 let one32 =
     93    `(module
     94       (func (export "f") (param i32) (result i32)
     95         (i32.mul (local.get 0) (i32.const 1))))`;
     96 codegenTestARM64_adhoc(
     97    one32,
     98    'f',
     99    '');
    100 assertEq(wasmEvalText(one32).exports.f(-37), -37)
    101 assertEq(wasmEvalText(one32).exports.f(42), 42)
    102 
    103 let one64 = `(module
    104       (func (export "f") (param i64) (result i64)
    105         (i64.mul (local.get 0) (i64.const 1))))`
    106 codegenTestARM64_adhoc(
    107    one64,
    108    'f',
    109    '');
    110 assertEq(wasmEvalText(one64).exports.f(-37000000000n), -37000000000n)
    111 assertEq(wasmEvalText(one64).exports.f(42000000000n), 42000000000n)
    112 
    113 // Test that multiplication by two yields an add
    114 
    115 let double32 =
    116    `(module
    117       (func (export "f") (param i32) (result i32)
    118         (i32.mul (local.get 0) (i32.const 2))))`;
    119 codegenTestARM64_adhoc(
    120    double32,
    121    'f',
    122    'add w0, w0, w0');
    123 assertEq(wasmEvalText(double32).exports.f(-37), -74)
    124 assertEq(wasmEvalText(double32).exports.f(42), 84)
    125 
    126 let double64 = `(module
    127       (func (export "f") (param i64) (result i64)
    128         (i64.mul (local.get 0) (i64.const 2))))`
    129 codegenTestARM64_adhoc(
    130    double64,
    131    'f',
    132    'add x0, x0, x0');
    133 assertEq(wasmEvalText(double64).exports.f(-37000000000n), -74000000000n)
    134 assertEq(wasmEvalText(double64).exports.f(42000000000n), 84000000000n)
    135 
    136 // Test that multiplication by four yields a shift
    137 
    138 let quad32 =
    139    `(module
    140       (func (export "f") (param i32) (result i32)
    141         (i32.mul (local.get 0) (i32.const 4))))`;
    142 codegenTestARM64_adhoc(
    143    quad32,
    144    'f',
    145    'lsl w0, w0, #2');
    146 assertEq(wasmEvalText(quad32).exports.f(-37), -148)
    147 assertEq(wasmEvalText(quad32).exports.f(42), 168)
    148 
    149 let quad64 = `(module
    150       (func (export "f") (param i64) (result i64)
    151         (i64.mul (local.get 0) (i64.const 4))))`
    152 codegenTestARM64_adhoc(
    153    quad64,
    154    'f',
    155    'lsl x0, x0, #2');
    156 assertEq(wasmEvalText(quad64).exports.f(-37000000000n), -148000000000n)
    157 assertEq(wasmEvalText(quad64).exports.f(42000000000n), 168000000000n)
    158 
    159 // Test that multiplication by five yields a multiply
    160 
    161 let quint32 =
    162    `(module
    163       (func (export "f") (param i32) (result i32)
    164         (i32.mul (local.get 0) (i32.const 5))))`;
    165 codegenTestARM64_adhoc(
    166    quint32,
    167    'f',
    168    `mov     w16, #0x5
    169     mul     w0, w0, w16`);
    170 assertEq(wasmEvalText(quint32).exports.f(-37), -37*5)
    171 assertEq(wasmEvalText(quint32).exports.f(42), 42*5)
    172 
    173 let quint64 = `(module
    174       (func (export "f") (param i64) (result i64)
    175         (i64.mul (local.get 0) (i64.const 5))))`
    176 codegenTestARM64_adhoc(
    177    quint64,
    178    'f',
    179    `mov     x16, #0x5
    180     mul     x0, x0, x16`);
    181 assertEq(wasmEvalText(quint64).exports.f(-37000000000n), -37000000000n*5n)
    182 assertEq(wasmEvalText(quint64).exports.f(42000000000n), 42000000000n*5n)
    183 
    184 // Test that add/sub/and/or/xor don't need to reuse their input register.  The
    185 // proof here is that the destination register does not equal any of the input
    186 // registers.
    187 //
    188 // We have adequate functionality tests for these elsewhere, so test only
    189 // codegen here.
    190 
    191 for ( [op, imm, expectVar, expectImm] of
    192      [['and', 64,
    193        'and     x0, x1, x2',
    194        'and     x0, x1, #0x40'],
    195       ['or', 64,
    196        'orr     x0, x1, x2',
    197        'orr     x0, x1, #0x40'],
    198       ['xor', 64,
    199        'eor     x0, x1, x2',
    200        'eor     x0, x1, #0x40'],
    201       ['add', 64,
    202        'add     x0, x1, x2',
    203        'add     x0, x1, #0x40 \\(64\\)'],
    204       ['sub', 64,
    205        'sub     x0, x1, x2',
    206        'sub     x0, x1, #0x40 \\(64\\)']] ) {
    207    codegenTestARM64_adhoc(`
    208 (module
    209  (func (export "f") (param i64) (param i64) (param i64) (result i64)
    210    (i64.${op} (local.get 1) (local.get 2))))`,
    211                           'f',
    212                           expectVar);
    213    codegenTestARM64_adhoc(`
    214 (module
    215  (func (export "f") (param i64) (param i64) (result i64)
    216    (i64.${op} (local.get 1) (i64.const ${imm}))))`,
    217                           'f',
    218                           expectImm);
    219 }
    220 
    221 // Test that shifts and rotates with a constant don't need to reuse their input
    222 // register.  The proof here is that the destination register does not equal any
    223 // of the input registers.
    224 //
    225 // We have adequate functionality tests for these elsewhere, so test only
    226 // codegen here.
    227 
    228 for ( [op, expect] of
    229      [['shl',   'lsl     x0, x1, #2'],
    230       ['shr_s', 'asr     x0, x1, #2'],
    231       ['shr_u', 'lsr     x0, x1, #2'],
    232       ['rotl',  'ror     x0, x1, #62'],
    233       ['rotr',  'ror     x0, x1, #2']] ) {
    234    codegenTestARM64_adhoc(`
    235 (module
    236  (func (export "f") (param i64) (param i64) (result i64)
    237    (i64.${op} (local.get 1) (i64.const 2))))`,
    238                           'f',
    239                           expect);
    240 }
    241 
    242 // Test that 0-n yields negation.
    243 
    244 let subneg32 =
    245    `(module
    246       (func (export "f") (param i32) (result i32)
    247         (i32.sub (i32.const 0) (local.get 0))))`
    248 codegenTestARM64_adhoc(
    249    subneg32,
    250    'f',
    251    'neg w0, w0');
    252 assertEq(wasmEvalText(subneg32).exports.f(-37), 37)
    253 assertEq(wasmEvalText(subneg32).exports.f(42), -42)
    254 
    255 let subneg64 = `(module
    256       (func (export "f") (param i64) (result i64)
    257         (i64.sub (i64.const 0) (local.get 0))))`
    258 codegenTestARM64_adhoc(
    259    subneg64,
    260    'f',
    261    'neg x0, x0');
    262 assertEq(wasmEvalText(subneg64).exports.f(-37000000000n), 37000000000n)
    263 assertEq(wasmEvalText(subneg64).exports.f(42000000000n), -42000000000n)
    264 
    265 // Test that select does something reasonable and does not tie its output to one
    266 // of its inputs.
    267 
    268 codegenTestARM64_adhoc(
    269    `(module
    270       (func (export "f") (param i64) (param i64) (param i64) (param i32) (result i64)
    271         (select (local.get 1) (local.get 2) (local.get 3))))`,
    272    'f',
    273    `tst     w3, w3
    274     csel    x0, x1, x2, ne`)
    275 
    276 codegenTestARM64_adhoc(
    277    `(module
    278       (func (export "f") (param f64) (param f64) (param f64) (param i32) (result f64)
    279         (select (local.get 1) (local.get 2) (local.get 3))))`,
    280    'f',
    281    `tst     w0, w0
    282     fcsel   d0, d1, d2, ne`)
    283 
    284 // Here we test that no boolean is generated and then re-tested, and that
    285 // operands are swapped so that we can use an immediate constant, and that the
    286 // input is not tied to the output.
    287 
    288 codegenTestARM64_adhoc(
    289    `(module
    290       (func (export "f") (param $a i32) (param $b i32) (param $c i32) (param $d i32) (result i32)
    291         (select (local.get $b) (local.get $d) (i32.lt_s (i32.const 0) (local.get $c)))))`,
    292    'f',
    293    `cmp     w2, #0x0 \\(0\\)
    294     csel    w0, w1, w3, gt`)
    295 
    296 codegenTestARM64_adhoc(
    297    `(module
    298       (func (export "f") (param $a f64) (param $b f64) (param $c f64) (param $d f64) (result f64)
    299         (select (local.get $b) (local.get $d) (f64.lt (f64.const 0) (local.get $c)))))`,
    300    'f',
    301    `movi    d0, #0x0
    302     fcmp    d0, d2
    303     fcsel   d0, d1, d3, lo`)
    304 
    305 // FP ABS should not tie its input to its output.
    306 
    307 codegenTestARM64_adhoc(
    308    `(module
    309       (func (export "f") (param f32) (param f32) (result f32)
    310         (f32.abs (local.get 1))))`,
    311    'f',
    312    'fabs    s0, s1');
    313 
    314 codegenTestARM64_adhoc(
    315    `(module
    316       (func (export "f") (param f64) (param f64) (result f64)
    317         (f64.abs (local.get 1))))`,
    318    'f',
    319    'fabs    d0, d1');
    320 
    321 // AND{32,64} followed by `== 0`: check the two operations are merged into a
    322 // single 'tst' insn, and no 'and' insn.  The merging isn't done for
    323 // {OR,XOR}{32,64}.  This is for both arguments being non-constant.
    324 
    325 for ( [ty, expect_tst] of
    326      [['i32',   'tst w0, w1'],
    327       ['i64',   'tst x0, x1']] ) {
    328   codegenTestARM64_adhoc(
    329    `(module
    330       (func (export "f") (param $p1 ${ty}) (param $p2 ${ty}) (result i32)
    331         (local $x i32)
    332         (local.set $x (i32.const 0x4D2))
    333         (if (${ty}.eq (${ty}.and (local.get $p1) (local.get $p2))
    334                       (${ty}.const 0))
    335           (then (local.set $x (i32.const 0x11D7)))
    336         )
    337         (local.get $x)
    338       )
    339    )`,
    340    'f',
    341    `${expect_tst}
    342     b\\.ne  #\\+0xc \\(addr .*\\)
    343     mov     w0, #0x11d7
    344     b       #\\+0x8 \\(addr .*\\)
    345     mov     w0, #0x4d2`
    346   );
    347 }
    348 
    349 // AND64 followed by `== 0`, with one of the args being a constant.
    350 
    351 for ( [imm, expect1, expect2] of
    352      [ // as a valid logical-immediate => imm in insn
    353        ['0x0F0F0F0F0F0F0F0F',
    354         'tst  x0, #0xf0f0f0f0f0f0f0f',
    355         ''],
    356        // anything else => imm synth'd into a reg
    357        ['-0x4771',
    358         'mov  x16, #0xffffffffffffb88f',
    359         'tst  x0, x16']]
    360      ) {
    361   codegenTestARM64_adhoc(
    362    `(module
    363       (func (export "f") (param $p1 i64) (result i32)
    364         (local $x i32)
    365         (local.set $x (i32.const 0x4D2))
    366         (if (i64.eq (i64.and (i64.const ${imm}) (local.get $p1))
    367                     (i64.const 0))
    368           (then (local.set $x (i32.const 0x11D7)))
    369         )
    370         (local.get $x)
    371       )
    372    )`,
    373    'f',
    374    `${expect1}
    375     ${expect2}
    376     b\\.ne  #\\+0xc \\(addr .*\\)
    377     mov     w0, #0x11d7
    378     b       #\\+0x8 \\(addr .*\\)
    379     mov     w0, #0x4d2`
    380   );
    381 }
    382 
    383 // For integer comparison followed by select, check that the comparison result
    384 // isn't materialised into a register, for specific types.
    385 
    386 for ( [cmpTy, cmpOp, selTy, cmpRegPfx, cselRegPfx, armCC] of
    387      [ ['i32', 'le_s', 'i32',  'w', 'w', 'le'],
    388        ['i32', 'lt_u', 'i64',  'w', 'x', 'lo'],
    389        ['i64', 'le_s', 'i32',  'x', 'w', 'le'],
    390        ['i64', 'lt_u', 'i64',  'x', 'x', 'lo'],
    391      ] ) {
    392   codegenTestARM64_adhoc(
    393    `(module
    394       (func (export "f")
    395             (param $p1 ${cmpTy}) (param $p2 ${cmpTy})
    396             (param $p3 ${selTy}) (param $p4 ${selTy})
    397             (result ${selTy})
    398         (select (local.get $p3)
    399                 (local.get $p4)
    400                 (${cmpTy}.${cmpOp} (local.get $p1) (local.get $p2)))
    401       )
    402    )`,
    403    'f',
    404    `cmp  ${cmpRegPfx}0, ${cmpRegPfx}1
    405     csel ${cselRegPfx}0, ${cselRegPfx}2, ${cselRegPfx}3, ${armCC}`
    406   );
    407 }