1; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs | FileCheck %s 2; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-disable-explicit-locals -wasm-keep-registers -verify-machineinstrs -fast-isel | FileCheck %s 3 4target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" 5target triple = "wasm32-unknown-unknown" 6 7%SmallStruct = type { i32 } 8%OddStruct = type { i32, i8, i32 } 9%AlignedStruct = type { double, double } 10%BigStruct = type { double, double, double, double, double, double, double, double, double, double, double, i8, i8, i8 } 11%EmptyStruct = type { } 12 13declare void @ext_func(%SmallStruct*) 14declare void @ext_func_empty(%EmptyStruct* byval(%EmptyStruct)) 15declare void @ext_byval_func(%SmallStruct* byval(%SmallStruct)) 16declare void @ext_byval_func_align8(%SmallStruct* byval(%SmallStruct) align 8) 17declare void @ext_byval_func_alignedstruct(%AlignedStruct* byval(%AlignedStruct)) 18declare void @ext_byval_func_empty(%EmptyStruct* byval(%EmptyStruct)) 19 20; CHECK-LABEL: byval_arg 21define void @byval_arg(%SmallStruct* %ptr) { 22 ; CHECK: .functype byval_arg (i32) -> () 23 ; Subtract 16 from SP (SP is 16-byte aligned) 24 ; CHECK-NEXT: global.get $push[[L2:.+]]=, __stack_pointer 25 ; CHECK-NEXT: i32.const $push[[L3:.+]]=, 16 26 ; CHECK-NEXT: i32.sub $push[[L11:.+]]=, $pop[[L2]], $pop[[L3]] 27 ; Ensure SP is stored back before the call 28 ; CHECK-NEXT: local.tee $push[[L10:.+]]=, $[[SP:.+]]=, $pop[[L11]]{{$}} 29 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L10]]{{$}} 30 ; Copy the SmallStruct argument to the stack (SP+12, original SP-4) 31 ; CHECK-NEXT: i32.load $push[[L0:.+]]=, 0($0) 32 ; CHECK-NEXT: i32.store 12($[[SP]]), $pop[[L0]] 33 ; Pass a pointer to the stack slot to the function 34 ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 12{{$}} 35 ; CHECK-NEXT: i32.add $push[[ARG:.+]]=, $[[SP]], $pop[[L5]]{{$}} 36 ; CHECK-NEXT: call ext_byval_func, $pop[[ARG]]{{$}} 37 call void @ext_byval_func(%SmallStruct* byval(%SmallStruct) %ptr) 38 ; Restore the stack 39 ; CHECK-NEXT: i32.const $push[[L6:.+]]=, 16 40 ; CHECK-NEXT: i32.add $push[[L8:.+]]=, $[[SP]], $pop[[L6]] 41 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L8]] 42 ; CHECK-NEXT: return 43 ret void 44} 45 46; CHECK-LABEL: byval_arg_align8 47define void @byval_arg_align8(%SmallStruct* %ptr) { 48 ; CHECK: .functype byval_arg_align8 (i32) -> () 49 ; Don't check the entire SP sequence, just enough to get the alignment. 50 ; CHECK: i32.const $push[[L1:.+]]=, 16 51 ; CHECK-NEXT: i32.sub $push[[L11:.+]]=, {{.+}}, $pop[[L1]] 52 ; CHECK-NEXT: local.tee $push[[L10:.+]]=, $[[SP:.+]]=, $pop[[L11]]{{$}} 53 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L10]]{{$}} 54 ; Copy the SmallStruct argument to the stack (SP+8, original SP-8) 55 ; CHECK-NEXT: i32.load $push[[L0:.+]]=, 0($0){{$}} 56 ; CHECK-NEXT: i32.store 8($[[SP]]), $pop[[L0]]{{$}} 57 ; Pass a pointer to the stack slot to the function 58 ; CHECK-NEXT: i32.const $push[[L5:.+]]=, 8{{$}} 59 ; CHECK-NEXT: i32.add $push[[ARG:.+]]=, $[[SP]], $pop[[L5]]{{$}} 60 ; CHECK-NEXT: call ext_byval_func_align8, $pop[[ARG]]{{$}} 61 call void @ext_byval_func_align8(%SmallStruct* byval(%SmallStruct) align 8 %ptr) 62 ret void 63} 64 65; CHECK-LABEL: byval_arg_double 66define void @byval_arg_double(%AlignedStruct* %ptr) { 67 ; CHECK: .functype byval_arg_double (i32) -> () 68 ; Subtract 16 from SP (SP is 16-byte aligned) 69 ; CHECK: i32.const $push[[L1:.+]]=, 16 70 ; CHECK-NEXT: i32.sub $push[[L14:.+]]=, {{.+}}, $pop[[L1]] 71 ; CHECK-NEXT: local.tee $push[[L13:.+]]=, $[[SP:.+]]=, $pop[[L14]] 72 ; CHECK-NEXT: global.set __stack_pointer, $pop[[L13]] 73 ; Copy the AlignedStruct argument to the stack (SP+0, original SP-16) 74 ; Just check the last load/store pair of the memcpy 75 ; CHECK: i64.load $push[[L4:.+]]=, 0($0) 76 ; CHECK-NEXT: i64.store 0($[[SP]]), $pop[[L4]] 77 ; Pass a pointer to the stack slot to the function 78 ; CHECK-NEXT: call ext_byval_func_alignedstruct, $[[SP]] 79 tail call void @ext_byval_func_alignedstruct(%AlignedStruct* byval(%AlignedStruct) %ptr) 80 ret void 81} 82 83; CHECK-LABEL: byval_param 84define void @byval_param(%SmallStruct* byval(%SmallStruct) align 32 %ptr) { 85 ; CHECK: .functype byval_param (i32) -> () 86 ; %ptr is just a pointer to a struct, so pass it directly through 87 ; CHECK: call ext_func, $0 88 call void @ext_func(%SmallStruct* %ptr) 89 ret void 90} 91 92; CHECK-LABEL: byval_empty_caller 93define void @byval_empty_caller(%EmptyStruct* %ptr) { 94 ; CHECK: .functype byval_empty_caller (i32) -> () 95 ; CHECK: call ext_byval_func_empty, $0 96 call void @ext_byval_func_empty(%EmptyStruct* byval(%EmptyStruct) %ptr) 97 ret void 98} 99 100; CHECK-LABEL: byval_empty_callee 101define void @byval_empty_callee(%EmptyStruct* byval(%EmptyStruct) %ptr) { 102 ; CHECK: .functype byval_empty_callee (i32) -> () 103 ; CHECK: call ext_func_empty, $0 104 call void @ext_func_empty(%EmptyStruct* %ptr) 105 ret void 106} 107 108; Call memcpy for "big" byvals. 109; CHECK-LABEL: big_byval: 110; CHECK: global.get $push[[L2:.+]]=, __stack_pointer{{$}} 111; CHECK-NEXT: i32.const $push[[L3:.+]]=, 131072 112; CHECK-NEXT: i32.sub $push[[L11:.+]]=, $pop[[L2]], $pop[[L3]] 113; CHECK-NEXT: local.tee $push[[L10:.+]]=, $[[SP:.+]]=, $pop[[L11]]{{$}} 114; CHECK-NEXT: global.set __stack_pointer, $pop[[L10]]{{$}} 115; CHECK-NEXT: i32.const $push[[L0:.+]]=, 131072 116; CHECK-NEXT: call $push[[L11:.+]]=, memcpy, $[[SP]], ${{.+}}, $pop{{.+}} 117; CHECK-NEXT: local.tee $push[[L9:.+]]=, $[[SP:.+]]=, $pop[[L11]]{{$}} 118; CHECK-NEXT: call big_byval_callee, 119%big = type [131072 x i8] 120declare void @big_byval_callee(%big* byval(%big) align 1) 121define void @big_byval(%big* byval(%big) align 1 %x) { 122 call void @big_byval_callee(%big* byval(%big) align 1 %x) 123 ret void 124} 125