This patch makes the dead_on_return parameter attribute optionally require a number
of bytes to be passed in to specify the number of bytes known to be dead
upon function return/unwind. This is aimed at enabling annotating the
this pointer in C++ destructors with dead_on_return in clang. We need
this to handle cases like the following:
```
struct X {
int n;
~X() {
this[n].n = 0;
}
};
void f() {
X xs[] = {42, -1};
}
```
Where we only certain that sizeof(X) bytes are dead upon return of ~X.
Otherwise DSE would be able to eliminate the store in ~X which would not
be correct.
This patch only does the wiring within IR. Future patches will make
clang emit correct sizing information and update DSE to only delete
stores to objects marked dead_on_return that are provably in bounds of
the number of bytes specified to be dead_on_return.
Reviewers: nikic, alinas, antoniofrighetto
Pull Request: https://github.com/llvm/llvm-project/pull/171712
105 lines
4.2 KiB
C++
105 lines
4.2 KiB
C++
// RUN: %clang_cc1 -no-enable-noundef-analysis -O1 -triple wasm32-unknown-unknown -emit-llvm -o - %s \
|
|
// RUN: | FileCheck %s
|
|
// RUN: %clang_cc1 -no-enable-noundef-analysis -O1 -triple wasm64-unknown-unknown -emit-llvm -o - %s \
|
|
// RUN: | FileCheck %s
|
|
|
|
#define concat_(x, y) x##y
|
|
#define concat(x, y) concat_(x, y)
|
|
|
|
#define test(T) \
|
|
T forward(T x) { return x; } \
|
|
void use(T x); \
|
|
T concat(def_, T)(void); \
|
|
void concat(test_, T)(void) { use(concat(def_, T)()); }
|
|
|
|
struct one_field {
|
|
double d;
|
|
};
|
|
test(one_field);
|
|
// CHECK: define double @_Z7forward9one_field(double returned %{{.*}})
|
|
//
|
|
// CHECK: define void @_Z14test_one_fieldv()
|
|
// CHECK: %[[call:.*]] = tail call double @_Z13def_one_fieldv()
|
|
// CHECK: call void @_Z3use9one_field(double %[[call]])
|
|
// CHECK: ret void
|
|
//
|
|
// CHECK: declare void @_Z3use9one_field(double)
|
|
// CHECK: declare double @_Z13def_one_fieldv()
|
|
|
|
struct two_fields {
|
|
double d, e;
|
|
};
|
|
test(two_fields);
|
|
// CHECK: define void @_Z7forward10two_fields(ptr dead_on_unwind noalias writable writeonly sret(%struct.two_fields) align 8 captures(none) initializes((0, 16)) %{{.*}}, ptr readonly byval(%struct.two_fields) align 8 captures(none) %{{.*}})
|
|
//
|
|
// CHECK: define void @_Z15test_two_fieldsv()
|
|
// CHECK: %[[tmp:.*]] = alloca %struct.two_fields, align 8
|
|
// CHECK: call void @_Z14def_two_fieldsv(ptr dead_on_unwind nonnull writable sret(%struct.two_fields) align 8 %[[tmp]])
|
|
// CHECK: call void @_Z3use10two_fields(ptr nonnull byval(%struct.two_fields) align 8 %[[tmp]])
|
|
// CHECK: ret void
|
|
//
|
|
// CHECK: declare void @_Z3use10two_fields(ptr byval(%struct.two_fields) align 8)
|
|
// CHECK: declare void @_Z14def_two_fieldsv(ptr dead_on_unwind writable sret(%struct.two_fields) align 8)
|
|
|
|
struct copy_ctor {
|
|
double d;
|
|
copy_ctor(copy_ctor const &);
|
|
};
|
|
test(copy_ctor);
|
|
// CHECK: define void @_Z7forward9copy_ctor(ptr dead_on_unwind noalias {{[^,]*}} sret(%struct.copy_ctor) align 8 %{{.*}}, ptr nonnull dead_on_return %{{.*}})
|
|
//
|
|
// CHECK: declare ptr @_ZN9copy_ctorC1ERKS_(ptr {{[^,]*}} returned {{[^,]*}}, ptr nonnull align 8 dereferenceable(8))
|
|
//
|
|
// CHECK: define void @_Z14test_copy_ctorv()
|
|
// CHECK: %[[tmp:.*]] = alloca %struct.copy_ctor, align 8
|
|
// CHECK: call void @_Z13def_copy_ctorv(ptr dead_on_unwind nonnull writable sret(%struct.copy_ctor) align 8 %[[tmp]])
|
|
// CHECK: call void @_Z3use9copy_ctor(ptr nonnull dead_on_return %[[tmp]])
|
|
// CHECK: ret void
|
|
//
|
|
// CHECK: declare void @_Z3use9copy_ctor(ptr dead_on_return)
|
|
// CHECK: declare void @_Z13def_copy_ctorv(ptr dead_on_unwind writable sret(%struct.copy_ctor) align 8)
|
|
|
|
struct __attribute__((aligned(16))) aligned_copy_ctor {
|
|
double d, e;
|
|
aligned_copy_ctor(aligned_copy_ctor const &);
|
|
};
|
|
test(aligned_copy_ctor);
|
|
// CHECK: define void @_Z7forward17aligned_copy_ctor(ptr dead_on_unwind noalias {{[^,]*}} sret(%struct.aligned_copy_ctor) align 16 %{{.*}}, ptr nonnull dead_on_return %{{.*}})
|
|
//
|
|
// CHECK: declare ptr @_ZN17aligned_copy_ctorC1ERKS_(ptr {{[^,]*}} returned {{[^,]*}}, ptr nonnull align 16 dereferenceable(16))
|
|
//
|
|
// CHECK: define void @_Z22test_aligned_copy_ctorv()
|
|
// CHECK: %[[tmp:.*]] = alloca %struct.aligned_copy_ctor, align 16
|
|
// CHECK: call void @_Z21def_aligned_copy_ctorv(ptr dead_on_unwind nonnull writable sret(%struct.aligned_copy_ctor) align 16 %[[tmp]])
|
|
// CHECK: call void @_Z3use17aligned_copy_ctor(ptr nonnull dead_on_return %[[tmp]])
|
|
// CHECK: ret void
|
|
//
|
|
// CHECK: declare void @_Z3use17aligned_copy_ctor(ptr dead_on_return)
|
|
// CHECK: declare void @_Z21def_aligned_copy_ctorv(ptr dead_on_unwind writable sret(%struct.aligned_copy_ctor) align 16)
|
|
|
|
struct empty {};
|
|
test(empty);
|
|
// CHECK: define void @_Z7forward5empty()
|
|
//
|
|
// CHECK: define void @_Z10test_emptyv()
|
|
// CHECK: call void @_Z9def_emptyv()
|
|
// CHECK: call void @_Z3use5empty()
|
|
// CHECK: ret void
|
|
//
|
|
// CHECK: declare void @_Z3use5empty()
|
|
// CHECK: declare void @_Z9def_emptyv()
|
|
|
|
struct one_bitfield {
|
|
int d : 3;
|
|
};
|
|
test(one_bitfield);
|
|
// CHECK: define i32 @_Z7forward12one_bitfield(i32 returned %{{.*}})
|
|
//
|
|
// CHECK: define void @_Z17test_one_bitfieldv()
|
|
// CHECK: %[[call:.*]] = tail call i32 @_Z16def_one_bitfieldv()
|
|
// CHECK: call void @_Z3use12one_bitfield(i32 %[[call]])
|
|
// CHECK: ret void
|
|
//
|
|
// CHECK: declare void @_Z3use12one_bitfield(i32)
|
|
// CHECK: declare i32 @_Z16def_one_bitfieldv()
|