1; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -mattr=+sign-ext,+simd128 | FileCheck --check-prefixes=CHECK,SLOW,NO-TAIL %s 2; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -fast-isel -fast-isel-abort=1 -mattr=+sign-ext,+simd128 | FileCheck --check-prefixes=CHECK,FAST,NO-TAIL %s 3; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -mattr=+sign-ext,+simd128,+tail-call | FileCheck --check-prefixes=CHECK,SLOW-TAIL %s 4; RUN: llc < %s -asm-verbose=false -disable-wasm-fallthrough-return-opt -wasm-keep-registers -fast-isel -fast-isel-abort=1 -mattr=+sign-ext,+simd128,+tail-call | FileCheck --check-prefixes=CHECK,FAST-TAIL %s 5 6; Test that basic call operations assemble as expected. 7 8target triple = "wasm32-unknown-unknown" 9 10declare i32 @i32_nullary() 11declare i32 @i32_unary(i32) 12declare i32 @i32_binary(i32, i32) 13declare i64 @i64_nullary() 14declare float @float_nullary() 15declare double @double_nullary() 16declare <16 x i8> @v128_nullary() 17declare void @void_nullary() 18 19; CHECK-LABEL: call_i32_nullary: 20; CHECK-NEXT: .functype call_i32_nullary () -> (i32){{$}} 21; CHECK-NEXT: {{^}} call $push[[NUM:[0-9]+]]=, i32_nullary{{$}} 22; CHECK-NEXT: return $pop[[NUM]]{{$}} 23define i32 @call_i32_nullary() { 24 %r = call i32 @i32_nullary() 25 ret i32 %r 26} 27 28; CHECK-LABEL: call_i64_nullary: 29; CHECK-NEXT: .functype call_i64_nullary () -> (i64){{$}} 30; CHECK-NEXT: {{^}} call $push[[NUM:[0-9]+]]=, i64_nullary{{$}} 31; CHECK-NEXT: return $pop[[NUM]]{{$}} 32define i64 @call_i64_nullary() { 33 %r = call i64 @i64_nullary() 34 ret i64 %r 35} 36 37; CHECK-LABEL: call_float_nullary: 38; CHECK-NEXT: .functype call_float_nullary () -> (f32){{$}} 39; CHECK-NEXT: {{^}} call $push[[NUM:[0-9]+]]=, float_nullary{{$}} 40; CHECK-NEXT: return $pop[[NUM]]{{$}} 41define float @call_float_nullary() { 42 %r = call float @float_nullary() 43 ret float %r 44} 45 46; CHECK-LABEL: call_double_nullary: 47; CHECK-NEXT: .functype call_double_nullary () -> (f64){{$}} 48; CHECK-NEXT: {{^}} call $push[[NUM:[0-9]+]]=, double_nullary{{$}} 49; CHECK-NEXT: return $pop[[NUM]]{{$}} 50define double @call_double_nullary() { 51 %r = call double @double_nullary() 52 ret double %r 53} 54 55; CHECK-LABEL: call_v128_nullary: 56; CHECK-NEXT: .functype call_v128_nullary () -> (v128){{$}} 57; CHECK-NEXT: {{^}} call $push[[NUM:[0-9]+]]=, v128_nullary{{$}} 58; CHECK-NEXT: return $pop[[NUM]]{{$}} 59define <16 x i8> @call_v128_nullary() { 60 %r = call <16 x i8> @v128_nullary() 61 ret <16 x i8> %r 62} 63 64; CHECK-LABEL: call_void_nullary: 65; CHECK-NEXT: .functype call_void_nullary () -> (){{$}} 66; CHECK-NEXT: {{^}} call void_nullary{{$}} 67; CHECK-NEXT: return{{$}} 68define void @call_void_nullary() { 69 call void @void_nullary() 70 ret void 71} 72 73; CHECK-LABEL: call_i32_unary: 74; CHECK-NEXT: .functype call_i32_unary (i32) -> (i32){{$}} 75; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}} 76; CHECK-NEXT: {{^}} call $push[[NUM:[0-9]+]]=, i32_unary, $pop[[L0]]{{$}} 77; CHECK-NEXT: return $pop[[NUM]]{{$}} 78define i32 @call_i32_unary(i32 %a) { 79 %r = call i32 @i32_unary(i32 %a) 80 ret i32 %r 81} 82 83; CHECK-LABEL: call_i32_binary: 84; CHECK-NEXT: .functype call_i32_binary (i32, i32) -> (i32){{$}} 85; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}} 86; CHECK-NEXT: local.get $push[[L1:[0-9]+]]=, 1{{$}} 87; CHECK-NEXT: {{^}} call $push[[NUM:[0-9]+]]=, i32_binary, $pop[[L0]], $pop[[L1]]{{$}} 88; CHECK-NEXT: return $pop[[NUM]]{{$}} 89define i32 @call_i32_binary(i32 %a, i32 %b) { 90 %r = call i32 @i32_binary(i32 %a, i32 %b) 91 ret i32 %r 92} 93 94; CHECK-LABEL: call_indirect_void: 95; CHECK-NEXT: .functype call_indirect_void (i32) -> (){{$}} 96; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}} 97; CHECK-NEXT: {{^}} call_indirect $pop[[L0]]{{$}} 98; CHECK-NEXT: return{{$}} 99define void @call_indirect_void(ptr %callee) { 100 call void %callee() 101 ret void 102} 103 104; CHECK-LABEL: call_indirect_i32: 105; CHECK-NEXT: .functype call_indirect_i32 (i32) -> (i32){{$}} 106; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}} 107; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}} 108; CHECK-NEXT: return $pop[[NUM]]{{$}} 109define i32 @call_indirect_i32(ptr %callee) { 110 %t = call i32 %callee() 111 ret i32 %t 112} 113 114; CHECK-LABEL: call_indirect_i64: 115; CHECK-NEXT: .functype call_indirect_i64 (i32) -> (i64){{$}} 116; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}} 117; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}} 118; CHECK-NEXT: return $pop[[NUM]]{{$}} 119define i64 @call_indirect_i64(ptr %callee) { 120 %t = call i64 %callee() 121 ret i64 %t 122} 123 124; CHECK-LABEL: call_indirect_float: 125; CHECK-NEXT: .functype call_indirect_float (i32) -> (f32){{$}} 126; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}} 127; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}} 128; CHECK-NEXT: return $pop[[NUM]]{{$}} 129define float @call_indirect_float(ptr %callee) { 130 %t = call float %callee() 131 ret float %t 132} 133 134; CHECK-LABEL: call_indirect_double: 135; CHECK-NEXT: .functype call_indirect_double (i32) -> (f64){{$}} 136; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}} 137; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}} 138; CHECK-NEXT: return $pop[[NUM]]{{$}} 139define double @call_indirect_double(ptr %callee) { 140 %t = call double %callee() 141 ret double %t 142} 143 144; CHECK-LABEL: call_indirect_v128: 145; CHECK-NEXT: .functype call_indirect_v128 (i32) -> (v128){{$}} 146; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 0{{$}} 147; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]]{{$}} 148; CHECK-NEXT: return $pop[[NUM]]{{$}} 149define <16 x i8> @call_indirect_v128(ptr %callee) { 150 %t = call <16 x i8> %callee() 151 ret <16 x i8> %t 152} 153 154; CHECK-LABEL: call_indirect_arg: 155; CHECK-NEXT: .functype call_indirect_arg (i32, i32) -> (){{$}} 156; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 1{{$}} 157; CHECK-NEXT: local.get $push[[L1:[0-9]+]]=, 0{{$}} 158; CHECK-NEXT: {{^}} call_indirect $pop[[L0]], $pop[[L1]]{{$}} 159; CHECK-NEXT: return{{$}} 160define void @call_indirect_arg(ptr %callee, i32 %arg) { 161 call void %callee(i32 %arg) 162 ret void 163} 164 165; CHECK-LABEL: call_indirect_arg_2: 166; CHECK-NEXT: .functype call_indirect_arg_2 (i32, i32, i32) -> (){{$}} 167; CHECK-NEXT: local.get $push[[L0:[0-9]+]]=, 1{{$}} 168; CHECK-NEXT: local.get $push[[L1:[0-9]+]]=, 2{{$}} 169; CHECK-NEXT: local.get $push[[L2:[0-9]+]]=, 0{{$}} 170; CHECK-NEXT: {{^}} call_indirect $push[[NUM:[0-9]+]]=, $pop[[L0]], $pop[[L1]], $pop[[L2]]{{$}} 171; CHECK-NEXT: drop $pop[[NUM]]{{$}} 172; CHECK-NEXT: return{{$}} 173define void @call_indirect_arg_2(ptr %callee, i32 %arg, i32 %arg2) { 174 call i32 %callee(i32 %arg, i32 %arg2) 175 ret void 176} 177 178; CHECK-LABEL: tail_call_void_nullary: 179; CHECK-NEXT: .functype tail_call_void_nullary () -> (){{$}} 180; NO-TAIL-NEXT: {{^}} call void_nullary{{$}} 181; NO-TAIL-NEXT: return{{$}} 182; SLOW-TAIL-NEXT: {{^}} return_call void_nullary{{$}} 183; FAST-TAIL-NEXT: {{^}} call void_nullary{{$}} 184; FAST-TAIL-NEXT: return{{$}} 185define void @tail_call_void_nullary() { 186 tail call void @void_nullary() 187 ret void 188} 189 190; CHECK-LABEL: fastcc_tail_call_void_nullary: 191; CHECK-NEXT: .functype fastcc_tail_call_void_nullary () -> (){{$}} 192; NO-TAIL-NEXT: {{^}} call void_nullary{{$}} 193; NO-TAIL-NEXT: return{{$}} 194; SLOW-TAIL-NEXT: {{^}} return_call void_nullary{{$}} 195; FAST-TAIL-NEXT: {{^}} call void_nullary{{$}} 196; FAST-TAIL-NEXT: return{{$}} 197define void @fastcc_tail_call_void_nullary() { 198 tail call fastcc void @void_nullary() 199 ret void 200} 201 202; CHECK-LABEL: coldcc_tail_call_void_nullary: 203; CHECK-NEXT: .functype coldcc_tail_call_void_nullary () -> (){{$}} 204; NO-TAIL-NEXT: {{^}} call void_nullary{{$}} 205; NO-TAIL-NEXT: return{{$}} 206; SLOW-TAIL-NEXT: {{^}} return_call void_nullary{{$}} 207; FAST-TAIL-NEXT: {{^}} call void_nullary{{$}} 208; FAST-TAIL-NEXT: return{{$}} 209define void @coldcc_tail_call_void_nullary() { 210 tail call coldcc void @void_nullary() 211 ret void 212} 213 214; CHECK-LABEL: call_constexpr: 215; CHECK-NEXT: .functype call_constexpr () -> (){{$}} 216; CHECK-NEXT: i32.const $push[[L0:[0-9]+]]=, 2{{$}} 217; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, 3{{$}} 218; CHECK-NEXT: call .Lvararg_func_bitcast, $pop[[L0]], $pop[[L1]]{{$}} 219; CHECK-NEXT: i32.const $push[[L3:[0-9]+]]=, void_nullary{{$}} 220; CHECK-NEXT: i32.const $push[[L2:[0-9]+]]=, other_void_nullary{{$}} 221; CHECK-NEXT: i32.add $push[[L4:[0-9]+]]=, $pop[[L3]], $pop[[L2]]{{$}} 222; CHECK-NEXT: call_indirect $pop[[L4]]{{$}} 223; CHECK-NEXT: call void_nullary{{$}} 224; CHECK-NEXT: return{{$}} 225declare void @vararg_func(...) 226declare void @other_void_nullary() 227define void @call_constexpr() { 228bb0: 229 call void @vararg_func(i32 2, i32 3) 230 br label %bb1 231bb1: 232 call void getelementptr (i8, ptr @void_nullary, i32 ptrtoint (ptr @other_void_nullary to i32))() 233 br label %bb2 234bb2: 235 call void inttoptr (i32 ptrtoint (ptr @void_nullary to i32) to ptr)() 236 ret void 237} 238 239; Allocas should be lowered to call_indirects. 240; CHECK-LABEL: call_indirect_alloca: 241; CHECK: local.tee $push{{.*}}=, [[L0:[0-9]+]] 242; CHECK-NEXT: global.set __stack_pointer 243; CHECK-NEXT: local.get $push{{.*}}=, [[L0]] 244; CHECK-NEXT: i32.const $push{{.*}}=, 12 245; CHECK-NEXT: i32.add 246; CHECK-NEXT: call_indirect $pop{{.*}} 247define void @call_indirect_alloca() { 248entry: 249 %ptr = alloca i32, align 4 250 call void %ptr() 251 ret void 252} 253 254; Calling non-functional globals should be lowered to call_indirects. 255; CHECK-LABEL: call_indirect_int: 256; CHECK: i32.const $push[[L0:[0-9]+]]=, global_i8 257; CHECK-NEXT: call_indirect $pop[[L0]] 258; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, global_i32 259; CHECK-NEXT: call_indirect $pop[[L1]] 260@global_i8 = global i8 0 261@global_i32 = global i32 0 262define void @call_indirect_int() { 263 call void @global_i8() 264 call void @global_i32() 265 ret void 266} 267 268; Calling aliases of non-functional globals should be lowered to call_indirects. 269; CHECK-LABEL: call_indirect_int_alias: 270; CHECK: i32.const $push[[L0:[0-9]+]]=, global_i8_alias 271; CHECK-NEXT: call_indirect $pop[[L0]] 272; CHECK-NEXT: i32.const $push[[L1:[0-9]+]]=, global_i32_alias 273; CHECK-NEXT: call_indirect $pop[[L1]] 274@global_i8_alias = alias i8, ptr @global_i8 275@global_i32_alias = alias i32, ptr @global_i32 276define void @call_indirect_int_alias() { 277 call void @global_i8_alias() 278 call void @global_i32_alias() 279 ret void 280} 281 282; Ideally calling aliases of functions should be lowered to direct calls. We 283; support this in the normal (=slow) isel. 284; CHECK-LABEL: call_func_alias: 285; SLOW: call func_alias 286; FAST: i32.const $push[[L0:[0-9]+]]=, func_alias 287; FAST-NEXT: call_indirect $pop[[L0]] 288@func_alias = alias void (), ptr @call_void_nullary 289define void @call_func_alias() { 290 call void @func_alias() 291 ret void 292} 293 294; TODO: test the following: 295; - More argument combinations. 296; - Tail call. 297; - Interesting returns (struct, multiple). 298; - Vararg. 299