blob: dde9ff10891856de4f3129ab64e38f0df0bc1975 [file] [edit]
; RUN: llc -verify-machineinstrs -mtriple=wasm32-unknown-unknown -mattr=+tail-call < %s | FileCheck --check-prefix=TAIL -DPTR=i32 %s
; RUN: llc -verify-machineinstrs -mtriple=wasm64-unknown-unknown -mattr=+tail-call < %s | FileCheck --check-prefix=TAIL -DPTR=i64 %s
; Stack protection must check the guard before a tail transfer and keep both
; halves of the selected call together when splitting the block.
declare i32 @tail_callee(i32)
define i32 @protected_tail_direct(i32 %x) sspreq {
; TAIL-LABEL: protected_tail_direct:
; TAIL: [[PTR]].load __stack_chk_guard
; TAIL: [[PTR]].store
; TAIL: [[PTR]].load __stack_chk_guard
; TAIL: [[PTR]].load
; TAIL: [[PTR]].eq
; TAIL: br_if
; TAIL: call __stack_chk_fail
; TAIL: unreachable
; TAIL: end_block
; TAIL: global.set __stack_pointer
; TAIL: return_call tail_callee
%r = tail call i32 @tail_callee(i32 %x)
ret i32 %r
}
define i32 @protected_tail_indirect(i32 %x, ptr %callee) sspreq {
; TAIL-LABEL: protected_tail_indirect:
; TAIL: [[PTR]].load __stack_chk_guard
; TAIL: [[PTR]].store
; TAIL: [[PTR]].load __stack_chk_guard
; TAIL: [[PTR]].load
; TAIL: [[PTR]].eq
; TAIL: br_if
; TAIL: call __stack_chk_fail
; TAIL: unreachable
; TAIL: end_block
; TAIL: global.set __stack_pointer
; TAIL: return_call_indirect
%r = tail call i32 %callee(i32 %x)
ret i32 %r
}
; Ordinary calls must still execute before the epilogue guard check.
define i32 @protected_notail(i32 %x) sspreq {
; TAIL-LABEL: protected_notail:
; TAIL: [[PTR]].load __stack_chk_guard
; TAIL: [[PTR]].store
; TAIL: {{^[[:space:]]*}}call tail_callee
; TAIL: [[PTR]].load __stack_chk_guard
; TAIL: [[PTR]].load
; TAIL: [[PTR]].eq
; TAIL: br_if
; TAIL: call __stack_chk_fail
; TAIL: unreachable
; TAIL: end_block
; TAIL: global.set __stack_pointer
; TAIL-NOT: return_call
%r = notail call i32 @tail_callee(i32 %x)
ret i32 %r
}