aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clang/docs/StructureProtection.rst70
-rw-r--r--clang/docs/index.rst1
-rw-r--r--clang/include/clang/AST/ASTContext.h22
-rw-r--r--clang/include/clang/AST/CXXRecordDeclDefinitionBits.def6
-rw-r--r--clang/include/clang/AST/DeclCXX.h6
-rw-r--r--clang/include/clang/Basic/Attr.td13
-rw-r--r--clang/include/clang/Basic/LangOptions.def5
-rw-r--r--clang/include/clang/Driver/Options.td11
-rw-r--r--clang/lib/AST/ASTContext.cpp90
-rw-r--r--clang/lib/AST/DeclCXX.cpp20
-rw-r--r--clang/lib/AST/ExprConstant.cpp1
-rw-r--r--clang/lib/AST/TypePrinter.cpp6
-rw-r--r--clang/lib/CodeGen/CGBuiltin.cpp44
-rw-r--r--clang/lib/CodeGen/CGCall.cpp150
-rw-r--r--clang/lib/CodeGen/CGClass.cpp26
-rw-r--r--clang/lib/CodeGen/CGExpr.cpp19
-rw-r--r--clang/lib/CodeGen/CGExprAgg.cpp3
-rw-r--r--clang/lib/CodeGen/CGExprConstant.cpp40
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.cpp72
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h12
-rw-r--r--clang/lib/CodeGen/CodeGenModule.cpp39
-rw-r--r--clang/lib/CodeGen/CodeGenModule.h7
-rw-r--r--clang/lib/CodeGen/ItaniumCXXABI.cpp1
-rw-r--r--clang/lib/CodeGen/MicrosoftCXXABI.cpp1
-rw-r--r--clang/lib/Driver/ToolChains/Clang.cpp9
-rw-r--r--clang/lib/Frontend/InitPreprocessor.cpp5
-rw-r--r--clang/lib/Sema/SemaDeclAttr.cpp8
-rw-r--r--clang/lib/Sema/SemaTypeTraits.cpp21
-rw-r--r--clang/test/CodeGenCXX/pfp-attribute-disable.cpp33
-rw-r--r--clang/test/CodeGenCXX/pfp-coerce.cpp222
-rw-r--r--clang/test/CodeGenCXX/pfp-load-store.cpp40
-rw-r--r--clang/test/CodeGenCXX/pfp-member-pointer-offsetof.cpp14
-rw-r--r--clang/test/CodeGenCXX/pfp-memcpy.cpp59
-rw-r--r--clang/test/CodeGenCXX/pfp-null-init.cpp21
-rw-r--r--clang/test/CodeGenCXX/pfp-struct-gep.cpp36
-rw-r--r--clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp101
-rw-r--r--clang/test/Misc/pragma-attribute-supported-attributes-list.test2
-rw-r--r--clang/test/Preprocessor/pfp-predefines.c5
38 files changed, 1204 insertions, 37 deletions
diff --git a/clang/docs/StructureProtection.rst b/clang/docs/StructureProtection.rst
new file mode 100644
index 0000000..b6a0ea3
--- /dev/null
+++ b/clang/docs/StructureProtection.rst
@@ -0,0 +1,70 @@
+====================
+Structure Protection
+====================
+
+.. contents::
+ :local:
+
+
+Introduction
+============
+
+Structure protection is an *experimental* mitigation
+against use-after-free vulnerabilities. For
+more information, please see the original `RFC
+<https://discourse.llvm.org/t/rfc-structure-protection-a-family-of-uaf-mitigation-techniques/85555>`_.
+An independent set of documentation will be contributed when the feature
+is promoted to stable.
+
+Usage
+=====
+
+To use structure protection, build your program using one or more of these flags:
+
+- ``-fexperimental-pointer-field-protection``: Enable pointer
+ field protection on all types that are not considered standard-layout
+ according to the C++ rules for standard layout. Specifying this flag
+ also defines the predefined macro ``__POINTER_FIELD_PROTECTION__``.
+
+- ``-fexperimental-pointer-field-protection-tagged``: On architectures
+ that support it (currently only AArch64), for types that are not considered
+ trivially copyable, use the address of the object to compute the pointer
+ encoding. Specifying this flag also defines the predefined macro
+ ``__POINTER_FIELD_PROTECTION_TAGGED__``.
+
+It is also possible to specify the attribute
+``[[clang::pointer_field_protection]]`` on a struct type to opt the
+struct's pointer fields into pointer field protection, even if the type is
+standard layout or none of the command line flags are specified. Note that
+this means that the type will not comply with pointer interconvertibility
+and other standard layout rules.
+
+Pointer field protection is inherited from bases and non-static data
+members.
+
+In order to avoid ABI breakage, the entire C++ part
+of the program must be built with a consistent set of
+``-fexperimental-pointer-field-protection*`` flags, and the C++ standard
+library must also be built with the same flags and statically linked
+into the program.
+
+To build libc++ with pointer field protection support, pass the following
+CMake flags:
+
+.. code-block:: console
+
+ "-DRUNTIMES_${triple}_LIBCXXABI_ENABLE_SHARED=OFF" \
+ "-DRUNTIMES_${triple}_LIBCXX_USE_COMPILER_RT=ON" \
+ "-DRUNTIMES_${triple}_LIBCXX_PFP=untagged" \
+ "-DRUNTIMES_${triple}_LIBCXX_ENABLE_SHARED=OFF" \
+ "-DRUNTIMES_${triple}_LIBCXX_TEST_CONFIG=llvm-libc++-static.cfg.in" \
+ "-DRUNTIMES_${triple}_LIBUNWIND_ENABLE_SHARED=OFF" \
+
+where ``${triple}`` is your target triple, such as
+``aarch64-unknown-linux``.
+
+The resulting toolchain may then be used to build programs
+with pointer field protection by passing ``-stdlib=libc++
+-fexperimental-pointer-field-protection`` at compile time
+and ``-Wl,-Bstatic -lc++ -lc++abi -Wl,-Bdynamic -lm -fuse-ld=lld
+-static-libstdc++`` at link time.
diff --git a/clang/docs/index.rst b/clang/docs/index.rst
index 542bfc9..dbc1d20 100644
--- a/clang/docs/index.rst
+++ b/clang/docs/index.rst
@@ -47,6 +47,7 @@ Using Clang as a Compiler
LTOVisibility
SafeStack
ShadowCallStack
+ StructureProtection
SourceBasedCodeCoverage
StandardCPlusPlusModules
Modules
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 1c17333..c4c62b6 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -183,6 +183,12 @@ struct TypeInfoChars {
}
};
+struct PFPField {
+ CharUnits offset;
+ FieldDecl *field;
+ bool isWithinUnion;
+};
+
/// Holds long-lived AST nodes (such as types and decls) that can be
/// referred to throughout the semantic analysis of a file.
class ASTContext : public RefCountedBase<ASTContext> {
@@ -3729,6 +3735,22 @@ public:
StringRef getCUIDHash() const;
+ void findPFPFields(QualType Ty, CharUnits Offset,
+ std::vector<PFPField> &Fields, bool IncludeVBases,
+ bool IsWithinUnion = false) const;
+ bool hasPFPFields(QualType ty) const;
+ bool isPFPField(const FieldDecl *field) const;
+
+ /// Returns whether this record's PFP fields (if any) are trivially
+ /// copyable (i.e. may be memcpy'd). This may also return true if the
+ /// record does not have any PFP fields, so it may be necessary for the caller
+ /// to check for PFP fields, e.g. by calling hasPFPFields().
+ bool arePFPFieldsTriviallyCopyable(const RecordDecl *RD) const;
+
+ llvm::SetVector<const FieldDecl *> PFPFieldsWithEvaluatedOffset;
+ void recordMemberDataPointerEvaluation(const ValueDecl *VD);
+ void recordOffsetOfEvaluation(const OffsetOfExpr *E);
+
private:
/// All OMPTraitInfo objects live in this collection, one per
/// `pragma omp [begin] declare variant` directive.
diff --git a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
index 6620840..83673dc 100644
--- a/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
+++ b/clang/include/clang/AST/CXXRecordDeclDefinitionBits.def
@@ -253,4 +253,10 @@ FIELD(IsAnyDestructorNoReturn, 1, NO_MERGE)
/// type that is intangible). HLSL only.
FIELD(IsHLSLIntangible, 1, NO_MERGE)
+/// Whether the pointer fields in this class should have pointer field
+/// protection (PFP) by default, either because of an attribute, the
+/// -fexperimental-pointer-field-protection compiler flag or inheritance from
+/// a base or member with PFP.
+FIELD(IsPFPType, 1, NO_MERGE)
+
#undef FIELD
diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 00d8f724..c1db6b3 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1235,6 +1235,12 @@ public:
/// Determine whether this class has any variant members.
bool hasVariantMembers() const { return data().HasVariantMembers; }
+ /// Returns whether the pointer fields in this class should have pointer field
+ /// protection (PFP) by default, either because of an attribute, the
+ /// -fexperimental-pointer-field-protection compiler flag or inheritance from
+ /// a base or member with PFP.
+ bool isPFPType() const { return data().IsPFPType; }
+
/// Determine whether this class has a trivial default constructor
/// (C++11 [class.ctor]p5).
bool hasTrivialDefaultConstructor() const {
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 29364c5..fc37e06 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2597,6 +2597,19 @@ def CountedByOrNull : DeclOrTypeAttr {
let LangOpts = [COnly];
}
+def NoFieldProtection : DeclOrTypeAttr {
+ let Spellings = [Clang<"no_field_protection">];
+ let Subjects = SubjectList<[Field], ErrorDiag>;
+ let Documentation = [Undocumented];
+}
+
+def PointerFieldProtection : DeclOrTypeAttr {
+ let Spellings = [Clang<"pointer_field_protection">];
+ let Subjects = SubjectList<[CXXRecord], ErrorDiag>;
+ let Documentation = [Undocumented];
+ let SimpleHandler = 1;
+}
+
def SizedBy : DeclOrTypeAttr {
let Spellings = [Clang<"sized_by">];
let Subjects = SubjectList<[Field], ErrorDiag>;
diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index f094ba1..2a75af4 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -458,6 +458,11 @@ LANGOPT(RelativeCXXABIVTables, 1, 0, NotCompatible,
LANGOPT(OmitVTableRTTI, 1, 0, NotCompatible,
"Use an ABI-incompatible v-table layout that omits the RTTI component")
+LANGOPT(PointerFieldProtection, 1, 0, NotCompatible,
+ "Encode struct pointer fields to protect against UAF vulnerabilities")
+LANGOPT(PointerFieldProtectionTagged, 1, 0, NotCompatible,
+ "Use pointer identity (tag) to discriminate pointers of non-trivially-copyable types")
+
LANGOPT(VScaleMin, 32, 0, NotCompatible, "Minimum vscale value")
LANGOPT(VScaleMax, 32, 0, NotCompatible, "Maximum vscale value")
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index b1ae3cf..50f6933 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -3077,6 +3077,17 @@ defm experimental_omit_vtable_rtti : BoolFOption<"experimental-omit-vtable-rtti"
NegFlag<SetFalse, [], [CC1Option], "Do not omit">,
BothFlags<[], [CC1Option], " the RTTI component from virtual tables">>;
+defm experimental_pointer_field_protection : BoolFOption<"experimental-pointer-field-protection",
+ LangOpts<"PointerFieldProtection">, DefaultFalse,
+ PosFlag<SetTrue, [], [CC1Option], "Enable">,
+ NegFlag<SetFalse, [], [], "Do not enable">,
+ BothFlags<[], [ClangOption], " pointer field protection on all non-standard layout struct types">>;
+defm experimental_pointer_field_protection_tagged : BoolFOption<"experimental-pointer-field-protection-tagged",
+ LangOpts<"PointerFieldProtectionTagged">, DefaultFalse,
+ PosFlag<SetTrue, [], [CC1Option], "Use">,
+ NegFlag<SetFalse, [], [], "Do not use">,
+ BothFlags<[], [ClangOption], " pointer identity (tag) to discriminate pointers of non-trivially-copyable types">>;
+
def fcxx_abi_EQ : Joined<["-"], "fc++-abi=">,
Group<f_clang_Group>, Visibility<[ClangOption, CC1Option]>,
HelpText<"C++ ABI to use. This will override the target C++ ABI.">;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index dca05b4..7e8f4f2 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -15256,3 +15256,93 @@ bool ASTContext::useAbbreviatedThunkName(GlobalDecl VirtualMethodDecl,
ThunksToBeAbbreviated[VirtualMethodDecl] = std::move(SimplifiedThunkNames);
return Result;
}
+
+bool ASTContext::arePFPFieldsTriviallyCopyable(const RecordDecl *RD) const {
+ bool IsPAuthSupported =
+ getTargetInfo().getTriple().getArch() == llvm::Triple::aarch64;
+ if (!IsPAuthSupported)
+ return true;
+ if (getLangOpts().PointerFieldProtectionTagged)
+ return !isa<CXXRecordDecl>(RD) ||
+ cast<CXXRecordDecl>(RD)->hasTrivialDestructor();
+ return true;
+}
+
+void ASTContext::findPFPFields(QualType Ty, CharUnits Offset,
+ std::vector<PFPField> &Fields,
+ bool IncludeVBases, bool IsWithinUnion) const {
+ if (auto *AT = getAsConstantArrayType(Ty)) {
+ if (auto *ElemDecl = AT->getElementType()->getAsCXXRecordDecl()) {
+ const ASTRecordLayout &ElemRL = getASTRecordLayout(ElemDecl);
+ for (unsigned i = 0; i != AT->getSize(); ++i) {
+ findPFPFields(AT->getElementType(), Offset + i * ElemRL.getSize(),
+ Fields, true);
+ }
+ }
+ }
+ auto *Decl = Ty->getAsCXXRecordDecl();
+ // isPFPType() is inherited from bases and members (including via arrays), so
+ // we can early exit if it is false.
+ if (!Decl || !Decl->isPFPType())
+ return;
+ IsWithinUnion |= Decl->isUnion();
+ const ASTRecordLayout &RL = getASTRecordLayout(Decl);
+ for (FieldDecl *field : Decl->fields()) {
+ CharUnits fieldOffset =
+ Offset + toCharUnitsFromBits(RL.getFieldOffset(field->getFieldIndex()));
+ if (isPFPField(field))
+ Fields.push_back({fieldOffset, field, IsWithinUnion});
+ findPFPFields(field->getType(), fieldOffset, Fields, true, IsWithinUnion);
+ }
+ for (auto &Base : Decl->bases()) {
+ if (Base.isVirtual())
+ continue;
+ CharUnits BaseOffset =
+ Offset + RL.getBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
+ findPFPFields(Base.getType(), BaseOffset, Fields, false, IsWithinUnion);
+ }
+ if (IncludeVBases) {
+ for (auto &Base : Decl->vbases()) {
+ CharUnits BaseOffset =
+ Offset + RL.getVBaseClassOffset(Base.getType()->getAsCXXRecordDecl());
+ findPFPFields(Base.getType(), BaseOffset, Fields, false, IsWithinUnion);
+ }
+ }
+}
+
+bool ASTContext::hasPFPFields(QualType Ty) const {
+ std::vector<PFPField> PFPFields;
+ findPFPFields(Ty, CharUnits::Zero(), PFPFields, true);
+ return !PFPFields.empty();
+}
+
+bool ASTContext::isPFPField(const FieldDecl *FD) const {
+ auto *RD = dyn_cast<CXXRecordDecl>(FD->getParent());
+ if (!RD || !RD->isPFPType())
+ return false;
+ return FD->getType()->isPointerType() &&
+ !FD->hasAttr<NoFieldProtectionAttr>();
+}
+
+void ASTContext::recordMemberDataPointerEvaluation(const ValueDecl *VD) {
+ if (!getLangOpts().PointerFieldProtection)
+ return;
+ auto *FD = dyn_cast<FieldDecl>(VD);
+ if (!FD)
+ FD = cast<FieldDecl>(cast<IndirectFieldDecl>(VD)->chain().back());
+ if (!isPFPField(FD))
+ return;
+ PFPFieldsWithEvaluatedOffset.insert(FD);
+}
+
+void ASTContext::recordOffsetOfEvaluation(const OffsetOfExpr *E) {
+ if (!getLangOpts().PointerFieldProtection || E->getNumComponents() == 0)
+ return;
+ OffsetOfNode Comp = E->getComponent(E->getNumComponents() - 1);
+ if (Comp.getKind() != OffsetOfNode::Field)
+ return;
+ FieldDecl *FD = Comp.getField();
+ if (!isPFPField(FD))
+ return;
+ PFPFieldsWithEvaluatedOffset.insert(FD);
+}
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index aa1f5a1..d08a807 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -109,9 +109,9 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
ImplicitCopyAssignmentHasConstParam(true),
HasDeclaredCopyConstructorWithConstParam(false),
HasDeclaredCopyAssignmentWithConstParam(false),
- IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), IsLambda(false),
- IsParsingBaseSpecifiers(false), ComputedVisibleConversions(false),
- HasODRHash(false), Definition(D) {}
+ IsAnyDestructorNoReturn(false), IsHLSLIntangible(false), IsPFPType(false),
+ IsLambda(false), IsParsingBaseSpecifiers(false),
+ ComputedVisibleConversions(false), HasODRHash(false), Definition(D) {}
CXXBaseSpecifier *CXXRecordDecl::DefinitionData::getBasesSlowCase() const {
return Bases.get(Definition->getASTContext().getExternalSource());
@@ -456,6 +456,9 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
if (!BaseClassDecl->allowConstDefaultInit())
data().HasUninitializedFields = true;
+ if (BaseClassDecl->isPFPType())
+ data().IsPFPType = true;
+
addedClassSubobject(BaseClassDecl);
}
@@ -1408,6 +1411,9 @@ void CXXRecordDecl::addedMember(Decl *D) {
if (FieldRec->hasVariantMembers() &&
Field->isAnonymousStructOrUnion())
data().HasVariantMembers = true;
+
+ if (FieldRec->isPFPType())
+ data().IsPFPType = true;
}
} else {
// Base element type of field is a non-class type.
@@ -2305,6 +2311,14 @@ void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
}
setHasUninitializedExplicitInitFields(false);
}
+
+ if (getLangOpts().PointerFieldProtection && !isStandardLayout()) {
+ data().IsPFPType = true;
+ } else if (hasAttr<PointerFieldProtectionAttr>()) {
+ data().IsPFPType = true;
+ data().IsStandardLayout = false;
+ data().IsCXX11StandardLayout = false;
+ }
}
bool CXXRecordDecl::mayBeAbstract() const {
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index e7dc1d1..bf88c74 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15410,6 +15410,7 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
}
bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *OOE) {
+ Info.Ctx.recordOffsetOfEvaluation(OOE);
CharUnits Result;
unsigned n = OOE->getNumComponents();
if (n == 0)
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 54ca42d..4ea78d2 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2167,6 +2167,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::CFISalt:
OS << "cfi_salt(\"" << cast<CFISaltAttr>(T->getAttr())->getSalt() << "\")";
break;
+ case attr::NoFieldProtection:
+ OS << "no_field_protection";
+ break;
+ case attr::PointerFieldProtection:
+ OS << "pointer_field_protection";
+ break;
}
OS << "))";
}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 16c0591..6f6fade 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4575,18 +4575,48 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
Address Dest = EmitPointerWithAlignment(E->getArg(0));
Address Src = EmitPointerWithAlignment(E->getArg(1));
Value *SizeVal = EmitScalarExpr(E->getArg(2));
+ Value *TypeSize = ConstantInt::get(
+ SizeVal->getType(),
+ getContext()
+ .getTypeSizeInChars(E->getArg(0)->getType()->getPointeeType())
+ .getQuantity());
if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate)
- SizeVal = Builder.CreateMul(
- SizeVal,
- ConstantInt::get(
- SizeVal->getType(),
- getContext()
- .getTypeSizeInChars(E->getArg(0)->getType()->getPointeeType())
- .getQuantity()));
+ SizeVal = Builder.CreateMul(SizeVal, TypeSize);
EmitArgCheck(TCK_Store, Dest, E->getArg(0), 0);
EmitArgCheck(TCK_Load, Src, E->getArg(1), 1);
auto *I = Builder.CreateMemMove(Dest, Src, SizeVal, false);
addInstToNewSourceAtom(I, nullptr);
+ if (BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_trivially_relocate) {
+ if (getContext().hasPFPFields(
+ E->getArg(0)->getType()->getPointeeType())) {
+ BasicBlock *Entry = Builder.GetInsertBlock();
+ BasicBlock *Loop = createBasicBlock("loop");
+ BasicBlock *LoopEnd = createBasicBlock("loop.end");
+ Builder.CreateCondBr(
+ Builder.CreateICmpEQ(SizeVal,
+ ConstantInt::get(SizeVal->getType(), 0)),
+ LoopEnd, Loop);
+
+ EmitBlock(Loop);
+ PHINode *Offset = Builder.CreatePHI(SizeVal->getType(), 2);
+ Offset->addIncoming(ConstantInt::get(SizeVal->getType(), 0), Entry);
+ Address DestRec = Dest.withPointer(
+ Builder.CreateInBoundsGEP(Int8Ty, Dest.getBasePointer(), {Offset}),
+ KnownNonNull);
+ Address SrcRec = Src.withPointer(
+ Builder.CreateInBoundsGEP(Int8Ty, Src.getBasePointer(), {Offset}),
+ KnownNonNull);
+ emitPFPTrivialRelocation(DestRec, SrcRec,
+ E->getArg(0)->getType()->getPointeeType());
+
+ Value *NextOffset = Builder.CreateAdd(Offset, TypeSize);
+ Offset->addIncoming(NextOffset, Loop);
+ Builder.CreateCondBr(Builder.CreateICmpEQ(NextOffset, SizeVal), LoopEnd,
+ Loop);
+
+ EmitBlock(LoopEnd);
+ }
+ }
return RValue::get(Dest, *this);
}
case Builtin::BImemset:
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 5b0dd23..77a93e6 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -1310,6 +1310,61 @@ static llvm::Value *CoerceIntOrPtrToIntOrPtr(llvm::Value *Val, llvm::Type *Ty,
return Val;
}
+static llvm::Value *CreatePFPCoercedLoad(Address Src, QualType SrcFETy,
+ llvm::Type *Ty, CodeGenFunction &CGF) {
+ // Coercion directly through memory does not work if the structure has pointer
+ // field protection because the struct in registers has a different bit
+ // pattern to the struct in memory, so we must read the elements one by one
+ // and use them to form the coerced structure.
+ std::vector<PFPField> PFPFields;
+ CGF.getContext().findPFPFields(SrcFETy, CharUnits::Zero(), PFPFields, true);
+ if (PFPFields.empty())
+ return nullptr;
+
+ auto LoadCoercedField = [&](CharUnits Offset,
+ llvm::Type *FieldType) -> llvm::Value * {
+ if (!PFPFields.empty() && PFPFields[0].offset == Offset) {
+ auto fieldAddr = CGF.EmitAddressOfPFPField(Src, PFPFields[0]);
+ llvm::Value *FieldVal = CGF.Builder.CreateLoad(fieldAddr);
+ if (isa<llvm::IntegerType>(FieldType))
+ FieldVal = CGF.Builder.CreatePtrToInt(FieldVal, FieldType);
+ PFPFields.erase(PFPFields.begin());
+ return FieldVal;
+ }
+ auto FieldAddr =
+ CGF.Builder
+ .CreateConstInBoundsByteGEP(Src.withElementType(CGF.Int8Ty), Offset)
+ .withElementType(FieldType);
+ return CGF.Builder.CreateLoad(FieldAddr);
+ };
+ if (isa<llvm::IntegerType>(Ty) || isa<llvm::PointerType>(Ty)) {
+ auto Addr = CGF.EmitAddressOfPFPField(Src, PFPFields[0]);
+ llvm::Value *Val = CGF.Builder.CreateLoad(Addr);
+ if (isa<llvm::IntegerType>(Ty))
+ Val = CGF.Builder.CreatePtrToInt(Val, Ty);
+ return Val;
+ }
+ if (auto *AT = dyn_cast<llvm::ArrayType>(Ty)) {
+ auto *ET = AT->getElementType();
+ CharUnits wordSize = CGF.getContext().toCharUnitsFromBits(
+ CGF.CGM.getDataLayout().getTypeSizeInBits(ET));
+ CharUnits Offset = CharUnits::Zero();
+ llvm::Value *Val = llvm::PoisonValue::get(AT);
+ for (unsigned i = 0; i != AT->getNumElements(); ++i, Offset += wordSize)
+ Val = CGF.Builder.CreateInsertValue(Val, LoadCoercedField(Offset, ET), i);
+ return Val;
+ }
+ auto *ST = cast<llvm::StructType>(Ty);
+ llvm::Value *Val = llvm::PoisonValue::get(ST);
+ auto *SL = CGF.CGM.getDataLayout().getStructLayout(ST);
+ for (unsigned i = 0; i != ST->getNumElements(); ++i) {
+ CharUnits Offset = CharUnits::fromQuantity(SL->getElementOffset(i));
+ Val = CGF.Builder.CreateInsertValue(
+ Val, LoadCoercedField(Offset, ST->getElementType(i)), i);
+ }
+ return Val;
+}
+
/// CreateCoercedLoad - Create a load from \arg SrcPtr interpreted as
/// a pointer to an object of type \arg Ty, known to be aligned to
/// \arg SrcAlign bytes.
@@ -1317,7 +1372,8 @@ static llvm::Value *CoerceIntOrPtrToIntOrPtr(llvm::Value *Val, llvm::Type *Ty,
/// This safely handles the case when the src type is smaller than the
/// destination type; in this situation the values of bits which not
/// present in the src are undefined.
-static llvm::Value *CreateCoercedLoad(Address Src, llvm::Type *Ty,
+static llvm::Value *CreateCoercedLoad(Address Src, QualType SrcFETy,
+ llvm::Type *Ty,
CodeGenFunction &CGF) {
llvm::Type *SrcTy = Src.getElementType();
@@ -1325,6 +1381,9 @@ static llvm::Value *CreateCoercedLoad(Address Src, llvm::Type *Ty,
if (SrcTy == Ty)
return CGF.Builder.CreateLoad(Src);
+ if (llvm::Value *V = CreatePFPCoercedLoad(Src, SrcFETy, Ty, CGF))
+ return V;
+
llvm::TypeSize DstSize = CGF.CGM.getDataLayout().getTypeAllocSize(Ty);
if (llvm::StructType *SrcSTy = dyn_cast<llvm::StructType>(SrcTy)) {
@@ -1396,7 +1455,60 @@ static llvm::Value *CreateCoercedLoad(Address Src, llvm::Type *Ty,
return CGF.Builder.CreateLoad(Tmp);
}
-void CodeGenFunction::CreateCoercedStore(llvm::Value *Src, Address Dst,
+static bool CreatePFPCoercedStore(llvm::Value *Src, QualType SrcFETy,
+ Address Dst, CodeGenFunction &CGF) {
+ // Coercion directly through memory does not work if the structure has pointer
+ // field protection because the struct passed by value has a different bit
+ // pattern to the struct in memory, so we must read the elements one by one
+ // and use them to form the coerced structure.
+ std::vector<PFPField> PFPFields;
+ CGF.getContext().findPFPFields(SrcFETy, CharUnits::Zero(), PFPFields, true);
+ if (PFPFields.empty())
+ return false;
+
+ llvm::Type *SrcTy = Src->getType();
+ auto StoreCoercedField = [&](CharUnits Offset, llvm::Value *FieldVal) {
+ if (!PFPFields.empty() && PFPFields[0].offset == Offset) {
+ auto fieldAddr = CGF.EmitAddressOfPFPField(Dst, PFPFields[0]);
+ if (isa<llvm::IntegerType>(FieldVal->getType()))
+ FieldVal = CGF.Builder.CreateIntToPtr(FieldVal, CGF.VoidPtrTy);
+ CGF.Builder.CreateStore(FieldVal, fieldAddr);
+ PFPFields.erase(PFPFields.begin());
+ } else {
+ auto fieldAddr =
+ CGF.Builder
+ .CreateConstInBoundsByteGEP(Dst.withElementType(CGF.Int8Ty), Offset)
+ .withElementType(FieldVal->getType());
+ CGF.Builder.CreateStore(FieldVal, fieldAddr);
+ }
+ };
+
+ if (isa<llvm::IntegerType>(SrcTy) || isa<llvm::PointerType>(SrcTy)) {
+ if (isa<llvm::IntegerType>(SrcTy))
+ Src = CGF.Builder.CreateIntToPtr(Src, CGF.VoidPtrTy);
+ auto Addr = CGF.EmitAddressOfPFPField(Dst, PFPFields[0]);
+ CGF.Builder.CreateStore(Src, Addr);
+ } else if (auto *AT = dyn_cast<llvm::ArrayType>(SrcTy)) {
+ auto *ET = AT->getElementType();
+ CharUnits WordSize = CGF.getContext().toCharUnitsFromBits(
+ CGF.CGM.getDataLayout().getTypeSizeInBits(ET));
+ CharUnits Offset = CharUnits::Zero();
+ for (unsigned i = 0; i != AT->getNumElements(); ++i, Offset += WordSize)
+ StoreCoercedField(Offset, CGF.Builder.CreateExtractValue(Src, i));
+ } else {
+ auto *ST = cast<llvm::StructType>(SrcTy);
+ auto *SL = CGF.CGM.getDataLayout().getStructLayout(ST);
+ for (unsigned i = 0; i != ST->getNumElements(); ++i) {
+ CharUnits Offset = CharUnits::fromQuantity(SL->getElementOffset(i));
+ StoreCoercedField(Offset, CGF.Builder.CreateExtractValue(Src, i));
+ }
+ }
+ return true;
+}
+
+void CodeGenFunction::CreateCoercedStore(llvm::Value *Src,
+ QualType SrcFETy,
+ Address Dst,
llvm::TypeSize DstSize,
bool DstIsVolatile) {
if (!DstSize)
@@ -1417,6 +1529,9 @@ void CodeGenFunction::CreateCoercedStore(llvm::Value *Src, Address Dst,
}
}
+ if (CreatePFPCoercedStore(Src, SrcFETy, Dst, *this))
+ return;
+
if (SrcSize.isScalable() || SrcSize <= DstSize) {
if (SrcTy->isIntegerTy() && Dst.getElementType()->isPointerTy() &&
SrcSize == CGM.getDataLayout().getTypeAllocSize(Dst.getElementType())) {
@@ -3414,6 +3529,13 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,
if (SrcSize > DstSize) {
Builder.CreateMemCpy(Ptr, AddrToStoreInto, DstSize);
}
+
+ // Structures with PFP fields require a coerced store to add any
+ // pointer signatures.
+ if (getContext().hasPFPFields(Ty)) {
+ llvm::Value *Struct = Builder.CreateLoad(Ptr);
+ CreatePFPCoercedStore(Struct, Ty, Ptr, *this);
+ }
}
} else {
// Simple case, just do a coerced store of the argument into the alloca.
@@ -3421,7 +3543,7 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI,
auto AI = Fn->getArg(FirstIRArg);
AI->setName(Arg->getName() + ".coerce");
CreateCoercedStore(
- AI, Ptr,
+ AI, Ty, Ptr,
llvm::TypeSize::getFixed(
getContext().getTypeSizeInChars(Ty).getQuantity() -
ArgI.getDirectOffset()),
@@ -4073,7 +4195,7 @@ void CodeGenFunction::EmitFunctionEpilog(
// If the value is offset in memory, apply the offset now.
Address V = emitAddressAtOffset(*this, ReturnValue, RetAI);
- RV = CreateCoercedLoad(V, RetAI.getCoerceToType(), *this);
+ RV = CreateCoercedLoad(V, RetTy, RetAI.getCoerceToType(), *this);
}
// In ARC, end functions that return a retainable type with a call
@@ -4123,6 +4245,7 @@ void CodeGenFunction::EmitFunctionEpilog(
auto eltAddr = Builder.CreateStructGEP(addr, i);
llvm::Value *elt = CreateCoercedLoad(
eltAddr,
+ RetTy,
unpaddedStruct ? unpaddedStruct->getElementType(unpaddedIndex++)
: unpaddedCoercionType,
*this);
@@ -5608,15 +5731,25 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
} else {
uint64_t SrcSize = SrcTypeSize.getFixedValue();
uint64_t DstSize = DstTypeSize.getFixedValue();
+ bool HasPFPFields = getContext().hasPFPFields(I->Ty);
// If the source type is smaller than the destination type of the
// coerce-to logic, copy the source value into a temp alloca the size
// of the destination type to allow loading all of it. The bits past
// the source value are left undef.
- if (SrcSize < DstSize) {
+ if (HasPFPFields || SrcSize < DstSize) {
Address TempAlloca = CreateTempAlloca(STy, Src.getAlignment(),
Src.getName() + ".coerce");
- Builder.CreateMemCpy(TempAlloca, Src, SrcSize);
+ if (HasPFPFields) {
+ // Structures with PFP fields require a coerced load to remove any
+ // pointer signatures.
+ Builder.CreateStore(
+ CreatePFPCoercedLoad(Src, I->Ty, ArgInfo.getCoerceToType(),
+ *this),
+ TempAlloca);
+ } else {
+ Builder.CreateMemCpy(TempAlloca, Src, SrcSize);
+ }
Src = TempAlloca;
} else {
Src = Src.withElementType(STy);
@@ -5635,7 +5768,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
// In the simple case, just pass the coerced loaded value.
assert(NumIRArgs == 1);
llvm::Value *Load =
- CreateCoercedLoad(Src, ArgInfo.getCoerceToType(), *this);
+ CreateCoercedLoad(Src, I->Ty, ArgInfo.getCoerceToType(), *this);
if (CallInfo.isCmseNSCall()) {
// For certain parameter types, clear padding bits, as they may reveal
@@ -5696,6 +5829,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
Address eltAddr = Builder.CreateStructGEP(addr, i);
llvm::Value *elt = CreateCoercedLoad(
eltAddr,
+ I->Ty,
unpaddedStruct ? unpaddedStruct->getElementType(unpaddedIndex++)
: unpaddedCoercionType,
*this);
@@ -6211,7 +6345,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
// If the value is offset in memory, apply the offset now.
Address StorePtr = emitAddressAtOffset(*this, DestPtr, RetAI);
CreateCoercedStore(
- CI, StorePtr,
+ CI, RetTy, StorePtr,
llvm::TypeSize::getFixed(DestSize - RetAI.getDirectOffset()),
DestIsVolatile);
}
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp
index bae55aa..fd99202 100644
--- a/clang/lib/CodeGen/CGClass.cpp
+++ b/clang/lib/CodeGen/CGClass.cpp
@@ -585,12 +585,20 @@ static void EmitBaseInitializer(CodeGenFunction &CGF,
isBaseVirtual);
}
-static bool isMemcpyEquivalentSpecialMember(const CXXMethodDecl *D) {
+static bool isMemcpyEquivalentSpecialMember(CodeGenModule &CGM,
+ const CXXMethodDecl *D) {
auto *CD = dyn_cast<CXXConstructorDecl>(D);
if (!(CD && CD->isCopyOrMoveConstructor()) &&
!D->isCopyAssignmentOperator() && !D->isMoveAssignmentOperator())
return false;
+ // Non-trivially-copyable fields with pointer field protection need to be
+ // copied one by one.
+ if (!CGM.getContext().arePFPFieldsTriviallyCopyable(D->getParent()) &&
+ CGM.getContext().hasPFPFields(
+ CGM.getContext().getCanonicalTagType(D->getParent())))
+ return false;
+
// We can emit a memcpy for a trivial copy or move constructor/assignment.
if (D->isTrivial() && !D->getParent()->mayInsertExtraPadding())
return true;
@@ -656,7 +664,7 @@ static void EmitMemberInitializer(CodeGenFunction &CGF,
QualType BaseElementTy = CGF.getContext().getBaseElementType(Array);
CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(MemberInit->getInit());
if (BaseElementTy.isPODType(CGF.getContext()) ||
- (CE && isMemcpyEquivalentSpecialMember(CE->getConstructor()))) {
+ (CE && isMemcpyEquivalentSpecialMember(CGF.CGM, CE->getConstructor()))) {
unsigned SrcArgIndex =
CGF.CGM.getCXXABI().getSrcArgforCopyCtor(Constructor, Args);
llvm::Value *SrcPtr
@@ -923,6 +931,11 @@ namespace {
if (PointerAuthQualifier Q = F->getType().getPointerAuth();
Q && Q.isAddressDiscriminated())
return false;
+ // Non-trivially-copyable fields with pointer field protection need to be
+ // copied one by one.
+ if (!CGF.getContext().arePFPFieldsTriviallyCopyable(ClassDecl) &&
+ CGF.getContext().isPFPField(F))
+ return false;
return true;
}
@@ -1060,7 +1073,8 @@ namespace {
CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(MemberInit->getInit());
// Bail out on non-memcpyable, not-trivially-copyable members.
- if (!(CE && isMemcpyEquivalentSpecialMember(CE->getConstructor())) &&
+ if (!(CE &&
+ isMemcpyEquivalentSpecialMember(CGF.CGM, CE->getConstructor())) &&
!(FieldType.isTriviallyCopyableType(CGF.getContext()) ||
FieldType->isReferenceType()))
return false;
@@ -1171,7 +1185,7 @@ namespace {
return nullptr;
} else if (CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(S)) {
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MCE->getCalleeDecl());
- if (!(MD && isMemcpyEquivalentSpecialMember(MD)))
+ if (!(MD && isMemcpyEquivalentSpecialMember(CGF.CGM, MD)))
return nullptr;
MemberExpr *IOA = dyn_cast<MemberExpr>(MCE->getImplicitObjectArgument());
if (!IOA)
@@ -2138,7 +2152,7 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D,
// If this is a trivial constructor, emit a memcpy now before we lose
// the alignment information on the argument.
// FIXME: It would be better to preserve alignment information into CallArg.
- if (isMemcpyEquivalentSpecialMember(D)) {
+ if (isMemcpyEquivalentSpecialMember(CGM, D)) {
assert(E->getNumArgs() == 1 && "unexpected argcount for trivial ctor");
const Expr *Arg = E->getArg(0);
@@ -2206,7 +2220,7 @@ void CodeGenFunction::EmitCXXConstructorCall(
// If this is a trivial constructor, just emit what's needed. If this is a
// union copy constructor, we must emit a memcpy, because the AST does not
// model that copy.
- if (isMemcpyEquivalentSpecialMember(D)) {
+ if (isMemcpyEquivalentSpecialMember(CGM, D)) {
assert(Args.size() == 2 && "unexpected argcount for trivial ctor");
QualType SrcTy = D->getParamDecl(0)->getType().getNonReferenceType();
Address Src = makeNaturalAddressForPointer(
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 844b445..70eae95 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -5191,12 +5191,13 @@ static Address emitAddrOfZeroSizeField(CodeGenFunction &CGF, Address Base,
return CGF.Builder.CreateConstInBoundsByteGEP(Base, Offset);
}
-/// Drill down to the storage of a field without walking into
-/// reference types.
+/// Drill down to the storage of a field without walking into reference types,
+/// and without respect for pointer field protection.
///
/// The resulting address doesn't necessarily have the right type.
-static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base,
- const FieldDecl *field, bool IsInBounds) {
+static Address emitRawAddrOfFieldStorage(CodeGenFunction &CGF, Address base,
+ const FieldDecl *field,
+ bool IsInBounds) {
if (isEmptyFieldForLayout(CGF.getContext(), field))
return emitAddrOfZeroSizeField(CGF, base, field, IsInBounds);
@@ -5211,6 +5212,16 @@ static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base,
return CGF.Builder.CreateStructGEP(base, idx, field->getName());
}
+static Address emitAddrOfFieldStorage(CodeGenFunction &CGF, Address base,
+ const FieldDecl *field, bool IsInBounds) {
+ Address Addr = emitRawAddrOfFieldStorage(CGF, base, field, IsInBounds);
+
+ if (!CGF.getContext().isPFPField(field))
+ return Addr;
+
+ return CGF.EmitAddressOfPFPField(base, Addr, field);
+}
+
static Address emitPreserveStructAccess(CodeGenFunction &CGF, LValue base,
Address addr, const FieldDecl *field) {
const RecordDecl *rec = field->getParent();
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index b8150a2..ad38a763 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -138,7 +138,7 @@ public:
if (llvm::Value *Result = ConstantEmitter(CGF).tryEmitConstantExpr(E)) {
CGF.CreateCoercedStore(
- Result, Dest.getAddress(),
+ Result, E->getType(), Dest.getAddress(),
llvm::TypeSize::getFixed(
Dest.getPreferredSize(CGF.getContext(), E->getType())
.getQuantity()),
@@ -2391,6 +2391,7 @@ void CodeGenFunction::EmitAggregateCopy(LValue Dest, LValue Src, QualType Ty,
auto *Inst = Builder.CreateMemCpy(DestPtr, SrcPtr, SizeVal, isVolatile);
addInstToCurrentSourceAtom(Inst, nullptr);
+ emitPFPTrivialRelocation(DestPtr, SrcPtr, Ty);
// Determine the metadata to describe the position of any padding in this
// memcpy, as well as the TBAA tags for the members of the struct, in case
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index b44dd9e..c135b99 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -31,6 +31,7 @@
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalVariable.h"
+#include "llvm/Support/SipHash.h"
#include <optional>
using namespace clang;
using namespace CodeGen;
@@ -905,6 +906,29 @@ bool ConstStructBuilder::Build(const APValue &Val, const RecordDecl *RD,
if (!EltInit)
return false;
+ if (CGM.getContext().isPFPField(*Field)) {
+ llvm::ConstantInt *Disc;
+ llvm::Constant *AddrDisc;
+ if (CGM.getContext().arePFPFieldsTriviallyCopyable(RD)) {
+ uint64_t FieldSignature =
+ llvm::getPointerAuthStableSipHash(CGM.getPFPFieldName(*Field));
+ Disc = llvm::ConstantInt::get(CGM.Int64Ty, FieldSignature);
+ AddrDisc = llvm::ConstantPointerNull::get(CGM.VoidPtrTy);
+ } else if (Emitter.isAbstract()) {
+ return false;
+ } else {
+ Disc = llvm::ConstantInt::get(
+ CGM.Int64Ty, (-(Layout.getFieldOffset(FieldNo) / 8)) & 0xffff);
+ AddrDisc = Emitter.getCurrentAddrPrivate();
+ }
+ EltInit = llvm::ConstantPtrAuth::get(
+ EltInit, llvm::ConstantInt::get(CGM.Int32Ty, 2), Disc, AddrDisc,
+ CGM.getPFPDeactivationSymbol(*Field));
+ if (!CGM.getContext().arePFPFieldsTriviallyCopyable(RD))
+ Emitter.registerCurrentAddrPrivate(EltInit,
+ cast<llvm::GlobalValue>(AddrDisc));
+ }
+
if (ZeroInitPadding) {
if (!DoZeroInitPadding(Layout, FieldNo, **Field, AllowOverwrite,
SizeSoFar, ZeroFieldSize))
@@ -1655,7 +1679,20 @@ ConstantEmitter::emitAbstract(SourceLocation loc, const APValue &value,
llvm::Constant *ConstantEmitter::tryEmitForInitializer(const VarDecl &D) {
initializeNonAbstract(D.getType().getAddressSpace());
- return markIfFailed(tryEmitPrivateForVarInit(D));
+ llvm::Constant *Init = tryEmitPrivateForVarInit(D);
+
+ // If a placeholder address was needed for a TLS variable, implying that the
+ // initializer's value depends on its address, then the object may not be
+ // initialized in .tdata because the initializer will be memcpy'd to the
+ // thread's TLS. Instead the initialization must be done in code.
+ if (!PlaceholderAddresses.empty() && D.getTLSKind() != VarDecl::TLS_None) {
+ for (auto &entry : PlaceholderAddresses)
+ entry.second->eraseFromParent();
+ PlaceholderAddresses.clear();
+ Init = nullptr;
+ }
+
+ return markIfFailed(Init);
}
llvm::Constant *ConstantEmitter::tryEmitForInitializer(const Expr *E,
@@ -2610,6 +2647,7 @@ CodeGenModule::getMemberPointerConstant(const UnaryOperator *uo) {
return getCXXABI().EmitMemberFunctionPointer(method);
// Otherwise, a member data pointer.
+ getContext().recordMemberDataPointerEvaluation(decl);
uint64_t fieldOffset = getContext().getFieldOffset(decl);
CharUnits chars = getContext().toCharUnitsFromBits((int64_t) fieldOffset);
return getCXXABI().EmitMemberDataPointer(type, chars);
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index b2fe917..14f31a4 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -46,6 +46,7 @@
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/MDBuilder.h"
#include "llvm/Support/CRC.h"
+#include "llvm/Support/SipHash.h"
#include "llvm/Support/xxhash.h"
#include "llvm/Transforms/Scalar/LowerExpectIntrinsic.h"
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
@@ -2218,6 +2219,45 @@ static void emitNonZeroVLAInit(CodeGenFunction &CGF, QualType baseType,
CGF.EmitBlock(contBB);
}
+Address CodeGenFunction::EmitAddressOfPFPField(Address RecordPtr,
+ const PFPField &Field) {
+ return EmitAddressOfPFPField(
+ RecordPtr,
+ Builder.CreateConstInBoundsByteGEP(RecordPtr.withElementType(Int8Ty),
+ Field.offset),
+ Field.field);
+}
+
+Address CodeGenFunction::EmitAddressOfPFPField(Address RecordPtr,
+ Address PtrPtr,
+ const FieldDecl *Field) {
+ llvm::Value *Disc;
+ bool IsPAuthSupported = getContext().getTargetInfo().getTriple().getArch() ==
+ llvm::Triple::aarch64;
+ if (!IsPAuthSupported ||
+ CGM.getContext().arePFPFieldsTriviallyCopyable(Field->getParent())) {
+ uint64_t FieldSignature =
+ llvm::getPointerAuthStableSipHash(CGM.getPFPFieldName(Field));
+ if (!IsPAuthSupported)
+ FieldSignature &= 0xff;
+ Disc = llvm::ConstantInt::get(CGM.Int64Ty, FieldSignature);
+ } else {
+ Disc = Builder.CreatePtrToInt(RecordPtr.getBasePointer(), CGM.Int64Ty);
+ }
+
+ llvm::GlobalValue *DS = CGM.getPFPDeactivationSymbol(Field);
+ llvm::OperandBundleDef DSBundle("deactivation-symbol", DS);
+
+ return Address(
+ Builder.CreateCall(
+ CGM.getIntrinsic(llvm::Intrinsic::protected_field_ptr,
+ PtrPtr.getType()),
+ {PtrPtr.getBasePointer(), Disc,
+ IsPAuthSupported ? Builder.getTrue() : Builder.getFalse()},
+ DSBundle),
+ VoidPtrTy, PtrPtr.getAlignment());
+}
+
void
CodeGenFunction::EmitNullInitialization(Address DestPtr, QualType Ty) {
// Ignore empty classes in C++.
@@ -2277,13 +2317,22 @@ CodeGenFunction::EmitNullInitialization(Address DestPtr, QualType Ty) {
// Get and call the appropriate llvm.memcpy overload.
Builder.CreateMemCpy(DestPtr, SrcPtr, SizeVal, false);
- return;
+ } else {
+ // Otherwise, just memset the whole thing to zero. This is legal
+ // because in LLVM, all default initializers (other than the ones we just
+ // handled above, and the case handled below) are guaranteed to have a bit
+ // pattern of all zeros.
+ Builder.CreateMemSet(DestPtr, Builder.getInt8(0), SizeVal, false);
}
- // Otherwise, just memset the whole thing to zero. This is legal
- // because in LLVM, all default initializers (other than the ones we just
- // handled above) are guaranteed to have a bit pattern of all zeros.
- Builder.CreateMemSet(DestPtr, Builder.getInt8(0), SizeVal, false);
+ // With the pointer field protection feature, null pointers do not have a bit
+ // pattern of zero in memory, so we must initialize them separately.
+ std::vector<PFPField> PFPFields;
+ getContext().findPFPFields(Ty, CharUnits::Zero(), PFPFields, true);
+ for (auto &Field : PFPFields) {
+ auto addr = EmitAddressOfPFPField(DestPtr, Field);
+ Builder.CreateStore(llvm::ConstantPointerNull::get(VoidPtrTy), addr);
+ }
}
llvm::BlockAddress *CodeGenFunction::GetAddrOfLabel(const LabelDecl *L) {
@@ -3386,3 +3435,16 @@ void CodeGenFunction::addInstToNewSourceAtom(llvm::Instruction *KeyInstruction,
DI->addInstToCurrentSourceAtom(KeyInstruction, Backup);
}
}
+
+void CodeGenFunction::emitPFPTrivialRelocation(Address DestPtr, Address SrcPtr,
+ QualType Ty) {
+ std::vector<PFPField> PFPFields;
+ getContext().findPFPFields(Ty, CharUnits::Zero(), PFPFields, true);
+ for (auto &Field : PFPFields) {
+ if (getContext().arePFPFieldsTriviallyCopyable(Field.field->getParent()))
+ continue;
+ auto DestFieldPtr = EmitAddressOfPFPField(DestPtr, Field);
+ auto SrcFieldPtr = EmitAddressOfPFPField(SrcPtr, Field);
+ Builder.CreateStore(Builder.CreateLoad(SrcFieldPtr), DestFieldPtr);
+ }
+}
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index c02ac18..0e7d410 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1708,6 +1708,10 @@ public:
void addInstToNewSourceAtom(llvm::Instruction *KeyInstruction,
llvm::Value *Backup);
+ /// Trivially relocate all PFP fields from SrcPtr (of type Ty) to DestPtr,
+ /// assuming that DestPtr was already memcpy'd from SrcPtr.
+ void emitPFPTrivialRelocation(Address DestPtr, Address SrcPtr, QualType Ty);
+
private:
/// SwitchInsn - This is nearest current switch instruction. It is null if
/// current context is not in a switch.
@@ -5018,8 +5022,8 @@ public:
/// Create a store to \arg DstPtr from \arg Src, truncating the stored value
/// to at most \arg DstSize bytes.
- void CreateCoercedStore(llvm::Value *Src, Address Dst, llvm::TypeSize DstSize,
- bool DstIsVolatile);
+ void CreateCoercedStore(llvm::Value *Src, QualType SrcFETy, Address Dst,
+ llvm::TypeSize DstSize, bool DstIsVolatile);
/// EmitExtendGCLifetime - Given a pointer to an Objective-C object,
/// make sure it survives garbage collection until this point.
@@ -5514,6 +5518,10 @@ public:
void EmitRISCVMultiVersionResolver(llvm::Function *Resolver,
ArrayRef<FMVResolverOption> Options);
+ Address EmitAddressOfPFPField(Address RecordPtr, const PFPField &Field);
+ Address EmitAddressOfPFPField(Address RecordPtr, Address FieldPtr,
+ const FieldDecl *Field);
+
private:
QualType getVarArgType(const Expr *Arg);
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 323823c..c28537d 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -960,6 +960,7 @@ void CodeGenModule::Release() {
applyGlobalValReplacements();
applyReplacements();
emitMultiVersionFunctions();
+ emitPFPFieldsWithEvaluatedOffset();
if (Context.getLangOpts().IncrementalExtensions &&
GlobalTopLevelStmtBlockInFlight.first) {
@@ -4610,6 +4611,35 @@ void CodeGenModule::emitMultiVersionFunctions() {
emitMultiVersionFunctions();
}
+llvm::GlobalValue *CodeGenModule::getPFPDeactivationSymbol(const FieldDecl *FD) {
+ std::string DSName = "__pfp_ds_" + getPFPFieldName(FD);
+ llvm::GlobalValue *DS = TheModule.getNamedValue(DSName);
+ if (!DS) {
+ DS = new llvm::GlobalVariable(TheModule, Int8Ty, false,
+ llvm::GlobalVariable::ExternalWeakLinkage,
+ nullptr, DSName);
+ DS->setVisibility(llvm::GlobalValue::HiddenVisibility);
+ }
+ return DS;
+}
+
+void CodeGenModule::emitPFPFieldsWithEvaluatedOffset() {
+ llvm::Constant *Nop = llvm::ConstantExpr::getIntToPtr(
+ llvm::ConstantInt::get(Int64Ty, 0xd503201f), VoidPtrTy);
+ for (auto *FD : getContext().PFPFieldsWithEvaluatedOffset) {
+ std::string DSName = "__pfp_ds_" + getPFPFieldName(FD);
+ llvm::GlobalValue *OldDS = TheModule.getNamedValue(DSName);
+ llvm::GlobalValue *DS = llvm::GlobalAlias::create(
+ Int8Ty, 0, llvm::GlobalValue::ExternalLinkage, DSName, Nop, &TheModule);
+ DS->setVisibility(llvm::GlobalValue::HiddenVisibility);
+ if (OldDS) {
+ DS->takeName(OldDS);
+ OldDS->replaceAllUsesWith(DS);
+ OldDS->eraseFromParent();
+ }
+ }
+}
+
static void replaceDeclarationWith(llvm::GlobalValue *Old,
llvm::Constant *New) {
assert(cast<llvm::Function>(Old)->isDeclaration() && "Not a declaration");
@@ -8147,3 +8177,12 @@ void CodeGenModule::moveLazyEmissionStates(CodeGenModule *NewBuilder) {
NewBuilder->ABI->MangleCtx = std::move(ABI->MangleCtx);
}
+
+std::string CodeGenModule::getPFPFieldName(const FieldDecl *FD) {
+ std::string OutName;
+ llvm::raw_string_ostream Out(OutName);
+ getCXXABI().getMangleContext().mangleCanonicalTypeName(
+ getContext().getCanonicalTagType(FD->getParent()), Out, false);
+ Out << "." << FD->getName();
+ return OutName;
+}
diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h
index b4b3a17..db73f23 100644
--- a/clang/lib/CodeGen/CodeGenModule.h
+++ b/clang/lib/CodeGen/CodeGenModule.h
@@ -1830,6 +1830,9 @@ public:
return TrapReasonBuilder(&getDiags(), DiagID, TR);
}
+ std::string getPFPFieldName(const FieldDecl *FD);
+ llvm::GlobalValue *getPFPDeactivationSymbol(const FieldDecl *FD);
+
private:
bool shouldDropDLLAttribute(const Decl *D, const llvm::GlobalValue *GV) const;
@@ -2024,6 +2027,10 @@ private:
llvm::Metadata *CreateMetadataIdentifierImpl(QualType T, MetadataTypeMap &Map,
StringRef Suffix);
+
+ /// Emit deactivation symbols for any PFP fields whose offset is taken with
+ /// offsetof.
+ void emitPFPFieldsWithEvaluatedOffset();
};
} // end namespace CodeGen
diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp
index 885b700..dfe8c69 100644
--- a/clang/lib/CodeGen/ItaniumCXXABI.cpp
+++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp
@@ -1245,6 +1245,7 @@ llvm::Constant *ItaniumCXXABI::EmitMemberPointer(const APValue &MP,
return pointerAuthResignMemberFunctionPointer(Src, MPType, SrcType, CGM);
}
+ getContext().recordMemberDataPointerEvaluation(MPD);
CharUnits FieldOffset =
getContext().toCharUnitsFromBits(getContext().getFieldOffset(MPD));
return EmitMemberDataPointer(MPT, ThisAdjustment + FieldOffset);
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index 88f0648..8ffcfe4 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -2920,6 +2920,7 @@ llvm::Constant *MicrosoftCXXABI::EmitMemberPointer(const APValue &MP,
// the class in which it was declared, and convert from there if necessary.
// For indirect field decls, get the outermost anonymous field and use the
// parent class.
+ Ctx.recordMemberDataPointerEvaluation(MPD);
CharUnits FieldOffset = Ctx.toCharUnitsFromBits(Ctx.getFieldOffset(MPD));
const FieldDecl *FD = dyn_cast<FieldDecl>(MPD);
if (!FD)
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index d22feae..2a5170a 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7624,6 +7624,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Twine("-funique-source-file-identifier=") + Input.getBaseInput()));
}
+ if (!IsCudaDevice) {
+ Args.addOptInFlag(CmdArgs,
+ options::OPT_fexperimental_pointer_field_protection,
+ options::OPT_fno_experimental_pointer_field_protection);
+ Args.addOptInFlag(
+ CmdArgs, options::OPT_fexperimental_pointer_field_protection_tagged,
+ options::OPT_fno_experimental_pointer_field_protection_tagged);
+ }
+
// Setup statistics file output.
SmallString<128> StatsFile = getStatsFileName(Args, Output, Input, D);
if (!StatsFile.empty()) {
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 4865c0b..76bea60 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -1526,6 +1526,11 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
if (LangOpts.Sanitize.has(SanitizerKind::Thread))
Builder.defineMacro("__SANITIZE_THREAD__");
+ if (LangOpts.PointerFieldProtection)
+ Builder.defineMacro("__POINTER_FIELD_PROTECTION__");
+ if (LangOpts.PointerFieldProtectionTagged)
+ Builder.defineMacro("__POINTER_FIELD_PROTECTION_TAGGED__");
+
// Target OS macro definitions.
if (PPOpts.DefineTargetOSMacros) {
const llvm::Triple &Triple = TI.getTriple();
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 3ded60c..b54a8ef 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6386,6 +6386,10 @@ static void handleZeroCallUsedRegsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
D->addAttr(ZeroCallUsedRegsAttr::Create(S.Context, Kind, AL));
}
+static void handleNoPFPAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
+ D->addAttr(NoFieldProtectionAttr::Create(S.Context, AL));
+}
+
static void handleCountedByAttrField(Sema &S, Decl *D, const ParsedAttr &AL) {
auto *FD = dyn_cast<FieldDecl>(D);
assert(FD);
@@ -7411,6 +7415,10 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
handleCountedByAttrField(S, D, AL);
break;
+ case ParsedAttr::AT_NoFieldProtection:
+ handleNoPFPAttrField(S, D, AL);
+ break;
+
// Microsoft attributes:
case ParsedAttr::AT_LayoutVersion:
handleLayoutVersion(S, D, AL);
diff --git a/clang/lib/Sema/SemaTypeTraits.cpp b/clang/lib/Sema/SemaTypeTraits.cpp
index 3755233..ede411d 100644
--- a/clang/lib/Sema/SemaTypeTraits.cpp
+++ b/clang/lib/Sema/SemaTypeTraits.cpp
@@ -234,6 +234,22 @@ static bool IsEligibleForReplacement(Sema &SemaRef, const CXXRecordDecl *D) {
return !D->hasDeletedDestructor();
}
+static bool IsImplementationDefinedNonRelocatable(Sema &SemaRef,
+ const CXXRecordDecl *D) {
+ // The implementation-defined carveout only exists for polymorphic types.
+ if (!D->isPolymorphic())
+ return false;
+
+ std::vector<PFPField> pfpFields;
+ SemaRef.Context.findPFPFields(SemaRef.Context.getCanonicalTagType(D),
+ CharUnits::Zero(), pfpFields, true);
+ for (PFPField f : pfpFields)
+ if (f.isWithinUnion)
+ return true;
+
+ return false;
+}
+
ASTContext::CXXRecordDeclRelocationInfo
Sema::CheckCXX2CRelocatableAndReplaceable(const CXXRecordDecl *D) {
ASTContext::CXXRecordDeclRelocationInfo Info{false, false};
@@ -275,6 +291,11 @@ Sema::CheckCXX2CRelocatableAndReplaceable(const CXXRecordDecl *D) {
if (!IsEligibleForTrivialRelocation(*this, D))
return false;
+ // if it does not meet implementation-defined criteria for inhibiting
+ // trivially-relocatable,
+ if (IsImplementationDefinedNonRelocatable(*this, D))
+ return false;
+
// has the trivially_relocatable_if_eligible class-property-specifier,
if (D->hasAttr<TriviallyRelocatableAttr>())
return true;
diff --git a/clang/test/CodeGenCXX/pfp-attribute-disable.cpp b/clang/test/CodeGenCXX/pfp-attribute-disable.cpp
new file mode 100644
index 0000000..bb0011a
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-attribute-disable.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck %s
+
+
+struct S {
+ int* ptr1;
+ __attribute__((no_field_protection)) int* ptr2;
+private:
+ int private_data;
+}; // Not Standard-layout, mixed access
+
+// CHECK-LABEL: load_pointers_without_no_field_protection
+int* load_pointers_without_no_field_protection(S *t) {
+ return t->ptr1;
+}
+// CHECK: call {{.*}} @llvm.protected.field.ptr.p0{{.*}}
+
+// CHECK-LABEL: load_pointers_with_no_field_protection
+int* load_pointers_with_no_field_protection(S *t) {
+ return t->ptr2;
+}
+// CHECK-NOT: call {{.*}} @llvm.protected.field.ptr.p0{{.*}}
+
+// CHECK-LABEL: store_pointers_without_no_field_protection
+void store_pointers_without_no_field_protection(S *t, int *input) {
+ t->ptr1 = input;
+}
+// CHECK: call {{.*}} @llvm.protected.field.ptr.p0{{.*}}
+
+// CHECK-LABEL: store_pointers_with_no_field_protection
+void store_pointers_with_no_field_protection(S *t, int *input) {
+ t->ptr2 = input;
+}
+// CHECK-NOT: call {{.*}} @llvm.protected.field.ptr.p0{{.*}}
diff --git a/clang/test/CodeGenCXX/pfp-coerce.cpp b/clang/test/CodeGenCXX/pfp-coerce.cpp
new file mode 100644
index 0000000..770d749
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-coerce.cpp
@@ -0,0 +1,222 @@
+// RUN: %clang_cc1 -triple aarch64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,AARCH64 %s
+// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,X86_64 %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
+
+ // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+ // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 51, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+
+ // CHECK: %3 = load ptr, ptr %2, align 8
+
+ // AARCH64: %4 = ptrtoint ptr %3 to i64
+ // AARCH64: %5 = insertvalue [2 x i64] poison, i64 %4, 0
+ // AARCH64: %6 = getelementptr inbounds i8, ptr %agg.tmp, i64 8
+ // AARCH64: %7 = load i64, ptr %6, align 8
+ // AARCH64: %8 = insertvalue [2 x i64] %5, i64 %7, 1
+ // AARCH64: call void @_Z19pass_pointer_callee7Pointer([2 x i64] %8)
+
+ // X86_64: %4 = insertvalue { ptr, i32 } poison, ptr %3, 0
+ // X86_64: %5 = getelementptr inbounds i8, ptr %agg.tmp, i64 8
+ // X86_64: %6 = load i32, ptr %5, align 8
+ // X86_64: %7 = insertvalue { ptr, i32 } %4, i32 %6, 1
+ // X86_64: store { ptr, i32 } %7, ptr %agg.tmp.coerce, align 8
+ // X86_64: %8 = getelementptr inbounds nuw { ptr, i32 }, ptr %agg.tmp.coerce, i32 0, i32 0
+ // X86_64: %9 = load ptr, ptr %8, align 8
+ // X86_64: %10 = getelementptr inbounds nuw { ptr, i32 }, ptr %agg.tmp.coerce, i32 0, i32 1
+ // X86_64: %11 = load i32, ptr %10, align 8
+ // X86_64: call void @_Z19pass_pointer_callee7Pointer(ptr %9, i32 %11)
+ pass_pointer_callee(*pp);
+}
+
+// AARCH64: define dso_local void @_Z14passed_pointer7PointerPS_([2 x i64] %p.coerce, ptr noundef %pp)
+// X86_64: define dso_local void @_Z14passed_pointer7PointerPS_(ptr %p.coerce0, i32 %p.coerce1, ptr noundef %pp)
+void passed_pointer(Pointer p, Pointer *pp) {
+ // AARCH64: %p = alloca %struct.Pointer, align 8
+ // AARCH64: %pp.addr = alloca ptr, align 8
+ // AARCH64: %0 = extractvalue [2 x i64] %p.coerce, 0
+ // AARCH64: %1 = getelementptr inbounds i8, ptr %p, i64 0
+ // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+ // AARCH64: %3 = inttoptr i64 %0 to ptr
+ // AARCH64: store ptr %3, ptr %2, align 8
+ // AARCH64: %4 = extractvalue [2 x i64] %p.coerce, 1
+ // AARCH64: %5 = getelementptr inbounds i8, ptr %p, i64 8
+ // AARCH64: store i64 %4, ptr %5, align 8
+ // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+ // AARCH64: %6 = load ptr, ptr %pp.addr, align 8
+ // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %6, ptr align 8 %p, i64 12, i1 false)
+
+ // X86_64: %p = alloca %struct.Pointer, align 8
+ // X86_64: %pp.addr = alloca ptr, align 8
+ // X86_64: %0 = getelementptr inbounds nuw { ptr, i32 }, ptr %p, i32 0, i32 0
+ // X86_64: store ptr %p.coerce0, ptr %0, align 8
+ // X86_64: %1 = getelementptr inbounds nuw { ptr, i32 }, ptr %p, i32 0, i32 1
+ // X86_64: store i32 %p.coerce1, ptr %1, align 8
+ // X86_64: %2 = load %struct.Pointer, ptr %p, align 8
+ // X86_64: %3 = extractvalue %struct.Pointer %2, 0
+ // X86_64: %4 = getelementptr inbounds i8, ptr %p, i64 0
+ // X86_64: %5 = call ptr @llvm.protected.field.ptr.p0(ptr %4, i64 51, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+ // X86_64: store ptr %3, ptr %5, align 8
+ // X86_64: %6 = extractvalue %struct.Pointer %2, 1
+ // X86_64: %7 = getelementptr inbounds i8, ptr %p, i64 8
+ // X86_64: store i32 %6, ptr %7, align 8
+ // X86_64: %8 = extractvalue %struct.Pointer %2, 2
+ // X86_64: %9 = getelementptr inbounds i8, ptr %p, i64 12
+ // X86_64: store [4 x i8] %8, ptr %9, align 4
+ // X86_64: store ptr %pp, ptr %pp.addr, align 8
+ // X86_64: %10 = load ptr, ptr %pp.addr, align 8
+ // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %10, ptr align 8 %p, i64 12, i1 false)
+ *pp = p;
+}
+
+// AARCH64: define dso_local [2 x i64] @_Z14return_pointerP7Pointer(ptr noundef %pp)
+// X86_64: define dso_local { ptr, i32 } @_Z14return_pointerP7Pointer(ptr noundef %pp)
+Pointer return_pointer(Pointer *pp) {
+ // AARCH64: %retval = alloca %struct.Pointer, align 8
+ // AARCH64: %pp.addr = alloca ptr, align 8
+ // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+ // AARCH64: %0 = load ptr, ptr %pp.addr, align 8
+ // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 8 %0, i64 16, i1 false)
+ // AARCH64: %1 = getelementptr inbounds i8, ptr %retval, i64 0
+ // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+ // AARCH64: %3 = load ptr, ptr %2, align 8
+ // AARCH64: %4 = ptrtoint ptr %3 to i64
+ // AARCH64: %5 = insertvalue [2 x i64] poison, i64 %4, 0
+ // AARCH64: %6 = getelementptr inbounds i8, ptr %retval, i64 8
+ // AARCH64: %7 = load i64, ptr %6, align 8
+ // AARCH64: %8 = insertvalue [2 x i64] %5, i64 %7, 1
+ // AARCH64: ret [2 x i64] %8
+
+ // X86_64: %retval = alloca %struct.Pointer, align 8
+ // X86_64: %pp.addr = alloca ptr, align 8
+ // X86_64: store ptr %pp, ptr %pp.addr, align 8
+ // X86_64: %0 = load ptr, ptr %pp.addr, align 8
+ // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 8 %0, i64 16, i1 false)
+ // X86_64: %1 = getelementptr inbounds i8, ptr %retval, i64 0
+ // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 51, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+ // X86_64: %3 = load ptr, ptr %2, align 8
+ // X86_64: %4 = insertvalue { ptr, i32 } poison, ptr %3, 0
+ // X86_64: %5 = getelementptr inbounds i8, ptr %retval, i64 8
+ // X86_64: %6 = load i32, ptr %5, align 8
+ // X86_64: %7 = insertvalue { ptr, i32 } %4, i32 %6, 1
+ // X86_64: ret { ptr, i32 } %7
+ return *pp;
+}
+
+Pointer returned_pointer_callee();
+
+// CHECK: define dso_local void @_Z16returned_pointerP7Pointer(ptr noundef %pp)
+void returned_pointer(Pointer *pp) {
+ // AARCH64: %pp.addr = alloca ptr, align 8
+ // AARCH64: %ref.tmp = alloca %struct.Pointer, align 8
+ // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+ // AARCH64: %call = call [2 x i64] @_Z23returned_pointer_calleev()
+ // AARCH64: %0 = extractvalue [2 x i64] %call, 0
+ // AARCH64: %1 = getelementptr inbounds i8, ptr %ref.tmp, i64 0
+ // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 36403, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+ // AARCH64: %3 = inttoptr i64 %0 to ptr
+ // AARCH64: store ptr %3, ptr %2, align 8
+ // AARCH64: %4 = extractvalue [2 x i64] %call, 1
+ // AARCH64: %5 = getelementptr inbounds i8, ptr %ref.tmp, i64 8
+ // AARCH64: store i64 %4, ptr %5, align 8
+ // AARCH64: %6 = load ptr, ptr %pp.addr, align 8
+ // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %6, ptr align 8 %ref.tmp, i64 12, i1 false)
+
+ // X86_64: %pp.addr = alloca ptr, align 8
+ // X86_64: %ref.tmp = alloca %struct.Pointer, align 8
+ // X86_64: store ptr %pp, ptr %pp.addr, align 8
+ // X86_64: %call = call { ptr, i32 } @_Z23returned_pointer_calleev()
+ // X86_64: %0 = extractvalue { ptr, i32 } %call, 0
+ // X86_64: %1 = getelementptr inbounds i8, ptr %ref.tmp, i64 0
+ // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 51, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS7Pointer.ptr) ]
+ // X86_64: store ptr %0, ptr %2, align 8
+ // X86_64: %3 = extractvalue { ptr, i32 } %call, 1
+ // X86_64: %4 = getelementptr inbounds i8, ptr %ref.tmp, i64 8
+ // X86_64: store i32 %3, ptr %4, align 8
+ // X86_64: %5 = load ptr, ptr %pp.addr, align 8
+ // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %5, ptr align 8 %ref.tmp, i64 12, i1 false)
+ *pp = returned_pointer_callee();
+}
+
+// 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) {
+ // AARCH64: %p = alloca %struct.TrivialAbiPointer, align 8
+ // AARCH64: %pp.addr = alloca ptr, align 8
+ // AARCH64: %coerce.dive = getelementptr inbounds nuw %struct.TrivialAbiPointer, ptr %p, i32 0, i32 0
+ // AARCH64: %0 = getelementptr inbounds i8, ptr %coerce.dive, i64 0
+ // AARCH64: %1 = ptrtoint ptr %coerce.dive to i64
+ // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %0, i64 %1, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+ // AARCH64: store ptr %p.coerce, ptr %2, align 8
+ // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+ // AARCH64: %3 = load ptr, ptr %pp.addr, align 8
+ // AARCH64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %3, ptr align 8 %p, i64 8, i1 false)
+ // AARCH64: %4 = getelementptr inbounds i8, ptr %3, i64 0
+ // AARCH64: %5 = ptrtoint ptr %3 to i64
+ // AARCH64: %6 = call ptr @llvm.protected.field.ptr.p0(ptr %4, i64 %5, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+ // AARCH64: %7 = getelementptr inbounds i8, ptr %p, i64 0
+ // AARCH64: %8 = ptrtoint ptr %p to i64
+ // AARCH64: %9 = call ptr @llvm.protected.field.ptr.p0(ptr %7, i64 %8, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+ // AARCH64: %10 = load ptr, ptr %9, align 8
+ // AARCH64: store ptr %10, ptr %6, align 8
+ // AARCH64: call void @_ZN17TrivialAbiPointerD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %p)
+
+ // X86_64: %p = alloca %struct.TrivialAbiPointer, align 8
+ // X86_64: %pp.addr = alloca ptr, align 8
+ // X86_64: %coerce.dive = getelementptr inbounds nuw %struct.TrivialAbiPointer, ptr %p, i32 0, i32 0
+ // X86_64: %0 = getelementptr inbounds i8, ptr %coerce.dive, i64 0
+ // X86_64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %0, i64 33, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+ // X86_64: store ptr %p.coerce, ptr %1, align 8
+ // X86_64: store ptr %pp, ptr %pp.addr, align 8
+ // X86_64: %2 = load ptr, ptr %pp.addr, align 8
+ // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %2, ptr align 8 %p, i64 8, i1 false)
+ // X86_64: call void @_ZN17TrivialAbiPointerD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %p)
+ *pp = p;
+}
+
+// AARCH64: define dso_local i64 @_Z26return_trivial_abi_pointerP17TrivialAbiPointer(ptr noundef %pp)
+// X86_64: define dso_local ptr @_Z26return_trivial_abi_pointerP17TrivialAbiPointer(ptr noundef %pp)
+TrivialAbiPointer return_trivial_abi_pointer(TrivialAbiPointer *pp) {
+ // AARCH64: %retval = alloca %struct.TrivialAbiPointer, align 8
+ // AARCH64: %pp.addr = alloca ptr, align 8
+ // AARCH64: store ptr %pp, ptr %pp.addr, align 8
+ // AARCH64: %0 = load ptr, ptr %pp.addr, align 8
+ // AARCH64: call void @_ZN17TrivialAbiPointerC1ERKS_(ptr noundef nonnull align 8 dereferenceable(8) %retval, ptr noundef nonnull align 8 dereferenceable(8) %0)
+ // AARCH64: %1 = getelementptr inbounds i8, ptr %retval, i64 0
+ // AARCH64: %2 = ptrtoint ptr %retval to i64
+ // AARCH64: %3 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 %2, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+ // AARCH64: %4 = load ptr, ptr %3, align 8
+ // AARCH64: %5 = ptrtoint ptr %4 to i64
+ // AARCH64: ret i64 %5
+
+ // X86_64: %retval = alloca %struct.TrivialAbiPointer, align 8
+ // X86_64: %pp.addr = alloca ptr, align 8
+ // X86_64: store ptr %pp, ptr %pp.addr, align 8
+ // X86_64: %0 = load ptr, ptr %pp.addr, align 8
+ // X86_64: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %retval, ptr align 8 %0, i64 8, i1 false)
+ // X86_64: %1 = getelementptr inbounds i8, ptr %retval, i64 0
+ // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %1, i64 33, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS17TrivialAbiPointer.ptr) ]
+ // X86_64: %3 = load ptr, ptr %2, align 8
+ // X86_64: ret ptr %3
+ return *pp;
+}
+
diff --git a/clang/test/CodeGenCXX/pfp-load-store.cpp b/clang/test/CodeGenCXX/pfp-load-store.cpp
new file mode 100644
index 0000000..86dd328
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-load-store.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -O1 -o - %s | FileCheck %s
+
+int val;
+
+struct Pointer {
+ int* ptr;
+private:
+ int private_data;
+};
+
+struct ArrayType {
+ int* array[3];
+private:
+ int private_data;
+};
+
+struct Array {
+ ArrayType array;
+private:
+ int private_data;
+};
+
+struct Struct {
+ Pointer ptr;
+};
+
+// CHECK-LABEL: test_pointer
+Pointer test_pointer(Pointer t) {
+ t.ptr = &val;
+ return t;
+}
+// CHECK: call {{.*}} @llvm.protected.field.ptr.p0{{.*}}
+
+
+
+// CHECK-LABEL: test_struct
+int* test_struct(Struct *t) {
+ return (t->ptr).ptr;
+}
+// CHECK: call {{.*}} @llvm.protected.field.ptr.p0{{.*}}
diff --git a/clang/test/CodeGenCXX/pfp-member-pointer-offsetof.cpp b/clang/test/CodeGenCXX/pfp-member-pointer-offsetof.cpp
new file mode 100644
index 0000000..34009c8
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-member-pointer-offsetof.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -o - %s | FileCheck %s
+
+// CHECK: @__pfp_ds__ZTS1S.ptr1 = hidden alias i8, inttoptr (i64 3573751839 to ptr)
+// CHECK: @__pfp_ds__ZTS1S.ptr2 = hidden alias i8, inttoptr (i64 3573751839 to ptr)
+
+struct [[clang::pointer_field_protection]] S {
+ int *ptr1;
+ int *ptr2;
+};
+
+void f() {
+ &S::ptr1;
+ __builtin_offsetof(S, ptr2);
+}
diff --git a/clang/test/CodeGenCXX/pfp-memcpy.cpp b/clang/test/CodeGenCXX/pfp-memcpy.cpp
new file mode 100644
index 0000000..53e96a1
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-memcpy.cpp
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -triple aarch64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck %s
+
+struct ClassWithTrivialCopy {
+ ClassWithTrivialCopy();
+ ~ClassWithTrivialCopy();
+ void *a;
+private:
+ void *c;
+};
+
+// Make sure that trivial assignments and copies include protected field copies.
+// CHECK-LABEL: define dso_local void @_Z14trivial_assignP20ClassWithTrivialCopyS0_
+void trivial_assign(ClassWithTrivialCopy *s1, ClassWithTrivialCopy *s2) {
+ // CHECK: %0 = load ptr, ptr %s2.addr, align 8
+ // CHECK-NEXT: %1 = load ptr, ptr %s1.addr, align 8
+ // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %1, ptr align 8 %0, i64 16, i1 false)
+ // CHECK-NEXT: %2 = getelementptr inbounds i8, ptr %1, i64 0
+ // CHECK-NEXT: %3 = ptrtoint ptr %1 to i64
+ // CHECK-NEXT: %4 = call ptr @llvm.protected.field.ptr.p0(ptr %2, i64 %3, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.a) ]
+ // CHECK-NEXT: %5 = getelementptr inbounds i8, ptr %0, i64 0
+ // CHECK-NEXT: %6 = ptrtoint ptr %0 to i64
+ // CHECK-NEXT: %7 = call ptr @llvm.protected.field.ptr.p0(ptr %5, i64 %6, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.a) ]
+ // CHECK-NEXT: %8 = load ptr, ptr %7, align 8
+ // CHECK-NEXT: store ptr %8, ptr %4, align 8
+ // CHECK-NEXT: %9 = getelementptr inbounds i8, ptr %1, i64 8
+ // CHECK-NEXT: %10 = ptrtoint ptr %1 to i64
+ // CHECK-NEXT: %11 = call ptr @llvm.protected.field.ptr.p0(ptr %9, i64 %10, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.c) ]
+ // CHECK-NEXT: %12 = getelementptr inbounds i8, ptr %0, i64 8
+ // CHECK-NEXT: %13 = ptrtoint ptr %0 to i64
+ // CHECK-NEXT: %14 = call ptr @llvm.protected.field.ptr.p0(ptr %12, i64 %13, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.c) ]
+ // CHECK-NEXT: %15 = load ptr, ptr %14, align 8
+ // CHECK-NEXT: store ptr %15, ptr %11, align 8
+ *s1 = *s2;
+}
+
+void trivial_copy(ClassWithTrivialCopy *s1) {
+ ClassWithTrivialCopy s2(*s1);
+}
+
+// CHECK-LABEL: define linkonce_odr void @_ZN20ClassWithTrivialCopyC2ERKS_
+// CHECK: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: %a = getelementptr inbounds nuw %struct.ClassWithTrivialCopy, ptr %this1, i32 0, i32 0
+// CHECK-NEXT: %1 = ptrtoint ptr %this1 to i64
+// CHECK-NEXT: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %a, i64 %1, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.a) ]
+// CHECK-NEXT: %3 = load ptr, ptr %.addr, align 8, !nonnull !2, !align !3
+// CHECK-NEXT: %a2 = getelementptr inbounds nuw %struct.ClassWithTrivialCopy, ptr %3, i32 0, i32 0
+// CHECK-NEXT: %4 = ptrtoint ptr %3 to i64
+// CHECK-NEXT: %5 = call ptr @llvm.protected.field.ptr.p0(ptr %a2, i64 %4, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.a) ]
+// CHECK-NEXT: %6 = load ptr, ptr %5, align 8
+// CHECK-NEXT: store ptr %6, ptr %2, align 8
+// CHECK-NEXT: %c = getelementptr inbounds nuw %struct.ClassWithTrivialCopy, ptr %this1, i32 0, i32 1
+// CHECK-NEXT: %7 = ptrtoint ptr %this1 to i64
+// CHECK-NEXT: %8 = call ptr @llvm.protected.field.ptr.p0(ptr %c, i64 %7, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.c) ]
+// CHECK-NEXT: %9 = load ptr, ptr %.addr, align 8, !nonnull !2, !align !3
+// CHECK-NEXT: %c3 = getelementptr inbounds nuw %struct.ClassWithTrivialCopy, ptr %9, i32 0, i32 1
+// CHECK-NEXT: %10 = ptrtoint ptr %9 to i64
+// CHECK-NEXT: %11 = call ptr @llvm.protected.field.ptr.p0(ptr %c3, i64 %10, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS20ClassWithTrivialCopy.c) ]
+// CHECK-NEXT: %12 = load ptr, ptr %11, align 8
+// CHECK-NEXT: store ptr %12, ptr %8, align 8
diff --git a/clang/test/CodeGenCXX/pfp-null-init.cpp b/clang/test/CodeGenCXX/pfp-null-init.cpp
new file mode 100644
index 0000000..f08bedf
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-null-init.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple aarch64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,AARCH64 %s
+// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,X86_64 %s
+
+struct S {
+ void *p;
+private:
+ int private_data;
+};
+
+// CHECK-LABEL: null_init
+void null_init() {
+ // Check that null initialization was correctly applied to the pointer field.
+ // CHECK: %s = alloca %struct.S, align 8
+ // CHECK: call void @llvm.memset.p0.i64(ptr align 8 %s, i8 0, i64 16, i1 false)
+ // CHECK: %0 = getelementptr inbounds i8, ptr %s, i64 0
+ // AARCH64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %0, i64 29832, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.p) ]
+ // X86_64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %0, i64 136, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.p) ]
+ // CHECK: store ptr null, ptr %1, align 8
+ S s{};
+}
+
diff --git a/clang/test/CodeGenCXX/pfp-struct-gep.cpp b/clang/test/CodeGenCXX/pfp-struct-gep.cpp
new file mode 100644
index 0000000..be4e7eb
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-struct-gep.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple aarch64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,AARCH64
+// RUN: %clang_cc1 -triple x86_64-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -emit-llvm -o - %s | FileCheck %s -check-prefixes=CHECK,X86_64
+
+struct S {
+ int* ptr;
+private:
+ int private_data;
+}; // Not Standard-layout, mixed access
+
+// CHECK-LABEL: load_pointers
+int* load_pointers(S *t) {
+ // CHECK: %t.addr = alloca ptr, align 8
+ // CHECK: store ptr %t, ptr %t.addr, align 8
+ // CHECK: %0 = load ptr, ptr %t.addr, align 8
+ // CHECK: %ptr = getelementptr inbounds nuw %struct.S, ptr %0, i32 0, i32 0
+ // AARCH64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %ptr, i64 63261, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ]
+ // X86_64: %1 = call ptr @llvm.protected.field.ptr.p0(ptr %ptr, i64 29, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ]
+ // CHECK: %2 = load ptr, ptr %1, align 8
+ // CHECK: ret ptr %2
+ return t->ptr;
+}
+
+// CHECK-LABEL: store_pointers
+void store_pointers(S* t, int* p) {
+ // CHECK: %t.addr = alloca ptr, align 8
+ // CHECK: %p.addr = alloca ptr, align 8
+ // CHECK: store ptr %t, ptr %t.addr, align 8
+ // CHECK: store ptr %p, ptr %p.addr, align 8
+ // CHECK: %0 = load ptr, ptr %p.addr, align 8
+ // CHECK: %1 = load ptr, ptr %t.addr, align 8
+ // CHECK: %ptr = getelementptr inbounds nuw %struct.S, ptr %1, i32 0, i32 0
+ // AARCH64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %ptr, i64 63261, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ]
+ // X86_64: %2 = call ptr @llvm.protected.field.ptr.p0(ptr %ptr, i64 29, i1 false) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.ptr) ]
+ // CHECK: store ptr %0, ptr %2, align 8
+ t->ptr = p;
+}
diff --git a/clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp b/clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp
new file mode 100644
index 0000000..b14cb11
--- /dev/null
+++ b/clang/test/CodeGenCXX/pfp-trivially-relocatable.cpp
@@ -0,0 +1,101 @@
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -o - %s | FileCheck --check-prefix=RELOC %s
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -o - %s | FileCheck --check-prefix=RELOC %s
+// RUN: %clang_cc1 -std=c++26 -triple aarch64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -o - %s | FileCheck --check-prefix=RELOC %s
+// RUN: %clang_cc1 -std=c++26 -triple aarch64-linux-gnu -emit-llvm -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged -o - %s | FileCheck --check-prefix=NONRELOC %s
+
+typedef __SIZE_TYPE__ size_t;
+
+struct S trivially_relocatable_if_eligible {
+ S(const S&);
+ ~S();
+ int* a;
+private:
+ int* b;
+};
+
+// CHECK: define dso_local void @_Z5test1P1SS0_(
+void test1(S* source, S* dest) {
+ // RELOC: %0 = load ptr, ptr %dest.addr, align 8
+ // RELOC-NEXT: %1 = load ptr, ptr %source.addr, align 8
+ // RELOC-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 8 %1, i64 16, i1 false)
+ // RELOC-NOT: @llvm.protected.field.ptr.p0
+
+ // NONRELOC: %0 = load ptr, ptr %dest.addr, align 8
+ // NONRELOC-NEXT: %1 = load ptr, ptr %source.addr, align 8
+ // NONRELOC-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 8 %1, i64 16, i1 false)
+ // NONRELOC-NEXT: br i1 false, label %loop.end, label %loop
+
+ // NONRELOC: loop:
+ // NONRELOC-NEXT: %2 = phi i64 [ 0, %entry ], [ %19, %loop ]
+ // NONRELOC-NEXT: %3 = getelementptr inbounds i8, ptr %0, i64 %2
+ // NONRELOC-NEXT: %4 = getelementptr inbounds i8, ptr %1, i64 %2
+ // NONRELOC-NEXT: %5 = getelementptr inbounds i8, ptr %3, i64 0
+ // NONRELOC-NEXT: %6 = ptrtoint ptr %3 to i64
+ // NONRELOC-NEXT: %7 = call ptr @llvm.protected.field.ptr.p0(ptr %5, i64 %6, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ]
+ // NONRELOC-NEXT: %8 = getelementptr inbounds i8, ptr %4, i64 0
+ // NONRELOC-NEXT: %9 = ptrtoint ptr %4 to i64
+ // NONRELOC-NEXT: %10 = call ptr @llvm.protected.field.ptr.p0(ptr %8, i64 %9, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ]
+ // NONRELOC-NEXT: %11 = load ptr, ptr %10, align 8
+ // NONRELOC-NEXT: store ptr %11, ptr %7, align 8
+ // NONRELOC-NEXT: %12 = getelementptr inbounds i8, ptr %3, i64 8
+ // NONRELOC-NEXT: %13 = ptrtoint ptr %3 to i64
+ // NONRELOC-NEXT: %14 = call ptr @llvm.protected.field.ptr.p0(ptr %12, i64 %13, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ]
+ // NONRELOC-NEXT: %15 = getelementptr inbounds i8, ptr %4, i64 8
+ // NONRELOC-NEXT: %16 = ptrtoint ptr %4 to i64
+ // NONRELOC-NEXT: %17 = call ptr @llvm.protected.field.ptr.p0(ptr %15, i64 %16, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ]
+ // NONRELOC-NEXT: %18 = load ptr, ptr %17, align 8
+ // NONRELOC-NEXT: store ptr %18, ptr %14, align 8
+ // NONRELOC-NEXT: %19 = add i64 %2, 16
+ // NONRELOC-NEXT: %20 = icmp eq i64 %19, 16
+ // NONRELOC-NEXT: br i1 %20, label %loop.end, label %loop
+
+ // NONRELOC: loop.end:
+ // NONRELOC-NEXT: ret void
+ __builtin_trivially_relocate(dest, source, 1);
+}
+
+// CHECK: define dso_local void @_Z5testNP1SS0_m(
+void testN(S* source, S* dest, size_t count) {
+ // RELOC: %0 = load ptr, ptr %dest.addr, align 8
+ // RELOC-NEXT: %1 = load ptr, ptr %source.addr, align 8
+ // RELOC-NEXT: %2 = load i64, ptr %count.addr, align 8
+ // RELOC-NEXT: %3 = mul i64 %2, 16
+ // RELOC-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 8 %1, i64 %3, i1 false)
+ // RELOC-NOT: @llvm.protected.field.ptr.p0
+
+ // NONRELOC: %0 = load ptr, ptr %dest.addr, align 8
+ // NONRELOC-NEXT: %1 = load ptr, ptr %source.addr, align 8
+ // NONRELOC-NEXT: %2 = load i64, ptr %count.addr, align 8
+ // NONRELOC-NEXT: %3 = mul i64 %2, 16
+ // NONRELOC-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 8 %0, ptr align 8 %1, i64 %3, i1 false)
+ // NONRELOC-NEXT: %4 = icmp eq i64 %3, 0
+ // NONRELOC-NEXT: br i1 %4, label %loop.end, label %loop
+
+ // NONRELOC: loop:
+ // NONRELOC-NEXT: %5 = phi i64 [ 0, %entry ], [ %22, %loop ]
+ // NONRELOC-NEXT: %6 = getelementptr inbounds i8, ptr %0, i64 %5
+ // NONRELOC-NEXT: %7 = getelementptr inbounds i8, ptr %1, i64 %5
+ // NONRELOC-NEXT: %8 = getelementptr inbounds i8, ptr %6, i64 0
+ // NONRELOC-NEXT: %9 = ptrtoint ptr %6 to i64
+ // NONRELOC-NEXT: %10 = call ptr @llvm.protected.field.ptr.p0(ptr %8, i64 %9, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ]
+ // NONRELOC-NEXT: %11 = getelementptr inbounds i8, ptr %7, i64 0
+ // NONRELOC-NEXT: %12 = ptrtoint ptr %7 to i64
+ // NONRELOC-NEXT: %13 = call ptr @llvm.protected.field.ptr.p0(ptr %11, i64 %12, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.a) ]
+ // NONRELOC-NEXT: %14 = load ptr, ptr %13, align 8
+ // NONRELOC-NEXT: store ptr %14, ptr %10, align 8
+ // NONRELOC-NEXT: %15 = getelementptr inbounds i8, ptr %6, i64 8
+ // NONRELOC-NEXT: %16 = ptrtoint ptr %6 to i64
+ // NONRELOC-NEXT: %17 = call ptr @llvm.protected.field.ptr.p0(ptr %15, i64 %16, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ]
+ // NONRELOC-NEXT: %18 = getelementptr inbounds i8, ptr %7, i64 8
+ // NONRELOC-NEXT: %19 = ptrtoint ptr %7 to i64
+ // NONRELOC-NEXT: %20 = call ptr @llvm.protected.field.ptr.p0(ptr %18, i64 %19, i1 true) [ "deactivation-symbol"(ptr @__pfp_ds__ZTS1S.b) ]
+ // NONRELOC-NEXT: %21 = load ptr, ptr %20, align 8
+ // NONRELOC-NEXT: store ptr %21, ptr %17, align 8
+ // NONRELOC-NEXT: %22 = add i64 %5, 16
+ // NONRELOC-NEXT: %23 = icmp eq i64 %22, %3
+ // NONRELOC-NEXT: br i1 %23, label %loop.end, label %loop
+
+ // NONRELOC: loop.end:
+ // NONRELOC-NEXT: ret void
+ __builtin_trivially_relocate(dest, source, count);
+};
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 37ff33e..bf27201 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -118,6 +118,7 @@
// CHECK-NEXT: NoDestroy (SubjectMatchRule_variable)
// CHECK-NEXT: NoDuplicate (SubjectMatchRule_function)
// CHECK-NEXT: NoEscape (SubjectMatchRule_variable_is_parameter)
+// CHECK-NEXT: NoFieldProtection (SubjectMatchRule_field)
// CHECK-NEXT: NoInline (SubjectMatchRule_function)
// CHECK-NEXT: NoInstrumentFunction (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: NoMerge (SubjectMatchRule_function, SubjectMatchRule_variable)
@@ -172,6 +173,7 @@
// CHECK-NEXT: PassObjectSize (SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: PatchableFunctionEntry (SubjectMatchRule_function, SubjectMatchRule_objc_method)
// CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union)
+// CHECK-NEXT: PointerFieldProtection (SubjectMatchRule_record)
// CHECK-NEXT: PreserveNone (SubjectMatchRule_hasType_functionType)
// CHECK-NEXT: RandomizeLayout (SubjectMatchRule_record)
// CHECK-NEXT: ReadOnlyPlacement (SubjectMatchRule_record)
diff --git a/clang/test/Preprocessor/pfp-predefines.c b/clang/test/Preprocessor/pfp-predefines.c
new file mode 100644
index 0000000..f2069a67
--- /dev/null
+++ b/clang/test/Preprocessor/pfp-predefines.c
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 -E -dM -triple aarch64-unknown-linux -fexperimental-pointer-field-protection %s | FileCheck %s --check-prefix=PFP
+// RUN: %clang_cc1 -E -dM -triple aarch64-unknown-linux -fexperimental-pointer-field-protection -fexperimental-pointer-field-protection-tagged %s | FileCheck %s --check-prefixes=PFP,PFP-TAGGED
+
+// PFP-TAGGED: #define __POINTER_FIELD_PROTECTION_TAGGED__ 1
+// PFP: #define __POINTER_FIELD_PROTECTION__ 1