1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: -p --function-signature 2; RUN: opt -mtriple=wasm32-unknown-unknown -S --passes=expand-variadics --expand-variadics-override=optimize < %s | FileCheck %s --check-prefixes=OPT 3; RUN: opt -mtriple=wasm32-unknown-unknown -S --passes=expand-variadics --expand-variadics-override=lowering < %s | FileCheck %s --check-prefixes=ABI 4; REQUIRES: webassembly-registered-target 5 6; Examples are variadic functions that return the first or the second of an int and a double 7; Split the functions into an internal equivalent that takes a va_list and a ABI preserving wrapper 8 9define i32 @variadic_int_double_get_firstz(...) { 10; OPT-LABEL: define {{[^@]+}}@variadic_int_double_get_firstz(...) { 11; OPT-NEXT: entry: 12; OPT-NEXT: %va_start = alloca ptr, align 4 13; OPT-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr %va_start) 14; OPT-NEXT: call void @llvm.va_start.p0(ptr %va_start) 15; OPT-NEXT: %0 = load ptr, ptr %va_start, align 4 16; OPT-NEXT: %1 = call i32 @variadic_int_double_get_firstz.valist(ptr %0) 17; OPT-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr %va_start) 18; OPT-NEXT: ret i32 %1 19; 20; ABI-LABEL: define {{[^@]+}}@variadic_int_double_get_firstz(ptr %varargs) { 21; ABI-NEXT: entry: 22; ABI-NEXT: %va = alloca ptr, align 4 23; ABI-NEXT: store ptr %varargs, ptr %va, align 4 24; ABI-NEXT: %argp.cur = load ptr, ptr %va, align 4 25; ABI-NEXT: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 26; ABI-NEXT: store ptr %argp.next, ptr %va, align 4 27; ABI-NEXT: %0 = load i32, ptr %argp.cur, align 4 28; ABI-NEXT: ret i32 %0 29; 30entry: 31 %va = alloca ptr, align 4 32 call void @llvm.va_start.p0(ptr nonnull %va) 33 %argp.cur = load ptr, ptr %va, align 4 34 %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 35 store ptr %argp.next, ptr %va, align 4 36 %0 = load i32, ptr %argp.cur, align 4 37 call void @llvm.va_end.p0(ptr %va) 38 ret i32 %0 39} 40 41; CHECK-LABEL: define i32 @variadic_int_double_get_firstz(...) { 42; CHECK-NEXT: entry: 43; CHECK-NEXT: %va_list = alloca ptr, align 4 44; CHECK-NEXT: call void @llvm.va_start.p0(ptr %va_list) 45; CHECK-NEXT: %0 = tail call i32 @variadic_int_double_get_firstz.valist(ptr %va_list) 46; CHECK-NEXT: ret i32 %0 47; CHECK-NEXT: } 48 49; CHECK-LABEL: define internal i32 @variadic_int_double_get_firstz.valist(ptr noalias %varargs) { 50; CHECK-NEXT: entry: 51; CHECK-NEXT: %va = alloca ptr, align 4 52; CHECK-NEXT: store ptr %varargs, ptr %va, align 4 53; CHECK-NEXT: %argp.cur = load ptr, ptr %va, align 4 54; CHECK-NEXT: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 55; CHECK-NEXT: store ptr %argp.next, ptr %va, align 4 56; CHECK-NEXT: %0 = load i32, ptr %argp.cur, align 4 57; CHECK-NEXT: ret i32 %0 58; CHECK-NEXT: } 59 60define double @variadic_int_double_get_secondz(...) { 61; OPT-LABEL: define {{[^@]+}}@variadic_int_double_get_secondz(...) { 62; OPT-NEXT: entry: 63; OPT-NEXT: %va_start = alloca ptr, align 4 64; OPT-NEXT: call void @llvm.lifetime.start.p0(i64 4, ptr %va_start) 65; OPT-NEXT: call void @llvm.va_start.p0(ptr %va_start) 66; OPT-NEXT: %0 = load ptr, ptr %va_start, align 4 67; OPT-NEXT: %1 = call double @variadic_int_double_get_secondz.valist(ptr %0) 68; OPT-NEXT: call void @llvm.lifetime.end.p0(i64 4, ptr %va_start) 69; OPT-NEXT: ret double %1 70; 71; ABI-LABEL: define {{[^@]+}}@variadic_int_double_get_secondz(ptr %varargs) { 72; ABI-NEXT: entry: 73; ABI-NEXT: %va = alloca ptr, align 4 74; ABI-NEXT: store ptr %varargs, ptr %va, align 4 75; ABI-NEXT: %argp.cur = load ptr, ptr %va, align 4 76; ABI-NEXT: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 77; ABI-NEXT: %argp.next2 = getelementptr inbounds i8, ptr %argp.cur, i32 12 78; ABI-NEXT: store ptr %argp.next2, ptr %va, align 4 79; ABI-NEXT: %0 = load double, ptr %argp.next, align 4 80; ABI-NEXT: ret double %0 81; 82entry: 83 %va = alloca ptr, align 4 84 call void @llvm.va_start.p0(ptr nonnull %va) 85 %argp.cur = load ptr, ptr %va, align 4 86 %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 87 %argp.next2 = getelementptr inbounds i8, ptr %argp.cur, i32 12 88 store ptr %argp.next2, ptr %va, align 4 89 %0 = load double, ptr %argp.next, align 4 90 call void @llvm.va_end.p0(ptr %va) 91 ret double %0 92} 93 94; CHECK-LABEL: define double @variadic_int_double_get_secondz(...) { 95; CHECK-NEXT: entry: 96; CHECK-NEXT: %va_list = alloca ptr, align 4 97; CHECK-NEXT: call void @llvm.va_start.p0(ptr %va_list) 98; CHECK-NEXT: %0 = tail call double @variadic_int_double_get_secondz.valist(ptr %va_list) 99; CHECK-NEXT: ret double %0 100; CHECK-NEXT: } 101 102; CHECK-LABEL: define internal double @variadic_int_double_get_secondz.valist(ptr noalias %varargs) { 103; CHECK-NEXT: entry: 104; CHECK-NEXT: %va = alloca ptr, align 4 105; CHECK-NEXT: store ptr %varargs, ptr %va, align 4 106; CHECK-NEXT: %argp.cur = load ptr, ptr %va, align 4 107; CHECK-NEXT: %argp.next = getelementptr inbounds i8, ptr %argp.cur, i32 4 108; CHECK-NEXT: %argp.next2 = getelementptr inbounds i8, ptr %argp.cur, i32 12 109; CHECK-NEXT: store ptr %argp.next2, ptr %va, align 4 110; CHECK-NEXT: %0 = load double, ptr %argp.next, align 4 111; CHECK-NEXT: ret double %0 112; CHECK-NEXT: } 113 114 115; CHECK-LABEL: @variadic_can_get_firstIidEEbT_T0_(i32 %x, double %y) { 116; CHECK-NEXT: entry: 117; CHECK-NEXT: %vararg_buffer = alloca %variadic_can_get_firstIidEEbT_T0_.vararg, align 16 118; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 12, ptr %vararg_buffer) 119; CHECK-NEXT: %0 = getelementptr inbounds %variadic_can_get_firstIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 0 120; CHECK-NEXT: store i32 %x, ptr %0, align 4 121; CHECK-NEXT: %1 = getelementptr inbounds %variadic_can_get_firstIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 1 122; CHECK-NEXT: store double %y, ptr %1, align 4 123; CHECK-NEXT: %call = call i32 @variadic_int_double_get_firstz.valist(ptr %vararg_buffer) 124; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 12, ptr %vararg_buffer) 125; CHECK-NEXT: %cmp.i = icmp eq i32 %call, %x 126; CHECK-NEXT: ret i1 %cmp.i 127; CHECK-NEXT: } 128 129define zeroext i1 @variadic_can_get_firstIidEEbT_T0_(i32 %x, double %y) { 130; OPT-LABEL: define {{[^@]+}}@variadic_can_get_firstIidEEbT_T0_(i32 %x, double %y) { 131; OPT-NEXT: entry: 132; OPT-NEXT: %vararg_buffer = alloca %variadic_can_get_firstIidEEbT_T0_.vararg, align 16 133; OPT-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr %vararg_buffer) 134; OPT-NEXT: %0 = getelementptr inbounds nuw %variadic_can_get_firstIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 0 135; OPT-NEXT: store i32 %x, ptr %0, align 4 136; OPT-NEXT: %1 = getelementptr inbounds nuw %variadic_can_get_firstIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 2 137; OPT-NEXT: store double %y, ptr %1, align 8 138; OPT-NEXT: %call = call i32 @variadic_int_double_get_firstz.valist(ptr %vararg_buffer) 139; OPT-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr %vararg_buffer) 140; OPT-NEXT: %cmp.i = icmp eq i32 %call, %x 141; OPT-NEXT: ret i1 %cmp.i 142; 143; ABI-LABEL: define {{[^@]+}}@variadic_can_get_firstIidEEbT_T0_(i32 %x, double %y) { 144; ABI-NEXT: entry: 145; ABI-NEXT: %vararg_buffer = alloca %variadic_can_get_firstIidEEbT_T0_.vararg, align 16 146; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr %vararg_buffer) 147; ABI-NEXT: %0 = getelementptr inbounds nuw %variadic_can_get_firstIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 0 148; ABI-NEXT: store i32 %x, ptr %0, align 4 149; ABI-NEXT: %1 = getelementptr inbounds nuw %variadic_can_get_firstIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 2 150; ABI-NEXT: store double %y, ptr %1, align 8 151; ABI-NEXT: %call = call i32 @variadic_int_double_get_firstz(ptr %vararg_buffer) 152; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr %vararg_buffer) 153; ABI-NEXT: %cmp.i = icmp eq i32 %call, %x 154; ABI-NEXT: ret i1 %cmp.i 155; 156entry: 157 %call = call i32 (...) @variadic_int_double_get_firstz(i32 %x, double %y) 158 %cmp.i = icmp eq i32 %call, %x 159 ret i1 %cmp.i 160} 161 162; CHECK-LABEL: @variadic_can_get_secondIidEEbT_T0_(i32 %x, double %y) { 163; CHECK-NEXT: entry: 164; CHECK-NEXT: %vararg_buffer = alloca %variadic_can_get_secondIidEEbT_T0_.vararg, align 16 165; CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 12, ptr %vararg_buffer) 166; CHECK-NEXT: %0 = getelementptr inbounds %variadic_can_get_secondIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 0 167; CHECK-NEXT: store i32 %x, ptr %0, align 4 168; CHECK-NEXT: %1 = getelementptr inbounds %variadic_can_get_secondIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 1 169; CHECK-NEXT: store double %y, ptr %1, align 4 170; CHECK-NEXT: %call = call double @variadic_int_double_get_secondz.valist(ptr %vararg_buffer) 171; CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 12, ptr %vararg_buffer) 172; CHECK-NEXT: %cmp.i = fcmp oeq double %call, %y 173; CHECK-NEXT: ret i1 %cmp.i 174; CHECK-NEXT: } 175 176define zeroext i1 @variadic_can_get_secondIidEEbT_T0_(i32 %x, double %y) { 177; OPT-LABEL: define {{[^@]+}}@variadic_can_get_secondIidEEbT_T0_(i32 %x, double %y) { 178; OPT-NEXT: entry: 179; OPT-NEXT: %vararg_buffer = alloca %variadic_can_get_secondIidEEbT_T0_.vararg, align 16 180; OPT-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr %vararg_buffer) 181; OPT-NEXT: %0 = getelementptr inbounds nuw %variadic_can_get_secondIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 0 182; OPT-NEXT: store i32 %x, ptr %0, align 4 183; OPT-NEXT: %1 = getelementptr inbounds nuw %variadic_can_get_secondIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 2 184; OPT-NEXT: store double %y, ptr %1, align 8 185; OPT-NEXT: %call = call double @variadic_int_double_get_secondz.valist(ptr %vararg_buffer) 186; OPT-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr %vararg_buffer) 187; OPT-NEXT: %cmp.i = fcmp oeq double %call, %y 188; OPT-NEXT: ret i1 %cmp.i 189; 190; ABI-LABEL: define {{[^@]+}}@variadic_can_get_secondIidEEbT_T0_(i32 %x, double %y) { 191; ABI-NEXT: entry: 192; ABI-NEXT: %vararg_buffer = alloca %variadic_can_get_secondIidEEbT_T0_.vararg, align 16 193; ABI-NEXT: call void @llvm.lifetime.start.p0(i64 16, ptr %vararg_buffer) 194; ABI-NEXT: %0 = getelementptr inbounds nuw %variadic_can_get_secondIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 0 195; ABI-NEXT: store i32 %x, ptr %0, align 4 196; ABI-NEXT: %1 = getelementptr inbounds nuw %variadic_can_get_secondIidEEbT_T0_.vararg, ptr %vararg_buffer, i32 0, i32 2 197; ABI-NEXT: store double %y, ptr %1, align 8 198; ABI-NEXT: %call = call double @variadic_int_double_get_secondz(ptr %vararg_buffer) 199; ABI-NEXT: call void @llvm.lifetime.end.p0(i64 16, ptr %vararg_buffer) 200; ABI-NEXT: %cmp.i = fcmp oeq double %call, %y 201; ABI-NEXT: ret i1 %cmp.i 202; 203entry: 204 %call = call double (...) @variadic_int_double_get_secondz(i32 %x, double %y) 205 %cmp.i = fcmp oeq double %call, %y 206 ret i1 %cmp.i 207} 208 209; Declaration unchanged 210; CHECK: declare void @variadic_without_callers(...) 211declare void @variadic_without_callers(...) 212 213declare void @llvm.va_start.p0(ptr) 214declare void @llvm.va_end.p0(ptr) 215