blob: f7bdcdd42ba13151360ff862212fd441b150c6a2 [file] [log] [blame]
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s --mtriple=wasm32-unknown-unknown -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck -DPTR=32 %s --check-prefix=CHECK-32
; RUN: llc < %s --mtriple=wasm64-unknown-unknown -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck -DPTR=64 %s --check-prefix=CHECK-64
declare void @ext_func(ptr %ptr)
declare void @ext_func_i32(ptr %ptr)
; Check that there is an extra local for the stack pointer.
define void @alloca32() noredzone {
; CHECK-32-LABEL: alloca32:
; CHECK-32: .functype alloca32 () -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push1=, __stack_pointer
; CHECK-32-NEXT: i32.const $push2=, 16
; CHECK-32-NEXT: i32.sub $push6=, $pop1, $pop2
; CHECK-32-NEXT: local.tee $push5=, 0, $pop6
; CHECK-32-NEXT: global.set __stack_pointer, $pop5
; CHECK-32-NEXT: local.get $push7=, 0
; CHECK-32-NEXT: i32.const $push0=, 0
; CHECK-32-NEXT: i32.store 12($pop7), $pop0
; CHECK-32-NEXT: local.get $push8=, 0
; CHECK-32-NEXT: i32.const $push3=, 16
; CHECK-32-NEXT: i32.add $push4=, $pop8, $pop3
; CHECK-32-NEXT: global.set __stack_pointer, $pop4
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: alloca32:
; CHECK-64: .functype alloca32 () -> ()
; CHECK-64-NEXT: .local i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push1=, __stack_pointer
; CHECK-64-NEXT: i64.const $push2=, 16
; CHECK-64-NEXT: i64.sub $push6=, $pop1, $pop2
; CHECK-64-NEXT: local.tee $push5=, 0, $pop6
; CHECK-64-NEXT: global.set __stack_pointer, $pop5
; CHECK-64-NEXT: local.get $push7=, 0
; CHECK-64-NEXT: i32.const $push0=, 0
; CHECK-64-NEXT: i32.store 12($pop7), $pop0
; CHECK-64-NEXT: local.get $push8=, 0
; CHECK-64-NEXT: i64.const $push3=, 16
; CHECK-64-NEXT: i64.add $push4=, $pop8, $pop3
; CHECK-64-NEXT: global.set __stack_pointer, $pop4
; CHECK-64-NEXT: return
%retval = alloca i32
store i32 0, ptr %retval
ret void
}
define void @alloca3264() {
; CHECK-32-LABEL: alloca3264:
; CHECK-32: .functype alloca3264 () -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push2=, __stack_pointer
; CHECK-32-NEXT: i32.const $push3=, 16
; CHECK-32-NEXT: i32.sub $push5=, $pop2, $pop3
; CHECK-32-NEXT: local.tee $push4=, 0, $pop5
; CHECK-32-NEXT: i64.const $push0=, 0
; CHECK-32-NEXT: i64.store 0($pop4), $pop0
; CHECK-32-NEXT: local.get $push6=, 0
; CHECK-32-NEXT: i32.const $push1=, 0
; CHECK-32-NEXT: i32.store 12($pop6), $pop1
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: alloca3264:
; CHECK-64: .functype alloca3264 () -> ()
; CHECK-64-NEXT: .local i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push2=, __stack_pointer
; CHECK-64-NEXT: i64.const $push3=, 16
; CHECK-64-NEXT: i64.sub $push5=, $pop2, $pop3
; CHECK-64-NEXT: local.tee $push4=, 0, $pop5
; CHECK-64-NEXT: i64.const $push0=, 0
; CHECK-64-NEXT: i64.store 0($pop4), $pop0
; CHECK-64-NEXT: local.get $push6=, 0
; CHECK-64-NEXT: i32.const $push1=, 0
; CHECK-64-NEXT: i32.store 12($pop6), $pop1
; CHECK-64-NEXT: return
%r1 = alloca i32
%r2 = alloca double
store i32 0, ptr %r1
store double 0.0, ptr %r2
ret void
}
define void @allocarray() {
; CHECK-32-LABEL: allocarray:
; CHECK-32: .functype allocarray () -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push3=, __stack_pointer
; CHECK-32-NEXT: i32.const $push4=, 144
; CHECK-32-NEXT: i32.sub $push9=, $pop3, $pop4
; CHECK-32-NEXT: local.tee $push8=, 0, $pop9
; CHECK-32-NEXT: global.set __stack_pointer, $pop8
; CHECK-32-NEXT: local.get $push10=, 0
; CHECK-32-NEXT: i32.const $push0=, 24
; CHECK-32-NEXT: i32.add $push1=, $pop10, $pop0
; CHECK-32-NEXT: i32.const $push2=, 1
; CHECK-32-NEXT: i32.store 0($pop1), $pop2
; CHECK-32-NEXT: local.get $push11=, 0
; CHECK-32-NEXT: i32.const $push7=, 1
; CHECK-32-NEXT: i32.store 12($pop11), $pop7
; CHECK-32-NEXT: local.get $push12=, 0
; CHECK-32-NEXT: i32.const $push5=, 144
; CHECK-32-NEXT: i32.add $push6=, $pop12, $pop5
; CHECK-32-NEXT: global.set __stack_pointer, $pop6
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: allocarray:
; CHECK-64: .functype allocarray () -> ()
; CHECK-64-NEXT: .local i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push3=, __stack_pointer
; CHECK-64-NEXT: i64.const $push4=, 144
; CHECK-64-NEXT: i64.sub $push9=, $pop3, $pop4
; CHECK-64-NEXT: local.tee $push8=, 0, $pop9
; CHECK-64-NEXT: global.set __stack_pointer, $pop8
; CHECK-64-NEXT: local.get $push10=, 0
; CHECK-64-NEXT: i64.const $push0=, 24
; CHECK-64-NEXT: i64.add $push1=, $pop10, $pop0
; CHECK-64-NEXT: i32.const $push2=, 1
; CHECK-64-NEXT: i32.store 0($pop1), $pop2
; CHECK-64-NEXT: local.get $push11=, 0
; CHECK-64-NEXT: i32.const $push7=, 1
; CHECK-64-NEXT: i32.store 12($pop11), $pop7
; CHECK-64-NEXT: local.get $push12=, 0
; CHECK-64-NEXT: i64.const $push5=, 144
; CHECK-64-NEXT: i64.add $push6=, $pop12, $pop5
; CHECK-64-NEXT: global.set __stack_pointer, $pop6
; CHECK-64-NEXT: return
%r = alloca [33 x i32]
store i32 1, ptr %r
%p2 = getelementptr [33 x i32], ptr %r, i32 0, i32 3
store i32 1, ptr %p2
ret void
}
define void @non_mem_use(ptr %addr) {
; CHECK-32-LABEL: non_mem_use:
; CHECK-32: .functype non_mem_use (i32) -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push0=, __stack_pointer
; CHECK-32-NEXT: i32.const $push1=, 48
; CHECK-32-NEXT: i32.sub $push9=, $pop0, $pop1
; CHECK-32-NEXT: local.tee $push8=, 1, $pop9
; CHECK-32-NEXT: global.set __stack_pointer, $pop8
; CHECK-32-NEXT: local.get $push10=, 1
; CHECK-32-NEXT: i32.const $push6=, 8
; CHECK-32-NEXT: i32.add $push7=, $pop10, $pop6
; CHECK-32-NEXT: call ext_func, $pop7
; CHECK-32-NEXT: local.get $push11=, 1
; CHECK-32-NEXT: call ext_func, $pop11
; CHECK-32-NEXT: local.get $push13=, 0
; CHECK-32-NEXT: local.get $push12=, 1
; CHECK-32-NEXT: i32.const $push4=, 16
; CHECK-32-NEXT: i32.add $push5=, $pop12, $pop4
; CHECK-32-NEXT: i32.store 0($pop13), $pop5
; CHECK-32-NEXT: local.get $push14=, 1
; CHECK-32-NEXT: i32.const $push2=, 48
; CHECK-32-NEXT: i32.add $push3=, $pop14, $pop2
; CHECK-32-NEXT: global.set __stack_pointer, $pop3
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: non_mem_use:
; CHECK-64: .functype non_mem_use (i64) -> ()
; CHECK-64-NEXT: .local i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push0=, __stack_pointer
; CHECK-64-NEXT: i64.const $push1=, 48
; CHECK-64-NEXT: i64.sub $push9=, $pop0, $pop1
; CHECK-64-NEXT: local.tee $push8=, 1, $pop9
; CHECK-64-NEXT: global.set __stack_pointer, $pop8
; CHECK-64-NEXT: local.get $push10=, 1
; CHECK-64-NEXT: i64.const $push6=, 8
; CHECK-64-NEXT: i64.add $push7=, $pop10, $pop6
; CHECK-64-NEXT: call ext_func, $pop7
; CHECK-64-NEXT: local.get $push11=, 1
; CHECK-64-NEXT: call ext_func, $pop11
; CHECK-64-NEXT: local.get $push13=, 0
; CHECK-64-NEXT: local.get $push12=, 1
; CHECK-64-NEXT: i64.const $push4=, 16
; CHECK-64-NEXT: i64.add $push5=, $pop12, $pop4
; CHECK-64-NEXT: i64.store 0($pop13), $pop5
; CHECK-64-NEXT: local.get $push14=, 1
; CHECK-64-NEXT: i64.const $push2=, 48
; CHECK-64-NEXT: i64.add $push3=, $pop14, $pop2
; CHECK-64-NEXT: global.set __stack_pointer, $pop3
; CHECK-64-NEXT: return
%buf = alloca [27 x i8], align 16
%r = alloca i64
%r2 = alloca i64
; %r is at SP+8
call void @ext_func(ptr %r)
; %r2 is at SP+0, no add needed
call void @ext_func(ptr %r2)
; Use as a value, but in a store
; %buf is at SP+16
store ptr %buf, ptr %addr
ret void
}
define void @allocarray_inbounds() {
; CHECK-32-LABEL: allocarray_inbounds:
; CHECK-32: .functype allocarray_inbounds () -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push2=, __stack_pointer
; CHECK-32-NEXT: i32.const $push3=, 32
; CHECK-32-NEXT: i32.sub $push8=, $pop2, $pop3
; CHECK-32-NEXT: local.tee $push7=, 0, $pop8
; CHECK-32-NEXT: global.set __stack_pointer, $pop7
; CHECK-32-NEXT: local.get $push9=, 0
; CHECK-32-NEXT: i32.const $push0=, 1
; CHECK-32-NEXT: i32.store 24($pop9), $pop0
; CHECK-32-NEXT: local.get $push10=, 0
; CHECK-32-NEXT: i32.const $push6=, 1
; CHECK-32-NEXT: i32.store 12($pop10), $pop6
; CHECK-32-NEXT: i32.const $push1=, 0
; CHECK-32-NEXT: call ext_func, $pop1
; CHECK-32-NEXT: local.get $push11=, 0
; CHECK-32-NEXT: i32.const $push4=, 32
; CHECK-32-NEXT: i32.add $push5=, $pop11, $pop4
; CHECK-32-NEXT: global.set __stack_pointer, $pop5
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: allocarray_inbounds:
; CHECK-64: .functype allocarray_inbounds () -> ()
; CHECK-64-NEXT: .local i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push2=, __stack_pointer
; CHECK-64-NEXT: i64.const $push3=, 32
; CHECK-64-NEXT: i64.sub $push8=, $pop2, $pop3
; CHECK-64-NEXT: local.tee $push7=, 0, $pop8
; CHECK-64-NEXT: global.set __stack_pointer, $pop7
; CHECK-64-NEXT: local.get $push9=, 0
; CHECK-64-NEXT: i32.const $push0=, 1
; CHECK-64-NEXT: i32.store 24($pop9), $pop0
; CHECK-64-NEXT: local.get $push10=, 0
; CHECK-64-NEXT: i32.const $push6=, 1
; CHECK-64-NEXT: i32.store 12($pop10), $pop6
; CHECK-64-NEXT: i64.const $push1=, 0
; CHECK-64-NEXT: call ext_func, $pop1
; CHECK-64-NEXT: local.get $push11=, 0
; CHECK-64-NEXT: i64.const $push4=, 32
; CHECK-64-NEXT: i64.add $push5=, $pop11, $pop4
; CHECK-64-NEXT: global.set __stack_pointer, $pop5
; CHECK-64-NEXT: return
%r = alloca [5 x i32]
store i32 1, ptr %r
; This store should have both the GEP and the FI folded into it.
%p2 = getelementptr inbounds [5 x i32], ptr %r, i32 0, i32 3
store i32 1, ptr %p2
call void @ext_func(ptr null);
ret void
}
define void @dynamic_alloca(i32 %alloc) {
; Target independent codegen bumps the stack pointer.
; Check that SP is written back to memory after decrement
; CHECK-32-LABEL: dynamic_alloca:
; CHECK-32: .functype dynamic_alloca (i32) -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push10=, __stack_pointer
; CHECK-32-NEXT: local.tee $push9=, 1, $pop10
; CHECK-32-NEXT: local.get $push11=, 0
; CHECK-32-NEXT: i32.const $push0=, 2
; CHECK-32-NEXT: i32.shl $push1=, $pop11, $pop0
; CHECK-32-NEXT: i32.const $push2=, 15
; CHECK-32-NEXT: i32.add $push3=, $pop1, $pop2
; CHECK-32-NEXT: i32.const $push4=, -16
; CHECK-32-NEXT: i32.and $push5=, $pop3, $pop4
; CHECK-32-NEXT: i32.sub $push8=, $pop9, $pop5
; CHECK-32-NEXT: local.tee $push7=, 0, $pop8
; CHECK-32-NEXT: global.set __stack_pointer, $pop7
; CHECK-32-NEXT: local.get $push12=, 0
; CHECK-32-NEXT: call ext_func_i32, $pop12
; CHECK-32-NEXT: local.get $push6=, 1
; CHECK-32-NEXT: global.set __stack_pointer, $pop6
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: dynamic_alloca:
; CHECK-64: .functype dynamic_alloca (i32) -> ()
; CHECK-64-NEXT: .local i64, i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push11=, __stack_pointer
; CHECK-64-NEXT: local.tee $push10=, 1, $pop11
; CHECK-64-NEXT: local.get $push12=, 0
; CHECK-64-NEXT: i64.extend_i32_u $push0=, $pop12
; CHECK-64-NEXT: i64.const $push1=, 2
; CHECK-64-NEXT: i64.shl $push2=, $pop0, $pop1
; CHECK-64-NEXT: i64.const $push3=, 15
; CHECK-64-NEXT: i64.add $push4=, $pop2, $pop3
; CHECK-64-NEXT: i64.const $push5=, 34359738352
; CHECK-64-NEXT: i64.and $push6=, $pop4, $pop5
; CHECK-64-NEXT: i64.sub $push9=, $pop10, $pop6
; CHECK-64-NEXT: local.tee $push8=, 2, $pop9
; CHECK-64-NEXT: global.set __stack_pointer, $pop8
; CHECK-64-NEXT: local.get $push13=, 2
; CHECK-64-NEXT: call ext_func_i32, $pop13
; CHECK-64-NEXT: local.get $push7=, 1
; CHECK-64-NEXT: global.set __stack_pointer, $pop7
; CHECK-64-NEXT: return
%r = alloca i32, i32 %alloc
; Target-independent codegen also calculates the store addr
call void @ext_func_i32(ptr %r)
ret void
}
define void @dynamic_alloca_redzone(i32 %alloc) {
; Target independent codegen bumps the stack pointer
; CHECK-32-LABEL: dynamic_alloca_redzone:
; CHECK-32: .functype dynamic_alloca_redzone (i32) -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push8=, __stack_pointer
; CHECK-32-NEXT: local.tee $push9=, 1, $pop8
; CHECK-32-NEXT: drop $pop9
; CHECK-32-NEXT: local.get $push11=, 1
; CHECK-32-NEXT: local.get $push10=, 0
; CHECK-32-NEXT: i32.const $push0=, 2
; CHECK-32-NEXT: i32.shl $push1=, $pop10, $pop0
; CHECK-32-NEXT: i32.const $push2=, 15
; CHECK-32-NEXT: i32.add $push3=, $pop1, $pop2
; CHECK-32-NEXT: i32.const $push4=, -16
; CHECK-32-NEXT: i32.and $push5=, $pop3, $pop4
; CHECK-32-NEXT: i32.sub $push7=, $pop11, $pop5
; CHECK-32-NEXT: local.tee $push12=, 0, $pop7
; CHECK-32-NEXT: drop $pop12
; CHECK-32-NEXT: local.get $push13=, 0
; CHECK-32-NEXT: i32.const $push6=, 0
; CHECK-32-NEXT: i32.store 0($pop13), $pop6
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: dynamic_alloca_redzone:
; CHECK-64: .functype dynamic_alloca_redzone (i32) -> ()
; CHECK-64-NEXT: .local i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push9=, __stack_pointer
; CHECK-64-NEXT: local.tee $push10=, 1, $pop9
; CHECK-64-NEXT: drop $pop10
; CHECK-64-NEXT: local.get $push12=, 1
; CHECK-64-NEXT: local.get $push11=, 0
; CHECK-64-NEXT: i64.extend_i32_u $push0=, $pop11
; CHECK-64-NEXT: i64.const $push1=, 2
; CHECK-64-NEXT: i64.shl $push2=, $pop0, $pop1
; CHECK-64-NEXT: i64.const $push3=, 15
; CHECK-64-NEXT: i64.add $push4=, $pop2, $pop3
; CHECK-64-NEXT: i64.const $push5=, 34359738352
; CHECK-64-NEXT: i64.and $push6=, $pop4, $pop5
; CHECK-64-NEXT: i64.sub $push8=, $pop12, $pop6
; CHECK-64-NEXT: local.tee $push13=, 1, $pop8
; CHECK-64-NEXT: drop $pop13
; CHECK-64-NEXT: local.get $push14=, 1
; CHECK-64-NEXT: i32.const $push7=, 0
; CHECK-64-NEXT: i32.store 0($pop14), $pop7
; CHECK-64-NEXT: return
%r = alloca i32, i32 %alloc
store i32 0, ptr %r
ret void
}
define void @dynamic_static_alloca(i32 %alloc) noredzone {
; Decrement SP in the prolog by the static amount and writeback to memory.
; Alloc and write to a static alloca
; CHECK-32-LABEL: dynamic_static_alloca:
; CHECK-32: .functype dynamic_static_alloca (i32) -> ()
; CHECK-32-NEXT: .local i32, i32, i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push11=, __stack_pointer
; CHECK-32-NEXT: i32.const $push12=, 16
; CHECK-32-NEXT: i32.sub $push25=, $pop11, $pop12
; CHECK-32-NEXT: local.tee $push24=, 1, $pop25
; CHECK-32-NEXT: global.set __stack_pointer, $pop24
; CHECK-32-NEXT: local.get $push23=, 1
; CHECK-32-NEXT: local.tee $push22=, 2, $pop23
; CHECK-32-NEXT: i32.const $push0=, 101
; CHECK-32-NEXT: i32.store 12($pop22), $pop0
; CHECK-32-NEXT: local.get $push27=, 1
; CHECK-32-NEXT: local.get $push26=, 0
; CHECK-32-NEXT: i32.const $push1=, 2
; CHECK-32-NEXT: i32.shl $push2=, $pop26, $pop1
; CHECK-32-NEXT: i32.const $push3=, 15
; CHECK-32-NEXT: i32.add $push4=, $pop2, $pop3
; CHECK-32-NEXT: i32.const $push5=, -16
; CHECK-32-NEXT: i32.and $push21=, $pop4, $pop5
; CHECK-32-NEXT: local.tee $push20=, 0, $pop21
; CHECK-32-NEXT: i32.sub $push19=, $pop27, $pop20
; CHECK-32-NEXT: local.tee $push18=, 1, $pop19
; CHECK-32-NEXT: local.tee $push17=, 3, $pop18
; CHECK-32-NEXT: global.set __stack_pointer, $pop17
; CHECK-32-NEXT: local.get $push28=, 2
; CHECK-32-NEXT: i32.const $push6=, 102
; CHECK-32-NEXT: i32.store 12($pop28), $pop6
; CHECK-32-NEXT: local.get $push29=, 1
; CHECK-32-NEXT: i32.const $push7=, 103
; CHECK-32-NEXT: i32.store 0($pop29), $pop7
; CHECK-32-NEXT: local.get $push31=, 3
; CHECK-32-NEXT: local.get $push30=, 0
; CHECK-32-NEXT: i32.sub $push16=, $pop31, $pop30
; CHECK-32-NEXT: local.tee $push15=, 0, $pop16
; CHECK-32-NEXT: global.set __stack_pointer, $pop15
; CHECK-32-NEXT: local.get $push32=, 2
; CHECK-32-NEXT: i32.const $push8=, 104
; CHECK-32-NEXT: i32.store 12($pop32), $pop8
; CHECK-32-NEXT: local.get $push33=, 1
; CHECK-32-NEXT: i32.const $push9=, 105
; CHECK-32-NEXT: i32.store 0($pop33), $pop9
; CHECK-32-NEXT: local.get $push34=, 0
; CHECK-32-NEXT: i32.const $push10=, 106
; CHECK-32-NEXT: i32.store 0($pop34), $pop10
; CHECK-32-NEXT: local.get $push35=, 2
; CHECK-32-NEXT: i32.const $push13=, 16
; CHECK-32-NEXT: i32.add $push14=, $pop35, $pop13
; CHECK-32-NEXT: global.set __stack_pointer, $pop14
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: dynamic_static_alloca:
; CHECK-64: .functype dynamic_static_alloca (i32) -> ()
; CHECK-64-NEXT: .local i64, i64, i64, i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push12=, __stack_pointer
; CHECK-64-NEXT: i64.const $push13=, 16
; CHECK-64-NEXT: i64.sub $push26=, $pop12, $pop13
; CHECK-64-NEXT: local.tee $push25=, 1, $pop26
; CHECK-64-NEXT: global.set __stack_pointer, $pop25
; CHECK-64-NEXT: local.get $push24=, 1
; CHECK-64-NEXT: local.tee $push23=, 2, $pop24
; CHECK-64-NEXT: i32.const $push0=, 101
; CHECK-64-NEXT: i32.store 12($pop23), $pop0
; CHECK-64-NEXT: local.get $push28=, 1
; CHECK-64-NEXT: local.get $push27=, 0
; CHECK-64-NEXT: i64.extend_i32_u $push1=, $pop27
; CHECK-64-NEXT: i64.const $push2=, 2
; CHECK-64-NEXT: i64.shl $push3=, $pop1, $pop2
; CHECK-64-NEXT: i64.const $push4=, 15
; CHECK-64-NEXT: i64.add $push5=, $pop3, $pop4
; CHECK-64-NEXT: i64.const $push6=, 34359738352
; CHECK-64-NEXT: i64.and $push22=, $pop5, $pop6
; CHECK-64-NEXT: local.tee $push21=, 3, $pop22
; CHECK-64-NEXT: i64.sub $push20=, $pop28, $pop21
; CHECK-64-NEXT: local.tee $push19=, 1, $pop20
; CHECK-64-NEXT: local.tee $push18=, 4, $pop19
; CHECK-64-NEXT: global.set __stack_pointer, $pop18
; CHECK-64-NEXT: local.get $push29=, 2
; CHECK-64-NEXT: i32.const $push7=, 102
; CHECK-64-NEXT: i32.store 12($pop29), $pop7
; CHECK-64-NEXT: local.get $push30=, 1
; CHECK-64-NEXT: i32.const $push8=, 103
; CHECK-64-NEXT: i32.store 0($pop30), $pop8
; CHECK-64-NEXT: local.get $push32=, 4
; CHECK-64-NEXT: local.get $push31=, 3
; CHECK-64-NEXT: i64.sub $push17=, $pop32, $pop31
; CHECK-64-NEXT: local.tee $push16=, 3, $pop17
; CHECK-64-NEXT: global.set __stack_pointer, $pop16
; CHECK-64-NEXT: local.get $push33=, 2
; CHECK-64-NEXT: i32.const $push9=, 104
; CHECK-64-NEXT: i32.store 12($pop33), $pop9
; CHECK-64-NEXT: local.get $push34=, 1
; CHECK-64-NEXT: i32.const $push10=, 105
; CHECK-64-NEXT: i32.store 0($pop34), $pop10
; CHECK-64-NEXT: local.get $push35=, 3
; CHECK-64-NEXT: i32.const $push11=, 106
; CHECK-64-NEXT: i32.store 0($pop35), $pop11
; CHECK-64-NEXT: local.get $push36=, 2
; CHECK-64-NEXT: i64.const $push14=, 16
; CHECK-64-NEXT: i64.add $push15=, $pop36, $pop14
; CHECK-64-NEXT: global.set __stack_pointer, $pop15
; CHECK-64-NEXT: return
%static = alloca i32
store volatile i32 101, ptr %static
; Decrement SP in the body by the dynamic amount.
%dynamic = alloca i32, i32 %alloc
; Ensure we don't modify the frame pointer after assigning it.
; Ensure the static address doesn't change after modifying the stack pointer.
store volatile i32 102, ptr %static
store volatile i32 103, ptr %dynamic
; Decrement SP in the body by the dynamic amount.
%dynamic.2 = alloca i32, i32 %alloc
; Ensure neither the static nor dynamic address changes after the second
; modification of the stack pointer.
store volatile i32 104, ptr %static
store volatile i32 105, ptr %dynamic
store volatile i32 106, ptr %dynamic.2
; Writeback to memory.
ret void
}
declare ptr @llvm.stacksave()
declare void @llvm.stackrestore(ptr)
define void @llvm_stack_builtins(i32 %alloc) noredzone {
; CHECK-32-LABEL: llvm_stack_builtins:
; CHECK-32: .functype llvm_stack_builtins (i32) -> ()
; CHECK-32-NEXT: .local i32, i32, i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push7=, __stack_pointer
; CHECK-32-NEXT: local.tee $push8=, 1, $pop7
; CHECK-32-NEXT: local.set 2, $pop8
; CHECK-32-NEXT: local.get $push9=, 1
; CHECK-32-NEXT: local.set 3, $pop9
; CHECK-32-NEXT: local.get $push11=, 1
; CHECK-32-NEXT: local.get $push10=, 0
; CHECK-32-NEXT: i32.const $push0=, 2
; CHECK-32-NEXT: i32.shl $push1=, $pop10, $pop0
; CHECK-32-NEXT: i32.const $push2=, 15
; CHECK-32-NEXT: i32.add $push3=, $pop1, $pop2
; CHECK-32-NEXT: i32.const $push4=, -16
; CHECK-32-NEXT: i32.and $push5=, $pop3, $pop4
; CHECK-32-NEXT: i32.sub $push6=, $pop11, $pop5
; CHECK-32-NEXT: global.set __stack_pointer, $pop6
; CHECK-32-NEXT: local.get $push12=, 3
; CHECK-32-NEXT: drop $pop12
; CHECK-32-NEXT: local.get $push13=, 2
; CHECK-32-NEXT: global.set __stack_pointer, $pop13
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: llvm_stack_builtins:
; CHECK-64: .functype llvm_stack_builtins (i32) -> ()
; CHECK-64-NEXT: .local i64, i64, i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push8=, __stack_pointer
; CHECK-64-NEXT: local.tee $push9=, 1, $pop8
; CHECK-64-NEXT: local.set 2, $pop9
; CHECK-64-NEXT: local.get $push10=, 1
; CHECK-64-NEXT: local.set 3, $pop10
; CHECK-64-NEXT: local.get $push12=, 1
; CHECK-64-NEXT: local.get $push11=, 0
; CHECK-64-NEXT: i64.extend_i32_u $push0=, $pop11
; CHECK-64-NEXT: i64.const $push1=, 2
; CHECK-64-NEXT: i64.shl $push2=, $pop0, $pop1
; CHECK-64-NEXT: i64.const $push3=, 15
; CHECK-64-NEXT: i64.add $push4=, $pop2, $pop3
; CHECK-64-NEXT: i64.const $push5=, 34359738352
; CHECK-64-NEXT: i64.and $push6=, $pop4, $pop5
; CHECK-64-NEXT: i64.sub $push7=, $pop12, $pop6
; CHECK-64-NEXT: global.set __stack_pointer, $pop7
; CHECK-64-NEXT: local.get $push13=, 3
; CHECK-64-NEXT: drop $pop13
; CHECK-64-NEXT: local.get $push14=, 2
; CHECK-64-NEXT: global.set __stack_pointer, $pop14
; CHECK-64-NEXT: return
%stack = call ptr @llvm.stacksave()
; Ensure we don't reassign the stacksave local
%dynamic = alloca i32, i32 %alloc
call void @llvm.stackrestore(ptr %stack)
ret void
}
; Use of stacksave requires local SP definition even without dymamic alloca.
; CHECK-LABEL: llvm_stacksave_noalloca:
define void @llvm_stacksave_noalloca() noredzone {
; CHECK: global.get $push[[L11:.+]]=, __stack_pointer{{$}}
%stack = call i8* @llvm.stacksave()
; CHECK-NEXT: call use_i8_star, $pop[[L11:.+]]
call void @use_i8_star(i8* %stack)
ret void
}
; Not actually using the alloca'd variables exposed an issue with register
; stackification, where copying the stack pointer into the frame pointer was
; moved after the stack pointer was updated for the dynamic alloca.
define void @dynamic_alloca_nouse(i32 %alloc) noredzone {
; CHECK-32-LABEL: dynamic_alloca_nouse:
; CHECK-32: .functype dynamic_alloca_nouse (i32) -> ()
; CHECK-32-NEXT: .local i32, i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push7=, __stack_pointer
; CHECK-32-NEXT: local.tee $push8=, 1, $pop7
; CHECK-32-NEXT: local.set 2, $pop8
; CHECK-32-NEXT: local.get $push10=, 1
; CHECK-32-NEXT: local.get $push9=, 0
; CHECK-32-NEXT: i32.const $push0=, 2
; CHECK-32-NEXT: i32.shl $push1=, $pop9, $pop0
; CHECK-32-NEXT: i32.const $push2=, 15
; CHECK-32-NEXT: i32.add $push3=, $pop1, $pop2
; CHECK-32-NEXT: i32.const $push4=, -16
; CHECK-32-NEXT: i32.and $push5=, $pop3, $pop4
; CHECK-32-NEXT: i32.sub $push6=, $pop10, $pop5
; CHECK-32-NEXT: global.set __stack_pointer, $pop6
; CHECK-32-NEXT: local.get $push11=, 2
; CHECK-32-NEXT: global.set __stack_pointer, $pop11
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: dynamic_alloca_nouse:
; CHECK-64: .functype dynamic_alloca_nouse (i32) -> ()
; CHECK-64-NEXT: .local i64, i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push8=, __stack_pointer
; CHECK-64-NEXT: local.tee $push9=, 1, $pop8
; CHECK-64-NEXT: local.set 2, $pop9
; CHECK-64-NEXT: local.get $push11=, 1
; CHECK-64-NEXT: local.get $push10=, 0
; CHECK-64-NEXT: i64.extend_i32_u $push0=, $pop10
; CHECK-64-NEXT: i64.const $push1=, 2
; CHECK-64-NEXT: i64.shl $push2=, $pop0, $pop1
; CHECK-64-NEXT: i64.const $push3=, 15
; CHECK-64-NEXT: i64.add $push4=, $pop2, $pop3
; CHECK-64-NEXT: i64.const $push5=, 34359738352
; CHECK-64-NEXT: i64.and $push6=, $pop4, $pop5
; CHECK-64-NEXT: i64.sub $push7=, $pop11, $pop6
; CHECK-64-NEXT: global.set __stack_pointer, $pop7
; CHECK-64-NEXT: local.get $push12=, 2
; CHECK-64-NEXT: global.set __stack_pointer, $pop12
; CHECK-64-NEXT: return
%dynamic = alloca i32, i32 %alloc
ret void
}
; The use of the alloca in a phi causes a CopyToReg DAG node to be generated,
; which has to have special handling because CopyToReg can't have a FI operand
define void @copytoreg_fi(i1 %cond, ptr %b) {
; CHECK-32-LABEL: copytoreg_fi:
; CHECK-32: .functype copytoreg_fi (i32, i32) -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0: # %entry
; CHECK-32-NEXT: global.get $push0=, __stack_pointer
; CHECK-32-NEXT: i32.const $push1=, 16
; CHECK-32-NEXT: i32.sub $push3=, $pop0, $pop1
; CHECK-32-NEXT: i32.const $push2=, 12
; CHECK-32-NEXT: i32.add $push6=, $pop3, $pop2
; CHECK-32-NEXT: local.set 2, $pop6
; CHECK-32-NEXT: local.get $push8=, 0
; CHECK-32-NEXT: i32.const $push4=, 1
; CHECK-32-NEXT: i32.and $push7=, $pop8, $pop4
; CHECK-32-NEXT: local.set 0, $pop7
; CHECK-32-NEXT: # %body
; CHECK-32-NEXT: # =>This Inner Loop Header: Depth=1
; CHECK-32-NEXT: loop # label0:
; CHECK-32-NEXT: local.get $push9=, 2
; CHECK-32-NEXT: i32.const $push5=, 1
; CHECK-32-NEXT: i32.store 0($pop9), $pop5
; CHECK-32-NEXT: local.get $push10=, 1
; CHECK-32-NEXT: local.set 2, $pop10
; CHECK-32-NEXT: local.get $push11=, 0
; CHECK-32-NEXT: br_if 0, $pop11 # 0: up to label0
; CHECK-32-NEXT: # %bb.2: # %exit
; CHECK-32-NEXT: end_loop
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: copytoreg_fi:
; CHECK-64: .functype copytoreg_fi (i32, i64) -> ()
; CHECK-64-NEXT: .local i64
; CHECK-64-NEXT: # %bb.0: # %entry
; CHECK-64-NEXT: global.get $push0=, __stack_pointer
; CHECK-64-NEXT: i64.const $push1=, 16
; CHECK-64-NEXT: i64.sub $push3=, $pop0, $pop1
; CHECK-64-NEXT: i64.const $push2=, 12
; CHECK-64-NEXT: i64.add $push6=, $pop3, $pop2
; CHECK-64-NEXT: local.set 2, $pop6
; CHECK-64-NEXT: local.get $push8=, 0
; CHECK-64-NEXT: i32.const $push4=, 1
; CHECK-64-NEXT: i32.and $push7=, $pop8, $pop4
; CHECK-64-NEXT: local.set 0, $pop7
; CHECK-64-NEXT: # %body
; CHECK-64-NEXT: # =>This Inner Loop Header: Depth=1
; CHECK-64-NEXT: loop # label0:
; CHECK-64-NEXT: local.get $push9=, 2
; CHECK-64-NEXT: i32.const $push5=, 1
; CHECK-64-NEXT: i32.store 0($pop9), $pop5
; CHECK-64-NEXT: local.get $push10=, 1
; CHECK-64-NEXT: local.set 2, $pop10
; CHECK-64-NEXT: local.get $push11=, 0
; CHECK-64-NEXT: br_if 0, $pop11 # 0: up to label0
; CHECK-64-NEXT: # %bb.2: # %exit
; CHECK-64-NEXT: end_loop
; CHECK-64-NEXT: return
entry:
%addr = alloca i32
br label %body
body:
%a = phi ptr [%addr, %entry], [%b, %body]
store i32 1, ptr %a
br i1 %cond, label %body, label %exit
exit:
ret void
}
declare void @use_i8_star(ptr)
declare ptr @llvm.frameaddress(i32)
; Test __builtin_frame_address(0).
define void @frameaddress_0() {
; CHECK-32-LABEL: frameaddress_0:
; CHECK-32: .functype frameaddress_0 () -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push1=, __stack_pointer
; CHECK-32-NEXT: local.tee $push0=, 0, $pop1
; CHECK-32-NEXT: call use_i8_star, $pop0
; CHECK-32-NEXT: local.get $push2=, 0
; CHECK-32-NEXT: global.set __stack_pointer, $pop2
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: frameaddress_0:
; CHECK-64: .functype frameaddress_0 () -> ()
; CHECK-64-NEXT: .local i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push1=, __stack_pointer
; CHECK-64-NEXT: local.tee $push0=, 0, $pop1
; CHECK-64-NEXT: call use_i8_star, $pop0
; CHECK-64-NEXT: local.get $push2=, 0
; CHECK-64-NEXT: global.set __stack_pointer, $pop2
; CHECK-64-NEXT: return
%t = call ptr @llvm.frameaddress(i32 0)
call void @use_i8_star(ptr %t)
ret void
}
; Test __builtin_frame_address(1).
define void @frameaddress_1() {
; CHECK-32-LABEL: frameaddress_1:
; CHECK-32: .functype frameaddress_1 () -> ()
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: i32.const $push0=, 0
; CHECK-32-NEXT: call use_i8_star, $pop0
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: frameaddress_1:
; CHECK-64: .functype frameaddress_1 () -> ()
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: i64.const $push0=, 0
; CHECK-64-NEXT: call use_i8_star, $pop0
; CHECK-64-NEXT: return
%t = call ptr @llvm.frameaddress(i32 1)
call void @use_i8_star(ptr %t)
ret void
}
; Test a stack address passed to an inline asm.
define void @inline_asm() {
; CHECK-32-LABEL: inline_asm:
; CHECK-32: .functype inline_asm () -> ()
; CHECK-32-NEXT: .local i32
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: global.get $push0=, __stack_pointer
; CHECK-32-NEXT: i32.const $push1=, 16
; CHECK-32-NEXT: i32.sub $push3=, $pop0, $pop1
; CHECK-32-NEXT: i32.const $push2=, 15
; CHECK-32-NEXT: i32.add $push4=, $pop3, $pop2
; CHECK-32-NEXT: local.set 0, $pop4
; CHECK-32-NEXT: #APP
; CHECK-32-NEXT: # %0
; CHECK-32-NEXT: #NO_APP
; CHECK-32-NEXT: return
;
; CHECK-64-LABEL: inline_asm:
; CHECK-64: .functype inline_asm () -> ()
; CHECK-64-NEXT: .local i64
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: global.get $push0=, __stack_pointer
; CHECK-64-NEXT: i64.const $push1=, 16
; CHECK-64-NEXT: i64.sub $push3=, $pop0, $pop1
; CHECK-64-NEXT: i64.const $push2=, 15
; CHECK-64-NEXT: i64.add $push4=, $pop3, $pop2
; CHECK-64-NEXT: local.set 0, $pop4
; CHECK-64-NEXT: #APP
; CHECK-64-NEXT: # %0
; CHECK-64-NEXT: #NO_APP
; CHECK-64-NEXT: return
%tmp = alloca i8
call void asm sideeffect "# %0", "r"(ptr %tmp)
ret void
}
; We optimize the format of "frame offset + operand" by folding it, but this is
; only possible when that operand is an immediate. In this example it is a
; global address, so we should not fold it.
@str = local_unnamed_addr global [3 x i8] c"abc", align 16
define i8 @frame_offset_with_global_address() {
; CHECK-32-LABEL: frame_offset_with_global_address:
; CHECK-32: .functype frame_offset_with_global_address () -> (i32)
; CHECK-32-NEXT: # %bb.0:
; CHECK-32-NEXT: i32.const $push0=, str
; CHECK-32-NEXT: global.get $push5=, __stack_pointer
; CHECK-32-NEXT: i32.const $push6=, 16
; CHECK-32-NEXT: i32.sub $push9=, $pop5, $pop6
; CHECK-32-NEXT: i32.const $push7=, 12
; CHECK-32-NEXT: i32.add $push8=, $pop9, $pop7
; CHECK-32-NEXT: i32.add $push1=, $pop0, $pop8
; CHECK-32-NEXT: i32.load8_u $push2=, 0($pop1)
; CHECK-32-NEXT: i32.const $push3=, 67
; CHECK-32-NEXT: i32.and $push4=, $pop2, $pop3
; CHECK-32-NEXT: return $pop4
;
; CHECK-64-LABEL: frame_offset_with_global_address:
; CHECK-64: .functype frame_offset_with_global_address () -> (i32)
; CHECK-64-NEXT: # %bb.0:
; CHECK-64-NEXT: i64.const $push1=, str
; CHECK-64-NEXT: global.get $push6=, __stack_pointer
; CHECK-64-NEXT: i64.const $push7=, 16
; CHECK-64-NEXT: i64.sub $push10=, $pop6, $pop7
; CHECK-64-NEXT: i64.const $push8=, 12
; CHECK-64-NEXT: i64.add $push9=, $pop10, $pop8
; CHECK-64-NEXT: i64.extend32_s $push0=, $pop9
; CHECK-64-NEXT: i64.add $push2=, $pop1, $pop0
; CHECK-64-NEXT: i32.load8_u $push3=, 0($pop2)
; CHECK-64-NEXT: i32.const $push4=, 67
; CHECK-64-NEXT: i32.and $push5=, $pop3, $pop4
; CHECK-64-NEXT: return $pop5
%1 = alloca i8, align 4
%2 = ptrtoint ptr %1 to i32
;; Here @str is a global address and not an immediate, so cannot be folded
%3 = getelementptr [3 x i8], ptr @str, i32 0, i32 %2
%4 = load i8, ptr %3, align 8
%5 = and i8 %4, 67
ret i8 %5
}
; TODO: test over-aligned alloca