| // 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; |
| } |
| |