blob: 60f8ef5a8242f34cbf6d0f10cc2fc009ad9e0150 [file] [log] [blame] [edit]
// RUN: %clang_cc1 -triple aarch64-linux -fexperimental-allow-pointer-field-protection-attr -fexperimental-pointer-field-protection-abi -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck %s
// Non-standard layout. Pointer fields are signed and discriminated by type.
struct Pointer {
int* ptr;
private:
int private_data;
};
void pass_pointer_callee(Pointer p);
// CHECK: define dso_local void @_Z12pass_pointerP7Pointer(
void pass_pointer(Pointer *pp) {
// CHECK: %0 = load ptr, ptr %pp.addr, align 8
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp, ptr align 8 %0, i64 16, i1 false)
// CHECK: %1 = getelementptr inbounds i8, ptr %agg.tmp, i64 0
// CHECK: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
// CHECK: %3 = load ptr, ptr %2, align 8
// CHECK: %4 = ptrtoint ptr %3 to i64
// CHECK: %5 = insertvalue [2 x i64] poison, i64 %4, 0
// CHECK: %6 = getelementptr inbounds i8, ptr %agg.tmp, i64 8
// CHECK: %7 = load i64, ptr %6, align 8
// CHECK: %8 = insertvalue [2 x i64] %5, i64 %7, 1
// CHECK: call void @_Z19pass_pointer_callee7Pointer([2 x i64] %8)
pass_pointer_callee(*pp);
}
// CHECK: define dso_local void @_Z14passed_pointer7PointerPS_([2 x i64] %p.coerce, ptr noundef %pp)
void passed_pointer(Pointer p, Pointer *pp) {
// CHECK: %p = alloca %struct.Pointer, align 8
// CHECK: %pp.addr = alloca ptr, align 8
// CHECK: %0 = extractvalue [2 x i64] %p.coerce, 0
// CHECK: %1 = getelementptr inbounds i8, ptr %p, i64 0
// CHECK: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
// CHECK: %3 = inttoptr i64 %0 to ptr
// CHECK: store ptr %3, ptr %2, align 8
// CHECK: %4 = extractvalue [2 x i64] %p.coerce, 1
// CHECK: %5 = getelementptr inbounds i8, ptr %p, i64 8
// CHECK: store i64 %4, ptr %5, align 8
// CHECK: store ptr %pp, ptr %pp.addr, align 8
// CHECK: %6 = load ptr, ptr %pp.addr, align 8
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %6, ptr align 8 %p, i64 12, i1 false)
*pp = p;
}
// CHECK: define dso_local [2 x i64] @_Z14return_pointerP7Pointer(ptr noundef %pp)
Pointer return_pointer(Pointer *pp) {
// CHECK: %retval = alloca %struct.Pointer, align 8
// CHECK: %pp.addr = alloca ptr, align 8
// CHECK: store ptr %pp, ptr %pp.addr, align 8
// CHECK: %0 = load ptr, ptr %pp.addr, align 8
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 8 %0, i64 16, i1 false)
// CHECK: %1 = getelementptr inbounds i8, ptr %retval, i64 0
// CHECK: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
// CHECK: %3 = load ptr, ptr %2, align 8
// CHECK: %4 = ptrtoint ptr %3 to i64
// CHECK: %5 = insertvalue [2 x i64] poison, i64 %4, 0
// CHECK: %6 = getelementptr inbounds i8, ptr %retval, i64 8
// CHECK: %7 = load i64, ptr %6, align 8
// CHECK: %8 = insertvalue [2 x i64] %5, i64 %7, 1
// CHECK: ret [2 x i64] %8
return *pp;
}
Pointer returned_pointer_callee();
// CHECK: define dso_local void @_Z16returned_pointerP7Pointer(ptr noundef %pp)
void returned_pointer(Pointer *pp) {
// CHECK: %pp.addr = alloca ptr, align 8
// CHECK: %ref.tmp = alloca %struct.Pointer, align 8
// CHECK: store ptr %pp, ptr %pp.addr, align 8
// CHECK: %call = call [2 x i64] @_Z23returned_pointer_calleev()
// CHECK: %0 = extractvalue [2 x i64] %call, 0
// CHECK: %1 = getelementptr inbounds i8, ptr %ref.tmp, i64 0
// CHECK: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
// CHECK: %3 = inttoptr i64 %0 to ptr
// CHECK: store ptr %3, ptr %2, align 8
// CHECK: %4 = extractvalue [2 x i64] %call, 1
// CHECK: %5 = getelementptr inbounds i8, ptr %ref.tmp, i64 8
// CHECK: store i64 %4, ptr %5, align 8
// CHECK: %6 = load ptr, ptr %pp.addr, align 8
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %6, ptr align 8 %ref.tmp, i64 12, i1 false)
*pp = returned_pointer_callee();
}
union PointerUnion {
Pointer ptr;
};
void pass_pointer_union_callee(PointerUnion pu);
// CHECK: define dso_local void @_Z18pass_pointer_unionP12PointerUnion(
void pass_pointer_union(PointerUnion *pup) {
// CHECK: %0 = load ptr, ptr %pup.addr, align 8
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp, ptr align 8 %0, i64 16, i1 false)
// CHECK: %coerce.dive = getelementptr inbounds nuw %union.PointerUnion, ptr %agg.tmp, i32 0, i32 0
// CHECK: %1 = load [2 x i64], ptr %coerce.dive, align 8
// CHECK: call void @_Z25pass_pointer_union_callee12PointerUnion([2 x i64] %1)
pass_pointer_union_callee(*pup);
}
// Manual opt into PFP, non-trivially destructible.
// Pointer fields are signed and discriminated by address.
// Trivial ABI: passed and returned by value despite being non-trivial.
struct [[clang::trivial_abi]] [[clang::pointer_field_protection]] TrivialAbiPointer {
int *ptr;
~TrivialAbiPointer();
};
// CHECK: define dso_local void @_Z24pass_trivial_abi_pointer17TrivialAbiPointerPS_(ptr %p.coerce, ptr noundef %pp)
void pass_trivial_abi_pointer(TrivialAbiPointer p, TrivialAbiPointer *pp) {
// CHECK: %p = alloca %struct.TrivialAbiPointer, align 8
// CHECK: %pp.addr = alloca ptr, align 8
// CHECK: %coerce.dive = getelementptr inbounds nuw %struct.TrivialAbiPointer, ptr %p, i32 0, i32 0
// CHECK: %0 = getelementptr inbounds i8, ptr %coerce.dive, i64 0
// CHECK: %1 = ptrtoint ptr %coerce.dive to i64
// CHECK: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %0, i64 %1, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
// CHECK: store ptr %p.coerce, ptr %2, align 8
// CHECK: store ptr %pp, ptr %pp.addr, align 8
// CHECK: %3 = load ptr, ptr %pp.addr, align 8
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %3, ptr align 8 %p, i64 8, i1 false)
// CHECK: %4 = getelementptr inbounds i8, ptr %3, i64 0
// CHECK: %5 = ptrtoint ptr %3 to i64
// CHECK: %6 = call ptr @llvm.protected.field.ptr.p0(ptr %4, i64 %5, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
// CHECK: %7 = getelementptr inbounds i8, ptr %p, i64 0
// CHECK: %8 = ptrtoint ptr %p to i64
// CHECK: %9 = call ptr @llvm.protected.field.ptr.p0(ptr %7, i64 %8, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
// CHECK: %10 = load ptr, ptr %9, align 8
// CHECK: store ptr %10, ptr %6, align 8
// CHECK: call void @_ZN17TrivialAbiPointerD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %p)
*pp = p;
}
// CHECK: define dso_local i64 @_Z26return_trivial_abi_pointerP17TrivialAbiPointer(ptr noundef %pp)
TrivialAbiPointer return_trivial_abi_pointer(TrivialAbiPointer *pp) {
// CHECK: %retval = alloca %struct.TrivialAbiPointer, align 8
// CHECK: %pp.addr = alloca ptr, align 8
// CHECK: store ptr %pp, ptr %pp.addr, align 8
// CHECK: %0 = load ptr, ptr %pp.addr, align 8
// CHECK: call void @_ZN17TrivialAbiPointerC1ERKS_(ptr noundef nonnull align 8 dereferenceable(8) %retval, ptr noundef nonnull align 8 dereferenceable(8) %0)
// CHECK: %1 = getelementptr inbounds i8, ptr %retval, i64 0
// CHECK: %2 = ptrtoint ptr %retval to i64
// CHECK: %3 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 %2, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
// CHECK: %4 = load ptr, ptr %3, align 8
// CHECK: %5 = ptrtoint ptr %4 to i64
// CHECK: ret i64 %5
return *pp;
}