// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature // REQUIRES: webassembly-registered-target // Simple calls to known variadic functions that are completely elided when // optimisations are on This is a functional check that the expand-variadic pass // is consistent with clang's va_arg handling // When expand-variadics is added to the default pipeline, clang -O1 will // suffice here -Wno-varargs avoids warning second argument to 'va_start' is not // the last named parameter // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -Wno-varargs -O1 -emit-llvm -o - | opt - -S --passes='module(expand-variadics,default)' --expand-variadics-override=optimize -o - | FileCheck %s #include #include template static X first(...) { va_list va; __builtin_va_start(va, 0); X r = va_arg(va, X); va_end(va); return r; } template static Y second(...) { va_list va; __builtin_va_start(va, 0); va_arg(va, X); Y r = va_arg(va, Y); va_end(va); return r; } extern "C" { // CHECK-LABEL: define {{[^@]+}}@first_pair_i32 // CHECK-SAME: (i32 noundef returned [[X:%.*]], i32 noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret i32 [[X]] // int first_pair_i32(int x, int y) { return first(x, y); } // CHECK-LABEL: define {{[^@]+}}@second_pair_i32 // CHECK-SAME: (i32 noundef [[X:%.*]], i32 noundef returned [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret i32 [[Y]] // int second_pair_i32(int x, int y) { return second(x, y); } // CHECK-LABEL: define {{[^@]+}}@first_pair_f64 // CHECK-SAME: (double noundef returned [[X:%.*]], double noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret double [[X]] // double first_pair_f64(double x, double y) { return first(x, y); } // CHECK-LABEL: define {{[^@]+}}@second_pair_f64 // CHECK-SAME: (double noundef [[X:%.*]], double noundef returned [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret double [[Y]] // double second_pair_f64(double x, double y) { return second(x, y); } } extern "C" { // CHECK-LABEL: define {{[^@]+}}@first_i32_f64 // CHECK-SAME: (i32 noundef returned [[X:%.*]], double noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret i32 [[X]] // int first_i32_f64(int x, double y) { return first(x, y); } // CHECK-LABEL: define {{[^@]+}}@second_i32_f64 // CHECK-SAME: (i32 noundef [[X:%.*]], double noundef returned [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret double [[Y]] // double second_i32_f64(int x, double y) { return second(x, y); } // CHECK-LABEL: define {{[^@]+}}@first_f64_i32 // CHECK-SAME: (double noundef returned [[X:%.*]], i32 noundef [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret double [[X]] // double first_f64_i32(double x, int y) { return first(x, y); } // CHECK-LABEL: define {{[^@]+}}@second_f64_i32 // CHECK-SAME: (double noundef [[X:%.*]], i32 noundef returned [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret i32 [[Y]] // int second_f64_i32(double x, int y) { return second(x, y); } } extern "C" { typedef uint64_t ulong2 __attribute__((__vector_size__(16), __aligned__(16))); // CHECK-LABEL: define {{[^@]+}}@first_i32_ulong2 // CHECK-SAME: (i32 noundef returned [[X:%.*]], ptr noundef readonly captures(none) [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret i32 [[X]] // int first_i32_ulong2(int x, ulong2 *y) { return first(x, *y); } // CHECK-LABEL: define {{[^@]+}}@second_i32_ulong2 // CHECK-SAME: (i32 noundef [[X:%.*]], ptr noundef readonly captures(none) [[Y:%.*]], ptr noundef writeonly captures(none) initializes((0, 16)) [[R:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] { // CHECK-NEXT: entry: // CHECK-NEXT: [[TMP0:%.*]] = load <2 x i64>, ptr [[Y]], align 16, !tbaa [[TBAA2:![0-9]+]] // CHECK-NEXT: store <2 x i64> [[TMP0]], ptr [[R]], align 16, !tbaa [[TBAA2]] // CHECK-NEXT: ret void // void second_i32_ulong2(int x, ulong2 *y, ulong2 *r) { *r = second(x, *y); } // CHECK-LABEL: define {{[^@]+}}@first_ulong2_i32 // CHECK-SAME: (ptr noundef readonly captures(none) [[X:%.*]], i32 noundef [[Y:%.*]], ptr noundef writeonly captures(none) initializes((0, 16)) [[R:%.*]]) local_unnamed_addr #[[ATTR1]] { // CHECK-NEXT: entry: // CHECK-NEXT: [[TMP0:%.*]] = load <2 x i64>, ptr [[X]], align 16, !tbaa [[TBAA2]] // CHECK-NEXT: store <2 x i64> [[TMP0]], ptr [[R]], align 16, !tbaa [[TBAA2]] // CHECK-NEXT: ret void // void first_ulong2_i32(ulong2 *x, int y, ulong2 *r) { *r = first(*x, y); } // CHECK-LABEL: define {{[^@]+}}@second_ulong2_i32 // CHECK-SAME: (ptr noundef readonly captures(none) [[X:%.*]], i32 noundef returned [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret i32 [[Y]] // int second_ulong2_i32(ulong2 *x, int y) { return second(*x, y); } } // ascending alignment typedef struct { char c; short s; int i; long l; float f; double d; } asc; extern "C" { // CHECK-LABEL: define {{[^@]+}}@first_i32_asc // CHECK-SAME: (i32 noundef returned [[X:%.*]], ptr noundef readonly captures(none) [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret i32 [[X]] // int first_i32_asc(int x, asc *y) { return first(x, *y); } // CHECK-LABEL: define {{[^@]+}}@second_i32_asc // CHECK-SAME: (i32 noundef [[X:%.*]], ptr noundef readonly captures(none) [[Y:%.*]], ptr noundef writeonly captures(none) initializes((0, 24)) [[R:%.*]]) local_unnamed_addr #[[ATTR1]] { // CHECK-NEXT: entry: // CHECK-NEXT: tail call void @llvm.memmove.p0.p0.i32(ptr noundef nonnull align 8 dereferenceable(24) [[R]], ptr noundef nonnull align 1 dereferenceable(24) [[Y]], i32 24, i1 false) // CHECK-NEXT: ret void // void second_i32_asc(int x, asc *y, asc *r) { *r = second(x, *y); } // CHECK-LABEL: define {{[^@]+}}@first_asc_i32 // CHECK-SAME: (ptr noundef readonly captures(none) [[X:%.*]], i32 noundef [[Y:%.*]], ptr noundef writeonly captures(none) initializes((0, 24)) [[R:%.*]]) local_unnamed_addr #[[ATTR1]] { // CHECK-NEXT: entry: // CHECK-NEXT: tail call void @llvm.memmove.p0.p0.i32(ptr noundef nonnull align 8 dereferenceable(24) [[R]], ptr noundef nonnull align 1 dereferenceable(24) [[X]], i32 24, i1 false) // CHECK-NEXT: ret void // void first_asc_i32(asc *x, int y, asc *r) { *r = first(*x, y); } // CHECK-LABEL: define {{[^@]+}}@second_asc_i32 // CHECK-SAME: (ptr noundef readonly captures(none) [[X:%.*]], i32 noundef returned [[Y:%.*]]) local_unnamed_addr #[[ATTR0]] { // CHECK-NEXT: entry: // CHECK-NEXT: ret i32 [[Y]] // int second_asc_i32(asc *x, int y) { return second(*x, y); } }