mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-05-14 01:46:41 +00:00

Set the writable and dead_on_unwind attributes for sret arguments. These indicate that the argument points to writable memory (and it's legal to introduce spurious writes to it on entry to the function) and that the argument memory will not be used if the call unwinds. This enables additional MemCpyOpt/DSE/LICM optimizations.
129 lines
5.0 KiB
C
129 lines
5.0 KiB
C
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature
|
|
// RUN: %clang_cc1 -triple wasm32-unknown-unknown -o - -emit-llvm %s | FileCheck %s
|
|
|
|
#include <stdarg.h>
|
|
|
|
// CHECK-LABEL: define {{[^@]+}}@test_i32
|
|
// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0:[0-9]+]] {
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[V:%.*]] = alloca i32, align 4
|
|
// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]])
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 4
|
|
// CHECK-NEXT: store i32 [[TMP0]], ptr [[V]], align 4
|
|
// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]])
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[V]], align 4
|
|
// CHECK-NEXT: ret i32 [[TMP1]]
|
|
//
|
|
int test_i32(char *fmt, ...) {
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
int v = va_arg(va, int);
|
|
va_end(va);
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: define {{[^@]+}}@test_i64
|
|
// CHECK-SAME: (ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] {
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[V:%.*]] = alloca i64, align 8
|
|
// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]])
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7
|
|
// CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8)
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 4
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load i64, ptr [[ARGP_CUR_ALIGNED]], align 8
|
|
// CHECK-NEXT: store i64 [[TMP1]], ptr [[V]], align 8
|
|
// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]])
|
|
// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[V]], align 8
|
|
// CHECK-NEXT: ret i64 [[TMP2]]
|
|
//
|
|
long long test_i64(char *fmt, ...) {
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
long long v = va_arg(va, long long);
|
|
va_end(va);
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
struct S {
|
|
int x;
|
|
int y;
|
|
int z;
|
|
};
|
|
|
|
// CHECK-LABEL: define {{[^@]+}}@test_struct
|
|
// CHECK-SAME: (ptr dead_on_unwind noalias writable sret([[STRUCT_S:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] {
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]])
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT]], ptr align 4 [[TMP0]], i32 12, i1 false)
|
|
// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]])
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
struct S test_struct(char *fmt, ...) {
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
struct S v = va_arg(va, struct S);
|
|
va_end(va);
|
|
|
|
return v;
|
|
}
|
|
|
|
|
|
struct Z {};
|
|
|
|
// CHECK-LABEL: define {{[^@]+}}@test_empty_struct
|
|
// CHECK-SAME: (ptr dead_on_unwind noalias writable sret([[STRUCT_S:%.*]]) align 4 [[AGG_RESULT:%.*]], ptr noundef [[FMT:%.*]], ...) #[[ATTR0]] {
|
|
// CHECK-NEXT: entry:
|
|
// CHECK-NEXT: [[FMT_ADDR:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[VA:%.*]] = alloca ptr, align 4
|
|
// CHECK-NEXT: [[U:%.*]] = alloca [[STRUCT_Z:%.*]], align 1
|
|
// CHECK-NEXT: store ptr [[FMT]], ptr [[FMT_ADDR]], align 4
|
|
// CHECK-NEXT: call void @llvm.va_start(ptr [[VA]])
|
|
// CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[VA]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 0
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[VA]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[U]], ptr align 4 [[ARGP_CUR]], i32 0, i1 false)
|
|
// CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[VA]], align 4
|
|
// CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4
|
|
// CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[VA]], align 4
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR1]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT]], ptr align 4 [[TMP0]], i32 12, i1 false)
|
|
// CHECK-NEXT: call void @llvm.va_end(ptr [[VA]])
|
|
// CHECK-NEXT: ret void
|
|
//
|
|
struct S test_empty_struct(char *fmt, ...) {
|
|
va_list va;
|
|
|
|
va_start(va, fmt);
|
|
struct Z u = va_arg(va, struct Z);
|
|
struct S v = va_arg(va, struct S);
|
|
va_end(va);
|
|
|
|
return v;
|
|
}
|
|
|