// |jit-test| skip-if: !hasDisassembler() || wasmCompileMode() != "ion" || getBuildConfiguration("windows") || !getBuildConfiguration("x64") || getBuildConfiguration("simulator"); include:codegen-x64-test.js

// Windows is disallowed because the argument registers are different from on
// Linux, and matching both is both difficult and not of much value.

// Signed 32-bit division with constants.
const i32_div_s = [
  // Division by zero.
  {
    divisor: 0,
    expected: `ud2`,
  },

  // Power of two divisor
  {
    divisor: 1,
    expected: `mov %edi, %eax`,
  },
  {
    divisor: 2,
    expected: `mov %edi, %eax
               shr \\$0x1F, %eax
               add %edi, %eax
               sar \\$0x01, %eax`,
  },
  {
    divisor: 4,
    expected: `mov %edi, %eax
               sar \\$0x1F, %eax
               shr \\$0x1E, %eax
               add %edi, %eax
               sar \\$0x02, %eax`,
  },

  // Division by -1 needs an overflow check.
  {
    divisor: -1,
    expected: `mov %edi, %eax
               neg %eax
               jno 0x${HEX}+
               ud2`,
  },

  // Other divisors.
  {
    divisor: 3,
    expected:  `movsxd %edi, %rax
                imul \\$0x55555556, %rax, %rax
                shr \\$0x20, %rax
                mov %edi, %ecx
                sar \\$0x1F, %ecx
                sub %ecx, %eax`,
  },
  {
    divisor: 5,
    expected: `movsxd %edi, %rax
               imul \\$0x66666667, %rax, %rax
               sar \\$0x21, %rax
               mov %edi, %ecx
               sar \\$0x1F, %ecx
               sub %ecx, %eax`,
  },
  {
    divisor: 7,
    expected: `movsxd %edi, %rax
               imul \\$-0x6DB6DB6D, %rax, %rax
               shr \\$0x20, %rax
               add %edi, %eax
               sar \\$0x02, %eax
               mov %edi, %ecx
               sar \\$0x1F, %ecx
               sub %ecx, %eax`,
  },
  {
    divisor: 9,
    expected: `movsxd %edi, %rax
               imul \\$0x38E38E39, %rax, %rax
               sar \\$0x21, %rax
               mov %edi, %ecx
               sar \\$0x1F, %ecx
               sub %ecx, %eax`,
  },
];

for (let {divisor, expected} of i32_div_s) {
  let divs32 =
    `(module
       (func (export "f") (param i32) (result i32)
         (i32.div_s (local.get 0) (i32.const ${divisor}))))`
  codegenTestX64_adhoc(divs32, 'f', expected);

  // Test negative divisors, too.
  if (divisor > 1) {
    let divs32 =
      `(module
         (func (export "f") (param i32) (result i32)
           (i32.div_s (local.get 0) (i32.const -${divisor}))))`
    codegenTestX64_adhoc(divs32, 'f', expected + `
      neg %eax`
    );
  }
}

// Unsigned 32-bit division with constants.
const i32_div_u = [
  // Division by zero.
  {
    divisor: 0,
    expected: `ud2`,
  },

  // Power of two divisor
  {
    divisor: 1,
    expected: `mov %edi, %ecx
               mov %ecx, %eax`,
  },
  {
    divisor: 2,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               shr \\$0x01, %eax`,
  },
  {
    divisor: 4,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               shr \\$0x02, %eax`,
  },

  // Other divisors.
  {
    divisor: 3,
    expected:  `mov %edi, %eax
                mov \\$-0x55555555, %ecx
                imul %rcx, %rax
                shr \\$0x21, %rax`,
  },
  {
    divisor: 5,
    expected: `mov %edi, %eax
               mov \\$-0x33333333, %ecx
               imul %rcx, %rax
               shr \\$0x22, %rax`,
  },
  {
    divisor: 7,
    expected: `mov %edi, %eax
               imul \\$0x24924925, %rax, %rax
               shr \\$0x20, %rax
               mov %edi, %ecx
               sub %eax, %ecx
               shr \\$0x01, %ecx
               add %ecx, %eax
               shr \\$0x02, %eax`,
  },
  {
    divisor: 9,
    expected: `mov %edi, %eax
               imul \\$0x38E38E39, %rax, %rax
               shr \\$0x21, %rax`,
  },

  // Special case: Zero (additional) shift amount.
  {
    divisor: 641,
    expected: `mov %edi, %eax
               imul \\$0x663D81, %rax, %rax
               shr \\$0x20, %rax`,
  },
];

for (let {divisor, expected} of i32_div_u) {
  let divu32 =
    `(module
       (func (export "f") (param i32) (result i32)
         (i32.div_u (local.get 0) (i32.const ${divisor}))))`
  codegenTestX64_adhoc(divu32, 'f', expected);
}

// Signed 64-bit division with constants.
const i64_div_s = [
  // Division by zero.
  {
    divisor: 0,
    expected: `ud2`,
  },

  // Power of two divisor
  {
    divisor: 1,
    expected: `mov %rdi, %rax`,
  },
  {
    divisor: 2,
    expected: `mov %rdi, %rax
               shr \\$0x3F, %rax
               add %rdi, %rax
               sar \\$0x01, %rax`,
  },
  {
    divisor: 4,
    expected: `mov %rdi, %rax
               sar \\$0x3F, %rax
               shr \\$0x3E, %rax
               add %rdi, %rax
               sar \\$0x02, %rax`,
  },
  {
    divisor: 0x1_0000_0000,
    expected: `mov %rdi, %rax
               sar \\$0x3F, %rax
               shr \\$0x20, %rax
               add %rdi, %rax
               sar \\$0x20, %rax`,
  },

  // Division by -1 needs an overflow check.
  {
    divisor: -1,
    expected: `mov %rdi, %rax
               neg %rax
               jno 0x${HEX}+
               ud2`,
  },

  // Other divisors.
  {
    divisor: 3,
    expected:  `mov \\$0x5555555555555556, %rax
                imul %rdi
                mov %rdi, %rax
                sar \\$0x3F, %rax
                sub %rax, %rdx`,
  },
  {
    divisor: 5,
    expected: `mov \\$0x6666666666666667, %rax
               imul %rdi
               sar \\$0x01, %rdx
               mov %rdi, %rax
               sar \\$0x3F, %rax
               sub %rax, %rdx`,
  },
  {
    divisor: 7,
    expected: `mov \\$0x4924924924924925, %rax
               imul %rdi
               sar \\$0x01, %rdx
               mov %rdi, %rax
               sar \\$0x3F, %rax
               sub %rax, %rdx`,
  },
  {
    divisor: 9,
    expected: `mov \\$0x1C71C71C71C71C72, %rax
               imul %rdi
               mov %rdi, %rax
               sar \\$0x3F, %rax
               sub %rax, %rdx`,
  },
];

for (let {divisor, expected} of i64_div_s) {
  let result = IsPowerOfTwo(divisor) ? "rax" : "rdx";
  let mov_rdx_to_rax = result !== "rax" ? `
    mov %rdx, %rax` : ``;

  let divs64 =
    `(module
       (func (export "f") (param i64) (result i64)
         (i64.div_s (local.get 0) (i64.const ${divisor}))))`
  codegenTestX64_adhoc(divs64, 'f', expected + mov_rdx_to_rax);

  // Test negative divisors, too.
  if (divisor > 1) {
    let divs64 =
      `(module
         (func (export "f") (param i64) (result i64)
           (i64.div_s (local.get 0) (i64.const -${divisor}))))`
    codegenTestX64_adhoc(divs64, 'f', expected + `
      neg %${result}` + mov_rdx_to_rax
    );
  }
}

function IsPowerOfTwo(x) {
  x = BigInt(x);
  if (x < 0) {
    x = -x;
  }
  return x && (x & (x - 1n)) === 0n;
}

// Unsigned 64-bit division with constants.
const i64_div_u = [
  // Division by zero.
  {
    divisor: 0,
    expected: `ud2
               mov %rdx, %rax`,
  },

  // Power of two divisor
  {
    divisor: 1,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax`,
  },
  {
    divisor: 2,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               shr \\$0x01, %rax`,
  },
  {
    divisor: 4,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               shr \\$0x02, %rax`,
  },
  {
    divisor: 0x1_0000_0000,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               shr \\$0x20, %rax`,
  },

  // Other divisors.
  {
    divisor: 3,
    expected:  `mov \\$-0x5555555555555555, %rax
                mul %rdi
                shr \\$0x01, %rdx
                mov %rdx, %rax`,
  },
  {
    divisor: 5,
    expected: `mov \\$-0x3333333333333333, %rax
               mul %rdi
               shr \\$0x02, %rdx
               mov %rdx, %rax`,
  },
  {
    divisor: 7,
    expected: `mov \\$0x2492492492492493, %rax
               mul %rdi
               mov %rdi, %rax
               sub %rdx, %rax
               shr \\$0x01, %rax
               add %rax, %rdx
               shr \\$0x02, %rdx
               mov %rdx, %rax`,
  },
  {
    divisor: 9,
    expected: `mov \\$-0x1C71C71C71C71C71, %rax
               mul %rdi
               shr \\$0x03, %rdx
               mov %rdx, %rax`,
  },

  // Special case: Zero shift amount.
  {
    divisor: 274177,
    expected: `mov \\$0x3D30F19CD101, %rax
               mul %rdi
               mov %rdx, %rax`,
  },
];

for (let {divisor, expected} of i64_div_u) {
  let divu64 =
    `(module
       (func (export "f") (param i64) (result i64)
         (i64.div_u (local.get 0) (i64.const ${divisor}))))`
  codegenTestX64_adhoc(divu64, 'f', expected);
}

//////////////



// Signed 32-bit remainder with constants.
const i32_rem_s = [
  // Division by zero.
  {
    divisor: 0,
    expected: `ud2`,
  },

  // Power of two divisor
  {
    divisor: 1,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               xor %eax, %eax`,
  },
  {
    divisor: 2,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               test %eax, %eax
               js 0x${HEX}+
               and \\$0x01, %eax
               jmp 0x${HEX}+
               neg %eax
               and \\$0x01, %eax
               neg %eax`,
  },
  {
    divisor: 4,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               test %eax, %eax
               js 0x${HEX}+
               and \\$0x03, %eax
               jmp 0x${HEX}+
               neg %eax
               and \\$0x03, %eax
               neg %eax`,
  },
  {
    divisor: 0x100,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               test %eax, %eax
               js 0x${HEX}+
               movzx %al, %eax
               jmp 0x${HEX}+
               neg %eax
               movzx %al, %eax
               neg %eax`,
  },
  {
    divisor: 0x10000,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               test %eax, %eax
               js 0x${HEX}+
               movzx %ax, %eax
               jmp 0x${HEX}+
               neg %eax
               movzx %ax, %eax
               neg %eax`,
  },
  {
    divisor: 0x8000_0000,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               test %eax, %eax
               js 0x${HEX}+
               and \\$0x7FFFFFFF, %eax
               jmp 0x${HEX}+
               neg %eax
               and \\$0x7FFFFFFF, %eax
               neg %eax`,
  },
];

for (let {divisor, expected} of i32_rem_s) {
  let rems32 =
    `(module
       (func (export "f") (param i32) (result i32)
         (i32.rem_s (local.get 0) (i32.const ${divisor}))))`
  codegenTestX64_adhoc(rems32, 'f', expected);

  // Test negative divisors, too.
  if (divisor > 0) {
    let rems32 =
      `(module
         (func (export "f") (param i32) (result i32)
           (i32.rem_s (local.get 0) (i32.const -${divisor}))))`
    codegenTestX64_adhoc(rems32, 'f', expected);
  }
}

// Unigned 32-bit remainder with constants.
const u32_rem_s = [
  // Division by zero.
  {
    divisor: 0,
    expected: `ud2`,
  },

  // Power of two divisor
  {
    divisor: 1,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               xor %eax, %eax`,
  },
  {
    divisor: 2,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               and \\$0x01, %eax`,
  },
  {
    divisor: 4,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               and \\$0x03, %eax`,
  },
  {
    divisor: 0x100,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               movzx %al, %eax`,
  },
  {
    divisor: 0x10000,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               movzx %ax, %eax`,
  },
  {
    divisor: 0x8000_0000,
    expected: `mov %edi, %ecx
               mov %ecx, %eax
               and \\$0x7FFFFFFF, %eax`,
  },
];

for (let {divisor, expected} of u32_rem_s) {
  let remu32 =
    `(module
       (func (export "f") (param i32) (result i32)
         (i32.rem_u (local.get 0) (i32.const ${divisor}))))`
  codegenTestX64_adhoc(remu32, 'f', expected);
}

// Signed 64-bit remainder with constants.
const i64_rem_s = [
  // Division by zero.
  {
    divisor: 0,
    expected: `ud2`,
  },

  // Power of two divisor
  {
    divisor: 1,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               xor %eax, %eax`,
  },
  {
    divisor: 2,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               test %rax, %rax
               js 0x${HEX}+
               and \\$0x01, %eax
               jmp 0x${HEX}+
               neg %rax
               and \\$0x01, %eax
               neg %rax`,
  },
  {
    divisor: 4,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               test %rax, %rax
               js 0x${HEX}+
               and \\$0x03, %eax
               jmp 0x${HEX}+
               neg %rax
               and \\$0x03, %eax
               neg %rax`,
  },
  {
    divisor: 0x100,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               test %rax, %rax
               js 0x${HEX}+
               movzx %al, %eax
               jmp 0x${HEX}+
               neg %rax
               movzx %al, %eax
               neg %rax`,
  },
  {
    divisor: 0x10000,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               test %rax, %rax
               js 0x${HEX}+
               movzx %ax, %eax
               jmp 0x${HEX}+
               neg %rax
               movzx %ax, %eax
               neg %rax`,
  },
  {
    divisor: 0x8000_0000,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               test %rax, %rax
               js 0x${HEX}+
               and \\$0x7FFFFFFF, %eax
               jmp 0x${HEX}+
               neg %rax
               and \\$0x7FFFFFFF, %eax
               neg %rax`,
  },
  {
    divisor: 0x1_0000_0000,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               test %rax, %rax
               js 0x${HEX}+
               mov %eax, %eax
               jmp 0x${HEX}+
               neg %rax
               mov %eax, %eax
               neg %rax`,
  },
  {
    divisor: 0x8000_0000_0000_0000n,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               test %rax, %rax
               js 0x${HEX}+
               mov \\$0x7FFFFFFFFFFFFFFF, %r11
               and %r11, %rax
               jmp 0x${HEX}+
               neg %rax
               mov \\$0x7FFFFFFFFFFFFFFF, %r11
               and %r11, %rax
               neg %rax`,
  },
];

for (let {divisor, expected} of i64_rem_s) {
  let rems64 =
    `(module
       (func (export "f") (param i64) (result i64)
         (i64.rem_s (local.get 0) (i64.const ${divisor}))))`
  codegenTestX64_adhoc(rems64, 'f', expected);

  // Test negative divisors, too.
  if (divisor > 0) {
    let rems64 =
      `(module
         (func (export "f") (param i64) (result i64)
           (i64.rem_s (local.get 0) (i64.const -${divisor}))))`
    codegenTestX64_adhoc(rems64, 'f', expected);
  }
}

// Unsigned 64-bit remainder with constants.
const i64_rem_u = [
  // Division by zero.
  {
    divisor: 0,
    expected: `ud2`,
  },

  // Power of two divisor
  {
    divisor: 1,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               xor %eax, %eax`,
  },
  {
    divisor: 2,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               and \\$0x01, %eax`,
  },
  {
    divisor: 4,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               and \\$0x03, %eax`,
  },
  {
    divisor: 0x100,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               movzx %al, %eax`,
  },
  {
    divisor: 0x10000,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               movzx %ax, %eax`,
  },
  {
    divisor: 0x8000_0000,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               and \\$0x7FFFFFFF, %eax`,
  },
  {
    divisor: 0x1_0000_0000,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               mov %eax, %eax`,
  },
  {
    divisor: 0x8000_0000_0000_0000n,
    expected: `mov %rdi, %rcx
               mov %rcx, %rax
               mov \\$0x7FFFFFFFFFFFFFFF, %r11
               and %r11, %rax`,
  },
];

for (let {divisor, expected} of i64_rem_u) {
  let remu64 =
    `(module
       (func (export "f") (param i64) (result i64)
         (i64.rem_u (local.get 0) (i64.const ${divisor}))))`
  codegenTestX64_adhoc(remu64, 'f', expected);
}
