// File: sid1607[P]avx2-merge-sort/merge_sort.cpp
// Function: merge_phase(int*, int*, int, int, int)
void merge_phase(int *a, int *out, int start, int mid, int end) {
  int i=start, j=mid+1, k=start;
  int i_end = i + mid - start + 1;
  int j_end = j + end - mid;

  auto ra = load_reg256(&a[i]);
  auto rb = load_reg256(&a[j]);

  i += SIMD_SIZE;
  j += SIMD_SIZE;

  // 8-by-8 merge
  if (mid-start+1 == SIMD_SIZE) {
    bitonic_merge(ra, rb);
    // save the smaller half
    store_reg256(&out[k], ra);
    k += SIMD_SIZE;
    // then save the larger half
    store_reg256(&out[k], rb);
    k += SIMD_SIZE;
    return;
  }

  do {
    bitonic_merge(ra, rb);
    
    // save the smaller half
    store_reg256(&out[k], ra);
    k += SIMD_SIZE;
    
    // use the larger half for the next comparison
    ra = rb;

    // select the input with the lowest value at the current pointer
    if (a[i] < a[j]) {
      rb = load_reg256(&a[i]);
      i += SIMD_SIZE;
    } else {
      rb = load_reg256(&a[j]);
      j += SIMD_SIZE;
    }
  } while (i < i_end && j < j_end);

  // merge the final pair of registers from each input
  bitonic_merge(ra, rb);
  store_reg256(&out[k], ra);
  k += SIMD_SIZE;
  ra = rb;

  // consume remaining data from a, if left
  while (i < i_end) {
    rb = load_reg256(&a[i]);
    i += SIMD_SIZE;
    bitonic_merge(ra, rb);
    store_reg256(&out[k], ra);
    k += SIMD_SIZE;
    ra = rb;
  }

  // consume remaining data from b, if left
  while (j < j_end) {
    rb = load_reg256(&a[j]);
    j += SIMD_SIZE;
    bitonic_merge(ra, rb);
    store_reg256(&out[k], ra);
    k += SIMD_SIZE;
    ra = rb;
  }

  // store the final batch
  store_reg256(&out[k], ra);
  k += SIMD_SIZE;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
andq $-0x20, %rsp
subq $0xa0, %rsp
movq %rdi, 0x88(%rsp)
movq %rsi, 0x80(%rsp)
movl %edx, 0x7c(%rsp)
movl %ecx, 0x78(%rsp)
movl %r8d, 0x74(%rsp)
movl 0x7c(%rsp), %eax
movl %eax, 0x70(%rsp)
movl 0x78(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x6c(%rsp)
movl 0x7c(%rsp), %eax
movl %eax, 0x68(%rsp)
movl 0x70(%rsp), %eax
addl 0x78(%rsp), %eax
subl 0x7c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x64(%rsp)
movl 0x6c(%rsp), %eax
addl 0x74(%rsp), %eax
subl 0x78(%rsp), %eax
movl %eax, 0x60(%rsp)
movq 0x88(%rsp), %rdi
movslq 0x70(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
callq 0x2b70
vmovdqa %ymm0, 0x40(%rsp)
movq 0x88(%rsp), %rdi
movslq 0x6c(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
callq 0x2b70
vmovdqa %ymm0, 0x20(%rsp)
movl 0x70(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x70(%rsp)
movl 0x6c(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x6c(%rsp)
movl 0x78(%rsp), %eax
subl 0x7c(%rsp), %eax
addl $0x1, %eax
cmpl $0x8, %eax
jne 0x25c7
leaq 0x40(%rsp), %rdi
leaq 0x20(%rsp), %rsi
vzeroupper
callq 0x2b90
movq 0x80(%rsp), %rdi
movslq 0x68(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
leaq 0x40(%rsp), %rsi
callq 0x2be0
movl 0x68(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x68(%rsp)
movq 0x80(%rsp), %rdi
movslq 0x68(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
leaq 0x20(%rsp), %rsi
callq 0x2be0
movl 0x68(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x68(%rsp)
jmp 0x2824
jmp 0x25c9
leaq 0x40(%rsp), %rdi
leaq 0x20(%rsp), %rsi
vzeroupper
callq 0x2b90
movq 0x80(%rsp), %rdi
movslq 0x68(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
leaq 0x40(%rsp), %rsi
callq 0x2be0
movl 0x68(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x68(%rsp)
vmovdqa 0x20(%rsp), %ymm0
vmovdqa %ymm0, 0x40(%rsp)
movq 0x88(%rsp), %rax
movslq 0x70(%rsp), %rcx
movl (%rax,%rcx,4), %eax
movq 0x88(%rsp), %rcx
movslq 0x6c(%rsp), %rdx
cmpl (%rcx,%rdx,4), %eax
jge 0x265e
movq 0x88(%rsp), %rdi
movslq 0x70(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
callq 0x2b70
vmovdqa %ymm0, 0x20(%rsp)
movl 0x70(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x70(%rsp)
jmp 0x2688
movq 0x88(%rsp), %rdi
movslq 0x6c(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
callq 0x2b70
vmovdqa %ymm0, 0x20(%rsp)
movl 0x6c(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x6c(%rsp)
jmp 0x268a
movl 0x70(%rsp), %ecx
xorl %eax, %eax
cmpl 0x64(%rsp), %ecx
movb %al, 0x1f(%rsp)
jge 0x26a9
movl 0x6c(%rsp), %eax
cmpl 0x60(%rsp), %eax
setl %al
movb %al, 0x1f(%rsp)
movb 0x1f(%rsp), %al
testb $0x1, %al
jne 0x25c9
leaq 0x40(%rsp), %rdi
leaq 0x20(%rsp), %rsi
vzeroupper
callq 0x2b90
movq 0x80(%rsp), %rdi
movslq 0x68(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
leaq 0x40(%rsp), %rsi
callq 0x2be0
movl 0x68(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x68(%rsp)
vmovdqa 0x20(%rsp), %ymm0
vmovdqa %ymm0, 0x40(%rsp)
movl 0x70(%rsp), %eax
cmpl 0x64(%rsp), %eax
jge 0x2779
movq 0x88(%rsp), %rdi
movslq 0x70(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
callq 0x2b70
vmovdqa %ymm0, 0x20(%rsp)
movl 0x70(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x70(%rsp)
leaq 0x40(%rsp), %rdi
leaq 0x20(%rsp), %rsi
vzeroupper
callq 0x2b90
movq 0x80(%rsp), %rdi
movslq 0x68(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
leaq 0x40(%rsp), %rsi
callq 0x2be0
movl 0x68(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x68(%rsp)
vmovdqa 0x20(%rsp), %ymm0
vmovdqa %ymm0, 0x40(%rsp)
jmp 0x26fc
jmp 0x277b
movl 0x6c(%rsp), %eax
cmpl 0x60(%rsp), %eax
jge 0x27f8
movq 0x88(%rsp), %rdi
movslq 0x6c(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
callq 0x2b70
vmovdqa %ymm0, 0x20(%rsp)
movl 0x6c(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x6c(%rsp)
leaq 0x40(%rsp), %rdi
leaq 0x20(%rsp), %rsi
vzeroupper
callq 0x2b90
movq 0x80(%rsp), %rdi
movslq 0x68(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
leaq 0x40(%rsp), %rsi
callq 0x2be0
movl 0x68(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x68(%rsp)
vmovdqa 0x20(%rsp), %ymm0
vmovdqa %ymm0, 0x40(%rsp)
jmp 0x277b
movq 0x80(%rsp), %rdi
movslq 0x68(%rsp), %rax
shlq $0x2, %rax
addq %rax, %rdi
leaq 0x40(%rsp), %rsi
vzeroupper
callq 0x2be0
movl 0x68(%rsp), %eax
addl $0x8, %eax
movl %eax, 0x68(%rsp)
movq %rbp, %rsp
popq %rbp
retq
nopl (%rax)