; RUN: llc < %s -march=mips -mcpu=mips2 -relocation-model=pic | FileCheck %s \
; RUN:    --check-prefixes=ALL,GP32
; RUN: llc < %s -march=mips -mcpu=mips32 -relocation-model=pic | FileCheck %s \
; RUN:    --check-prefixes=ALL,GP32
; RUN: llc < %s -march=mips -mcpu=mips32r6 -relocation-model=pic | FileCheck %s \
; RUN:    --check-prefixes=ALL,GP32
; RUN: llc < %s -march=mips64 -mcpu=mips3 -relocation-model=pic | FileCheck %s \
; RUN:    --check-prefixes=ALL,GP64,N64
; RUN: llc < %s -march=mips64 -mcpu=mips64 -relocation-model=pic | FileCheck %s \
; RUN:    --check-prefixes=ALL,GP64,N64
; RUN: llc < %s -march=mips64 -mcpu=mips64r6 -relocation-model=pic | FileCheck %s \
; RUN:    --check-prefixes=ALL,GP64,N64
; RUN: llc < %s -march=mips64 -mcpu=mips3 -target-abi n32 -relocation-model=pic | FileCheck %s \
; RUN:    --check-prefixes=ALL,GP64,N32
; RUN: llc < %s -march=mips64 -mcpu=mips64 -target-abi n32 -relocation-model=pic | FileCheck %s \
; RUN:    --check-prefixes=ALL,GP64,N32
; RUN: llc < %s -march=mips64 -mcpu=mips64r6 -target-abi n32 -relocation-model=pic | FileCheck %s \
; RUN:    --check-prefixes=ALL,GP64,N32

; Check dynamic stack realignment in functions without variable-sized objects.

declare void @helper_01(i32, i32, i32, i32, i32*)

; O32 ABI
define void @func_01() {
entry:
; GP32-LABEL: func_01:

  ; prologue
  ; FIXME: We are currently over-allocating stack space. This particular case
  ;        needs a frame of up to between 16 and 512-bytes but currently
  ;        allocates between 1024 and 1536 bytes
  ; GP32:       addiu   $sp, $sp, -1024
  ; GP32:       sw      $ra, 1020($sp)
  ; GP32:       sw      $fp, 1016($sp)
  ;
  ; GP32:       move    $fp, $sp
  ; GP32:       addiu   $[[T0:[0-9]+|ra|gp]], $zero, -512
  ; GP32-NEXT:  and     $sp, $sp, $[[T0]]

  ; body
  ; GP32:       addiu   $[[T1:[0-9]+]], $sp, 512
  ; GP32:       sw      $[[T1]], 16($sp)

  ; epilogue
  ; GP32:       move    $sp, $fp
  ; GP32:       lw      $fp, 1016($sp)
  ; GP32:       lw      $ra, 1020($sp)
  ; GP32:       addiu   $sp, $sp, 1024

  %a = alloca i32, align 512
  call void @helper_01(i32 0, i32 0, i32 0, i32 0, i32* %a)
  ret void
}

declare void @helper_02(i32, i32, i32, i32,
                        i32, i32, i32, i32, i32*)

; N32/N64 ABIs
define void @func_02() {
entry:
; GP64-LABEL: func_02:

  ; prologue
  ; FIXME: We are currently over-allocating stack space. This particular case
  ;        needs a frame of up to between 16 and 512-bytes but currently
  ;        allocates between 1024 and 1536 bytes
  ; N32:        addiu   $sp, $sp, -1024
  ; N64:        daddiu  $sp, $sp, -1024
  ; GP64:       sd      $ra, 1016($sp)
  ; GP64:       sd      $fp, 1008($sp)
  ; N32:        sd      $gp, 1000($sp)
  ;
  ; GP64:       move    $fp, $sp
  ; N32:        addiu   $[[T0:[0-9]+|ra]], $zero, -512
  ; N64:        daddiu  $[[T0:[0-9]+|ra]], $zero, -512
  ; GP64-NEXT:  and     $sp, $sp, $[[T0]]

  ; body
  ; N32:        addiu   $[[T1:[0-9]+]], $sp, 512
  ; N64:        daddiu  $[[T1:[0-9]+]], $sp, 512
  ; GP64:       sd      $[[T1]], 0($sp)

  ; epilogue
  ; GP64:       move    $sp, $fp
  ; N32:        ld      $gp, 1000($sp)
  ; GP64:       ld      $fp, 1008($sp)
  ; GP64:       ld      $ra, 1016($sp)
  ; N32:        addiu   $sp, $sp, 1024
  ; N64:        daddiu  $sp, $sp, 1024

  %a = alloca i32, align 512
  call void @helper_02(i32 0, i32 0, i32 0, i32 0,
                       i32 0, i32 0, i32 0, i32 0, i32* %a)
  ret void
}

; Verify that we use $fp for referencing incoming arguments.

declare void @helper_03(i32, i32, i32, i32, i32*, i32*)

; O32 ABI
define void @func_03(i32 %p0, i32 %p1, i32 %p2, i32 %p3, i32* %b) {
entry:
; GP32-LABEL: func_03:

  ; body
  ; FIXME: We are currently over-allocating stack space.
  ; GP32-DAG:   addiu   $[[T0:[0-9]+]], $sp, 512
  ; GP32-DAG:   sw      $[[T0]], 16($sp)
  ; GP32-DAG:   lw      $[[T1:[0-9]+]], 1040($fp)
  ; GP32-DAG:   sw      $[[T1]], 20($sp)

  %a = alloca i32, align 512
  call void @helper_03(i32 0, i32 0, i32 0, i32 0, i32* %a, i32* %b)
  ret void
}

declare void @helper_04(i32, i32, i32, i32,
                        i32, i32, i32, i32, i32*, i32*)

; N32/N64 ABIs
define void @func_04(i32 %p0, i32 %p1, i32 %p2, i32 %p3,
                     i32 %p4, i32 %p5, i32 %p6, i32 %p7,
                     i32* %b) {
entry:
; GP64-LABEL: func_04:

  ; body
  ; FIXME: We are currently over-allocating stack space.
  ; N32-DAG:    addiu   $[[T0:[0-9]+]], $sp, 512
  ; N64-DAG:    daddiu  $[[T0:[0-9]+]], $sp, 512
  ; GP64-DAG:   sd      $[[T0]], 0($sp)
  ; GP64-DAG:   ld      $[[T1:[0-9]+]], 1024($fp)
  ; GP64-DAG:   sd      $[[T1]], 8($sp)

  %a = alloca i32, align 512
  call void @helper_04(i32 0, i32 0, i32 0, i32 0,
                       i32 0, i32 0, i32 0, i32 0, i32* %a, i32* %b)
  ret void
}

; Check dynamic stack realignment in functions with variable-sized objects.

; O32 ABI
define void @func_05(i32 %sz) {
entry:
; GP32-LABEL: func_05:

  ; prologue
  ; FIXME: We are currently over-allocating stack space.
  ; GP32:       addiu   $sp, $sp, -1024
  ; GP32:       sw      $fp, 1020($sp)
  ; GP32:       sw      $23, 1016($sp)
  ;
  ; GP32:       move    $fp, $sp
  ; GP32:       addiu   $[[T0:[0-9]+|gp]], $zero, -512
  ; GP32-NEXT:  and     $sp, $sp, $[[T0]]
  ; GP32-NEXT:  move    $23, $sp

  ; body
  ; GP32:       addiu   $[[T1:[0-9]+]], $zero, 222
  ; GP32:       sw      $[[T1]], 508($23)

  ; epilogue
  ; GP32:       move    $sp, $fp
  ; GP32:       lw      $23, 1016($sp)
  ; GP32:       lw      $fp, 1020($sp)
  ; GP32:       addiu   $sp, $sp, 1024

  %a0 = alloca i32, i32 %sz, align 512
  %a1 = alloca i32, align 4

  store volatile i32 111, i32* %a0, align 512
  store volatile i32 222, i32* %a1, align 4

  ret void
}

; N32/N64 ABIs
define void @func_06(i32 %sz) {
entry:
; GP64-LABEL: func_06:

  ; prologue
  ; FIXME: We are currently over-allocating stack space.
  ; N32:        addiu   $sp, $sp, -1024
  ; N64:        daddiu  $sp, $sp, -1024
  ; GP64:       sd      $fp, 1016($sp)
  ; GP64:       sd      $23, 1008($sp)
  ;
  ; GP64:       move    $fp, $sp
  ; GP64:       addiu   $[[T0:[0-9]+|gp]], $zero, -512
  ; GP64-NEXT:  and     $sp, $sp, $[[T0]]
  ; GP64-NEXT:  move    $23, $sp

  ; body
  ; GP64:       addiu   $[[T1:[0-9]+]], $zero, 222
  ; GP64:       sw      $[[T1]], 508($23)

  ; epilogue
  ; GP64:       move    $sp, $fp
  ; GP64:       ld      $23, 1008($sp)
  ; GP64:       ld      $fp, 1016($sp)
  ; N32:        addiu   $sp, $sp, 1024
  ; N64:        daddiu  $sp, $sp, 1024

  %a0 = alloca i32, i32 %sz, align 512
  %a1 = alloca i32, align 4

  store volatile i32 111, i32* %a0, align 512
  store volatile i32 222, i32* %a1, align 4

  ret void
}

; Verify that we use $fp for referencing incoming arguments and $sp for
; building outbound arguments for nested function calls.

; O32 ABI
define void @func_07(i32 %p0, i32 %p1, i32 %p2, i32 %p3, i32 %sz) {
entry:
; GP32-LABEL: func_07:

  ; body
  ; FIXME: We are currently over-allocating stack space.
  ; GP32-DAG:       lw      $[[T0:[0-9]+]], 1040($fp)
  ;
  ; GP32-DAG:       addiu   $[[T1:[0-9]+]], $zero, 222
  ; GP32-DAG:       sw      $[[T1]], 508($23)
  ;
  ; GP32-DAG:       sw      $[[T2:[0-9]+]], 16($sp)

  %a0 = alloca i32, i32 %sz, align 512
  %a1 = alloca i32, align 4

  store volatile i32 111, i32* %a0, align 512
  store volatile i32 222, i32* %a1, align 4

  call void @helper_01(i32 0, i32 0, i32 0, i32 0, i32* %a1)

  ret void
}

; N32/N64 ABIs
define void @func_08(i32 %p0, i32 %p1, i32 %p2, i32 %p3,
                     i32 %p4, i32 %p5, i32 %p6, i32 %p7,
                     i32 %sz) {
entry:
; GP64-LABEL: func_08:

  ; body
  ; FIXME: We are currently over-allocating stack space.
  ; N32-DAG:        lw      $[[T0:[0-9]+]], 1028($fp)
  ; N64-DAG:        lwu     $[[T0:[0-9]+]], 1028($fp)
  ;
  ; GP64-DAG:       addiu   $[[T1:[0-9]+]], $zero, 222
  ; GP64-DAG:       sw      $[[T1]], 508($23)
  ;
  ; GP64-DAG:       sd      $[[T2:[0-9]+]], 0($sp)

  %a0 = alloca i32, i32 %sz, align 512
  %a1 = alloca i32, align 4

  store volatile i32 111, i32* %a0, align 512
  store volatile i32 222, i32* %a1, align 4

  call void @helper_02(i32 0, i32 0, i32 0, i32 0,
                       i32 0, i32 0, i32 0, i32 0, i32* %a1)
  ret void
}

; Check that we do not perform dynamic stack realignment in the presence of
; the "no-realign-stack" function attribute.
define void @func_09() "no-realign-stack" {
entry:
; ALL-LABEL: func_09:

  ; ALL-NOT:  and     $sp, $sp, $[[T0:[0-9]+|ra|gp]]

  %a = alloca i32, align 512
  call void @helper_01(i32 0, i32 0, i32 0, i32 0, i32* %a)
  ret void
}

define void @func_10(i32 %sz) "no-realign-stack" {
entry:
; ALL-LABEL: func_10:

  ; ALL-NOT:  and     $sp, $sp, $[[T0:[0-9]+|ra|gp]]

  %a0 = alloca i32, i32 %sz, align 512
  %a1 = alloca i32, align 4

  store volatile i32 111, i32* %a0, align 512
  store volatile i32 222, i32* %a1, align 4

  ret void
}