llvm-project/clang/lib/CodeGen/CGNonTrivialStruct.cpp
Oliver Hunt 5b16941f57
[clang] Ensure correct copying of records with authenticated fields (#136783)
When records contain fields with pointer authentication, even simple
copies can require
additional work be performed. This patch contains the core functionality
required to
handle user defined structs, as well as the implicitly constructed
structs for blocks, etc.

Co-authored-by: Ahmed Bougacha
Co-authored-by: Akira Hatanaka
Co-authored-by: John Mccall
2025-04-24 16:22:50 -07:00

1013 lines
42 KiB
C++

//===--- CGNonTrivialStruct.cpp - Emit Special Functions for C Structs ----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines functions to generate various special functions for C
// structs.
//
//===----------------------------------------------------------------------===//
#include "CGDebugInfo.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "clang/AST/NonTrivialTypeVisitor.h"
#include "clang/CodeGen/CodeGenABITypes.h"
#include "llvm/Support/ScopedPrinter.h"
#include <array>
using namespace clang;
using namespace CodeGen;
// Return the size of a field in number of bits.
static uint64_t getFieldSize(const FieldDecl *FD, QualType FT,
ASTContext &Ctx) {
if (FD && FD->isBitField())
return FD->getBitWidthValue();
return Ctx.getTypeSize(FT);
}
namespace {
enum { DstIdx = 0, SrcIdx = 1 };
const char *ValNameStr[2] = {"dst", "src"};
template <class Derived> struct StructVisitor {
StructVisitor(ASTContext &Ctx) : Ctx(Ctx) {}
template <class... Ts>
void visitStructFields(QualType QT, CharUnits CurStructOffset, Ts... Args) {
const RecordDecl *RD = QT->castAs<RecordType>()->getDecl();
// Iterate over the fields of the struct.
for (const FieldDecl *FD : RD->fields()) {
QualType FT = FD->getType();
FT = QT.isVolatileQualified() ? FT.withVolatile() : FT;
asDerived().visit(FT, FD, CurStructOffset, Args...);
}
asDerived().flushTrivialFields(Args...);
}
template <class... Ts> void visitTrivial(Ts... Args) {}
template <class... Ts> void visitCXXDestructor(Ts... Args) {
llvm_unreachable("field of a C++ struct type is not expected");
}
template <class... Ts> void flushTrivialFields(Ts... Args) {}
uint64_t getFieldOffsetInBits(const FieldDecl *FD) {
return FD ? Ctx.getASTRecordLayout(FD->getParent())
.getFieldOffset(FD->getFieldIndex())
: 0;
}
CharUnits getFieldOffset(const FieldDecl *FD) {
return Ctx.toCharUnitsFromBits(getFieldOffsetInBits(FD));
}
Derived &asDerived() { return static_cast<Derived &>(*this); }
ASTContext &getContext() { return Ctx; }
ASTContext &Ctx;
};
template <class Derived, bool IsMove>
struct CopyStructVisitor : StructVisitor<Derived>,
CopiedTypeVisitor<Derived, IsMove> {
using StructVisitor<Derived>::asDerived;
using Super = CopiedTypeVisitor<Derived, IsMove>;
CopyStructVisitor(ASTContext &Ctx) : StructVisitor<Derived>(Ctx) {}
template <class... Ts>
void preVisit(QualType::PrimitiveCopyKind PCK, QualType FT,
const FieldDecl *FD, CharUnits CurStructOffset, Ts &&... Args) {
if (PCK)
asDerived().flushTrivialFields(std::forward<Ts>(Args)...);
}
template <class... Ts>
void visitWithKind(QualType::PrimitiveCopyKind PCK, QualType FT,
const FieldDecl *FD, CharUnits CurStructOffset,
Ts &&... Args) {
if (const auto *AT = asDerived().getContext().getAsArrayType(FT)) {
asDerived().visitArray(PCK, AT, FT.isVolatileQualified(), FD,
CurStructOffset, std::forward<Ts>(Args)...);
return;
}
Super::visitWithKind(PCK, FT, FD, CurStructOffset,
std::forward<Ts>(Args)...);
}
template <class... Ts>
void visitTrivial(QualType FT, const FieldDecl *FD, CharUnits CurStructOffset,
Ts... Args) {
assert(!FT.isVolatileQualified() && "volatile field not expected");
ASTContext &Ctx = asDerived().getContext();
uint64_t FieldSize = getFieldSize(FD, FT, Ctx);
// Ignore zero-sized fields.
if (FieldSize == 0)
return;
uint64_t FStartInBits = asDerived().getFieldOffsetInBits(FD);
uint64_t FEndInBits = FStartInBits + FieldSize;
uint64_t RoundedFEnd = llvm::alignTo(FEndInBits, Ctx.getCharWidth());
// Set Start if this is the first field of a sequence of trivial fields.
if (Start == End)
Start = CurStructOffset + Ctx.toCharUnitsFromBits(FStartInBits);
End = CurStructOffset + Ctx.toCharUnitsFromBits(RoundedFEnd);
}
CharUnits Start = CharUnits::Zero(), End = CharUnits::Zero();
};
// This function creates the mangled name of a special function of a non-trivial
// C struct. Since there is no ODR in C, the function is mangled based on the
// struct contents and not the name. The mangled name has the following
// structure:
//
// <function-name> ::= <prefix> <alignment-info> "_" <struct-field-info>
// <prefix> ::= "__destructor_" | "__default_constructor_" |
// "__copy_constructor_" | "__move_constructor_" |
// "__copy_assignment_" | "__move_assignment_"
// <alignment-info> ::= <dst-alignment> ["_" <src-alignment>]
// <struct-field-info> ::= <field-info>+
// <field-info> ::= <struct-or-scalar-field-info> | <array-field-info>
// <struct-or-scalar-field-info> ::= "_S" <struct-field-info> |
// <strong-field-info> | <trivial-field-info>
// <array-field-info> ::= "_AB" <array-offset> "s" <element-size> "n"
// <num-elements> <innermost-element-info> "_AE"
// <innermost-element-info> ::= <struct-or-scalar-field-info>
// <strong-field-info> ::= "_s" ["b"] ["v"] <field-offset>
// <trivial-field-info> ::= "_t" ["v"] <field-offset> "_" <field-size>
template <class Derived> struct GenFuncNameBase {
std::string getVolatileOffsetStr(bool IsVolatile, CharUnits Offset) {
std::string S;
if (IsVolatile)
S = "v";
S += llvm::to_string(Offset.getQuantity());
return S;
}
void visitARCStrong(QualType FT, const FieldDecl *FD,
CharUnits CurStructOffset) {
appendStr("_s");
if (FT->isBlockPointerType())
appendStr("b");
CharUnits FieldOffset = CurStructOffset + asDerived().getFieldOffset(FD);
appendStr(getVolatileOffsetStr(FT.isVolatileQualified(), FieldOffset));
}
void visitARCWeak(QualType FT, const FieldDecl *FD,
CharUnits CurStructOffset) {
appendStr("_w");
CharUnits FieldOffset = CurStructOffset + asDerived().getFieldOffset(FD);
appendStr(getVolatileOffsetStr(FT.isVolatileQualified(), FieldOffset));
}
void visitStruct(QualType QT, const FieldDecl *FD,
CharUnits CurStructOffset) {
CharUnits FieldOffset = CurStructOffset + asDerived().getFieldOffset(FD);
appendStr("_S");
asDerived().visitStructFields(QT, FieldOffset);
}
template <class FieldKind>
void visitArray(FieldKind FK, const ArrayType *AT, bool IsVolatile,
const FieldDecl *FD, CharUnits CurStructOffset) {
// String for non-volatile trivial fields is emitted when
// flushTrivialFields is called.
if (!FK)
return asDerived().visitTrivial(QualType(AT, 0), FD, CurStructOffset);
asDerived().flushTrivialFields();
CharUnits FieldOffset = CurStructOffset + asDerived().getFieldOffset(FD);
ASTContext &Ctx = asDerived().getContext();
const ConstantArrayType *CAT = cast<ConstantArrayType>(AT);
unsigned NumElts = Ctx.getConstantArrayElementCount(CAT);
QualType EltTy = Ctx.getBaseElementType(CAT);
CharUnits EltSize = Ctx.getTypeSizeInChars(EltTy);
appendStr("_AB" + llvm::to_string(FieldOffset.getQuantity()) + "s" +
llvm::to_string(EltSize.getQuantity()) + "n" +
llvm::to_string(NumElts));
EltTy = IsVolatile ? EltTy.withVolatile() : EltTy;
asDerived().visitWithKind(FK, EltTy, nullptr, FieldOffset);
appendStr("_AE");
}
void appendStr(StringRef Str) { Name += Str; }
std::string getName(QualType QT, bool IsVolatile) {
QT = IsVolatile ? QT.withVolatile() : QT;
asDerived().visitStructFields(QT, CharUnits::Zero());
return Name;
}
Derived &asDerived() { return static_cast<Derived &>(*this); }
std::string Name;
};
template <class Derived>
struct GenUnaryFuncName : StructVisitor<Derived>, GenFuncNameBase<Derived> {
GenUnaryFuncName(StringRef Prefix, CharUnits DstAlignment, ASTContext &Ctx)
: StructVisitor<Derived>(Ctx) {
this->appendStr(Prefix);
this->appendStr(llvm::to_string(DstAlignment.getQuantity()));
}
};
// Helper function to create a null constant.
static llvm::Constant *getNullForVariable(Address Addr) {
llvm::Type *Ty = Addr.getElementType();
return llvm::ConstantPointerNull::get(cast<llvm::PointerType>(Ty));
}
template <bool IsMove>
struct GenBinaryFuncName : CopyStructVisitor<GenBinaryFuncName<IsMove>, IsMove>,
GenFuncNameBase<GenBinaryFuncName<IsMove>> {
GenBinaryFuncName(StringRef Prefix, CharUnits DstAlignment,
CharUnits SrcAlignment, ASTContext &Ctx)
: CopyStructVisitor<GenBinaryFuncName<IsMove>, IsMove>(Ctx) {
this->appendStr(Prefix);
this->appendStr(llvm::to_string(DstAlignment.getQuantity()));
this->appendStr("_" + llvm::to_string(SrcAlignment.getQuantity()));
}
void flushTrivialFields() {
if (this->Start == this->End)
return;
this->appendStr("_t" + llvm::to_string(this->Start.getQuantity()) + "w" +
llvm::to_string((this->End - this->Start).getQuantity()));
this->Start = this->End = CharUnits::Zero();
}
void visitVolatileTrivial(QualType FT, const FieldDecl *FD,
CharUnits CurStructOffset) {
// Zero-length bit-fields don't need to be copied/assigned.
if (FD && FD->isZeroLengthBitField())
return;
// Because volatile fields can be bit-fields and are individually copied,
// their offset and width are in bits.
uint64_t OffsetInBits =
this->Ctx.toBits(CurStructOffset) + this->getFieldOffsetInBits(FD);
this->appendStr("_tv" + llvm::to_string(OffsetInBits) + "w" +
llvm::to_string(getFieldSize(FD, FT, this->Ctx)));
}
void visitPtrAuth(QualType FT, const FieldDecl *FD,
CharUnits CurStructOffset) {
this->appendStr("_pa");
PointerAuthQualifier PtrAuth = FT.getPointerAuth().withoutKeyNone();
this->appendStr(llvm::to_string(PtrAuth.getKey()) + "_");
this->appendStr(llvm::to_string(PtrAuth.getExtraDiscriminator()) + "_");
if (PtrAuth.authenticatesNullValues())
this->appendStr("anv_");
CharUnits FieldOffset = CurStructOffset + this->getFieldOffset(FD);
this->appendStr(llvm::to_string(FieldOffset.getQuantity()));
}
};
struct GenDefaultInitializeFuncName
: GenUnaryFuncName<GenDefaultInitializeFuncName>,
DefaultInitializedTypeVisitor<GenDefaultInitializeFuncName> {
using Super = DefaultInitializedTypeVisitor<GenDefaultInitializeFuncName>;
GenDefaultInitializeFuncName(CharUnits DstAlignment, ASTContext &Ctx)
: GenUnaryFuncName<GenDefaultInitializeFuncName>("__default_constructor_",
DstAlignment, Ctx) {}
void visitWithKind(QualType::PrimitiveDefaultInitializeKind PDIK, QualType FT,
const FieldDecl *FD, CharUnits CurStructOffset) {
if (const auto *AT = getContext().getAsArrayType(FT)) {
visitArray(PDIK, AT, FT.isVolatileQualified(), FD, CurStructOffset);
return;
}
Super::visitWithKind(PDIK, FT, FD, CurStructOffset);
}
};
struct GenDestructorFuncName : GenUnaryFuncName<GenDestructorFuncName>,
DestructedTypeVisitor<GenDestructorFuncName> {
using Super = DestructedTypeVisitor<GenDestructorFuncName>;
GenDestructorFuncName(const char *Prefix, CharUnits DstAlignment,
ASTContext &Ctx)
: GenUnaryFuncName<GenDestructorFuncName>(Prefix, DstAlignment, Ctx) {}
void visitWithKind(QualType::DestructionKind DK, QualType FT,
const FieldDecl *FD, CharUnits CurStructOffset) {
if (const auto *AT = getContext().getAsArrayType(FT)) {
visitArray(DK, AT, FT.isVolatileQualified(), FD, CurStructOffset);
return;
}
Super::visitWithKind(DK, FT, FD, CurStructOffset);
}
};
// Helper function that creates CGFunctionInfo for an N-ary special function.
template <size_t N>
static const CGFunctionInfo &getFunctionInfo(CodeGenModule &CGM,
FunctionArgList &Args) {
ASTContext &Ctx = CGM.getContext();
llvm::SmallVector<ImplicitParamDecl *, N> Params;
QualType ParamTy = Ctx.getPointerType(Ctx.VoidPtrTy);
for (unsigned I = 0; I < N; ++I)
Params.push_back(ImplicitParamDecl::Create(
Ctx, nullptr, SourceLocation(), &Ctx.Idents.get(ValNameStr[I]), ParamTy,
ImplicitParamKind::Other));
llvm::append_range(Args, Params);
return CGM.getTypes().arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Args);
}
template <size_t N, size_t... Ints>
static std::array<Address, N> getParamAddrs(std::index_sequence<Ints...> IntSeq,
std::array<CharUnits, N> Alignments,
const FunctionArgList &Args,
CodeGenFunction *CGF) {
return std::array<Address, N>{
{Address(CGF->Builder.CreateLoad(CGF->GetAddrOfLocalVar(Args[Ints])),
CGF->VoidPtrTy, Alignments[Ints], KnownNonNull)...}};
}
// Template classes that are used as bases for classes that emit special
// functions.
template <class Derived> struct GenFuncBase {
template <size_t N>
void visitStruct(QualType FT, const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, N> Addrs) {
this->asDerived().callSpecialFunction(
FT, CurStructOffset + asDerived().getFieldOffset(FD), Addrs);
}
template <class FieldKind, size_t N>
void visitArray(FieldKind FK, const ArrayType *AT, bool IsVolatile,
const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, N> Addrs) {
// Non-volatile trivial fields are copied when flushTrivialFields is called.
if (!FK)
return asDerived().visitTrivial(QualType(AT, 0), FD, CurStructOffset,
Addrs);
asDerived().flushTrivialFields(Addrs);
CodeGenFunction &CGF = *this->CGF;
ASTContext &Ctx = CGF.getContext();
// Compute the end address.
QualType BaseEltQT;
std::array<Address, N> StartAddrs = Addrs;
for (unsigned I = 0; I < N; ++I)
StartAddrs[I] = getAddrWithOffset(Addrs[I], CurStructOffset, FD);
Address DstAddr = StartAddrs[DstIdx];
llvm::Value *NumElts = CGF.emitArrayLength(AT, BaseEltQT, DstAddr);
unsigned BaseEltSize = Ctx.getTypeSizeInChars(BaseEltQT).getQuantity();
llvm::Value *BaseEltSizeVal =
llvm::ConstantInt::get(NumElts->getType(), BaseEltSize);
llvm::Value *SizeInBytes =
CGF.Builder.CreateNUWMul(BaseEltSizeVal, NumElts);
llvm::Value *DstArrayEnd = CGF.Builder.CreateInBoundsGEP(
CGF.Int8Ty, DstAddr.emitRawPointer(CGF), SizeInBytes);
llvm::BasicBlock *PreheaderBB = CGF.Builder.GetInsertBlock();
// Create the header block and insert the phi instructions.
llvm::BasicBlock *HeaderBB = CGF.createBasicBlock("loop.header");
CGF.EmitBlock(HeaderBB);
llvm::PHINode *PHIs[N];
for (unsigned I = 0; I < N; ++I) {
PHIs[I] = CGF.Builder.CreatePHI(CGF.CGM.Int8PtrPtrTy, 2, "addr.cur");
PHIs[I]->addIncoming(StartAddrs[I].emitRawPointer(CGF), PreheaderBB);
}
// Create the exit and loop body blocks.
llvm::BasicBlock *ExitBB = CGF.createBasicBlock("loop.exit");
llvm::BasicBlock *LoopBB = CGF.createBasicBlock("loop.body");
// Emit the comparison and conditional branch instruction that jumps to
// either the exit or the loop body.
llvm::Value *Done =
CGF.Builder.CreateICmpEQ(PHIs[DstIdx], DstArrayEnd, "done");
CGF.Builder.CreateCondBr(Done, ExitBB, LoopBB);
// Visit the element of the array in the loop body.
CGF.EmitBlock(LoopBB);
QualType EltQT = AT->getElementType();
CharUnits EltSize = Ctx.getTypeSizeInChars(EltQT);
std::array<Address, N> NewAddrs = Addrs;
for (unsigned I = 0; I < N; ++I)
NewAddrs[I] =
Address(PHIs[I], CGF.Int8PtrTy,
StartAddrs[I].getAlignment().alignmentAtOffset(EltSize));
EltQT = IsVolatile ? EltQT.withVolatile() : EltQT;
this->asDerived().visitWithKind(FK, EltQT, nullptr, CharUnits::Zero(),
NewAddrs);
LoopBB = CGF.Builder.GetInsertBlock();
for (unsigned I = 0; I < N; ++I) {
// Instrs to update the destination and source addresses.
// Update phi instructions.
NewAddrs[I] = getAddrWithOffset(NewAddrs[I], EltSize);
PHIs[I]->addIncoming(NewAddrs[I].emitRawPointer(CGF), LoopBB);
}
// Insert an unconditional branch to the header block.
CGF.Builder.CreateBr(HeaderBB);
CGF.EmitBlock(ExitBB);
}
/// Return an address with the specified offset from the passed address.
Address getAddrWithOffset(Address Addr, CharUnits Offset) {
assert(Addr.isValid() && "invalid address");
if (Offset.getQuantity() == 0)
return Addr;
Addr = Addr.withElementType(CGF->CGM.Int8Ty);
Addr = CGF->Builder.CreateConstInBoundsGEP(Addr, Offset.getQuantity());
return Addr.withElementType(CGF->CGM.Int8PtrTy);
}
Address getAddrWithOffset(Address Addr, CharUnits StructFieldOffset,
const FieldDecl *FD) {
return getAddrWithOffset(Addr, StructFieldOffset +
asDerived().getFieldOffset(FD));
}
template <size_t N>
llvm::Function *getFunction(StringRef FuncName, QualType QT,
std::array<CharUnits, N> Alignments,
CodeGenModule &CGM) {
// If the special function already exists in the module, return it.
if (llvm::Function *F = CGM.getModule().getFunction(FuncName)) {
bool WrongType = false;
if (!F->getReturnType()->isVoidTy())
WrongType = true;
else {
for (const llvm::Argument &Arg : F->args())
if (Arg.getType() != CGM.Int8PtrPtrTy)
WrongType = true;
}
if (WrongType) {
std::string FuncName = std::string(F->getName());
SourceLocation Loc = QT->castAs<RecordType>()->getDecl()->getLocation();
CGM.Error(Loc, "special function " + FuncName +
" for non-trivial C struct has incorrect type");
return nullptr;
}
return F;
}
ASTContext &Ctx = CGM.getContext();
FunctionArgList Args;
const CGFunctionInfo &FI = getFunctionInfo<N>(CGM, Args);
llvm::FunctionType *FuncTy = CGM.getTypes().GetFunctionType(FI);
llvm::Function *F =
llvm::Function::Create(FuncTy, llvm::GlobalValue::LinkOnceODRLinkage,
FuncName, &CGM.getModule());
F->setVisibility(llvm::GlobalValue::HiddenVisibility);
CGM.SetLLVMFunctionAttributes(GlobalDecl(), FI, F, /*IsThunk=*/false);
CGM.SetLLVMFunctionAttributesForDefinition(nullptr, F);
CodeGenFunction NewCGF(CGM);
setCGF(&NewCGF);
CGF->StartFunction(GlobalDecl(), Ctx.VoidTy, F, FI, Args);
auto AL = ApplyDebugLocation::CreateArtificial(*CGF);
std::array<Address, N> Addrs =
getParamAddrs<N>(std::make_index_sequence<N>{}, Alignments, Args, CGF);
asDerived().visitStructFields(QT, CharUnits::Zero(), Addrs);
CGF->FinishFunction();
return F;
}
template <size_t N>
void callFunc(StringRef FuncName, QualType QT, std::array<Address, N> Addrs,
CodeGenFunction &CallerCGF) {
std::array<CharUnits, N> Alignments;
llvm::Value *Ptrs[N];
for (unsigned I = 0; I < N; ++I) {
Alignments[I] = Addrs[I].getAlignment();
Ptrs[I] = Addrs[I].emitRawPointer(CallerCGF);
}
if (llvm::Function *F =
getFunction(FuncName, QT, Alignments, CallerCGF.CGM))
CallerCGF.EmitNounwindRuntimeCall(F, Ptrs);
}
Derived &asDerived() { return static_cast<Derived &>(*this); }
void setCGF(CodeGenFunction *F) { CGF = F; }
CodeGenFunction *CGF = nullptr;
};
template <class Derived, bool IsMove>
struct GenBinaryFunc : CopyStructVisitor<Derived, IsMove>,
GenFuncBase<Derived> {
GenBinaryFunc(ASTContext &Ctx) : CopyStructVisitor<Derived, IsMove>(Ctx) {}
void flushTrivialFields(std::array<Address, 2> Addrs) {
CharUnits Size = this->End - this->Start;
if (Size.getQuantity() == 0)
return;
Address DstAddr = this->getAddrWithOffset(Addrs[DstIdx], this->Start);
Address SrcAddr = this->getAddrWithOffset(Addrs[SrcIdx], this->Start);
// Emit memcpy.
if (Size.getQuantity() >= 16 ||
!llvm::has_single_bit<uint32_t>(Size.getQuantity())) {
llvm::Value *SizeVal =
llvm::ConstantInt::get(this->CGF->SizeTy, Size.getQuantity());
DstAddr = DstAddr.withElementType(this->CGF->Int8Ty);
SrcAddr = SrcAddr.withElementType(this->CGF->Int8Ty);
this->CGF->Builder.CreateMemCpy(DstAddr, SrcAddr, SizeVal, false);
} else {
llvm::Type *Ty = llvm::Type::getIntNTy(
this->CGF->getLLVMContext(),
Size.getQuantity() * this->CGF->getContext().getCharWidth());
DstAddr = DstAddr.withElementType(Ty);
SrcAddr = SrcAddr.withElementType(Ty);
llvm::Value *SrcVal = this->CGF->Builder.CreateLoad(SrcAddr, false);
this->CGF->Builder.CreateStore(SrcVal, DstAddr, false);
}
this->Start = this->End = CharUnits::Zero();
}
template <class... Ts>
void visitVolatileTrivial(QualType FT, const FieldDecl *FD, CharUnits Offset,
std::array<Address, 2> Addrs) {
LValue DstLV, SrcLV;
if (FD) {
// No need to copy zero-length bit-fields.
if (FD->isZeroLengthBitField())
return;
QualType RT = QualType(FD->getParent()->getTypeForDecl(), 0);
llvm::Type *Ty = this->CGF->ConvertType(RT);
Address DstAddr = this->getAddrWithOffset(Addrs[DstIdx], Offset);
LValue DstBase =
this->CGF->MakeAddrLValue(DstAddr.withElementType(Ty), FT);
DstLV = this->CGF->EmitLValueForField(DstBase, FD);
Address SrcAddr = this->getAddrWithOffset(Addrs[SrcIdx], Offset);
LValue SrcBase =
this->CGF->MakeAddrLValue(SrcAddr.withElementType(Ty), FT);
SrcLV = this->CGF->EmitLValueForField(SrcBase, FD);
} else {
llvm::Type *Ty = this->CGF->ConvertTypeForMem(FT);
Address DstAddr = Addrs[DstIdx].withElementType(Ty);
Address SrcAddr = Addrs[SrcIdx].withElementType(Ty);
DstLV = this->CGF->MakeAddrLValue(DstAddr, FT);
SrcLV = this->CGF->MakeAddrLValue(SrcAddr, FT);
}
RValue SrcVal = this->CGF->EmitLoadOfLValue(SrcLV, SourceLocation());
this->CGF->EmitStoreThroughLValue(SrcVal, DstLV);
}
void visitPtrAuth(QualType FT, const FieldDecl *FD, CharUnits CurStackOffset,
std::array<Address, 2> Addrs) {
PointerAuthQualifier PtrAuth = FT.getPointerAuth().withoutKeyNone();
Addrs[DstIdx] = this->getAddrWithOffset(Addrs[DstIdx], CurStackOffset, FD);
Addrs[SrcIdx] = this->getAddrWithOffset(Addrs[SrcIdx], CurStackOffset, FD);
this->CGF->EmitPointerAuthCopy(PtrAuth, FT, Addrs[DstIdx], Addrs[SrcIdx]);
}
};
// These classes that emit the special functions for a non-trivial struct.
struct GenDestructor : StructVisitor<GenDestructor>,
GenFuncBase<GenDestructor>,
DestructedTypeVisitor<GenDestructor> {
using Super = DestructedTypeVisitor<GenDestructor>;
GenDestructor(ASTContext &Ctx) : StructVisitor<GenDestructor>(Ctx) {}
void visitWithKind(QualType::DestructionKind DK, QualType FT,
const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, 1> Addrs) {
if (const auto *AT = getContext().getAsArrayType(FT)) {
visitArray(DK, AT, FT.isVolatileQualified(), FD, CurStructOffset, Addrs);
return;
}
Super::visitWithKind(DK, FT, FD, CurStructOffset, Addrs);
}
void visitARCStrong(QualType QT, const FieldDecl *FD,
CharUnits CurStructOffset, std::array<Address, 1> Addrs) {
CGF->destroyARCStrongImprecise(
*CGF, getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD), QT);
}
void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, 1> Addrs) {
CGF->destroyARCWeak(
*CGF, getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD), QT);
}
void callSpecialFunction(QualType FT, CharUnits Offset,
std::array<Address, 1> Addrs) {
CGF->callCStructDestructor(
CGF->MakeAddrLValue(getAddrWithOffset(Addrs[DstIdx], Offset), FT));
}
};
struct GenDefaultInitialize
: StructVisitor<GenDefaultInitialize>,
GenFuncBase<GenDefaultInitialize>,
DefaultInitializedTypeVisitor<GenDefaultInitialize> {
using Super = DefaultInitializedTypeVisitor<GenDefaultInitialize>;
typedef GenFuncBase<GenDefaultInitialize> GenFuncBaseTy;
GenDefaultInitialize(ASTContext &Ctx)
: StructVisitor<GenDefaultInitialize>(Ctx) {}
void visitWithKind(QualType::PrimitiveDefaultInitializeKind PDIK, QualType FT,
const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, 1> Addrs) {
if (const auto *AT = getContext().getAsArrayType(FT)) {
visitArray(PDIK, AT, FT.isVolatileQualified(), FD, CurStructOffset,
Addrs);
return;
}
Super::visitWithKind(PDIK, FT, FD, CurStructOffset, Addrs);
}
void visitARCStrong(QualType QT, const FieldDecl *FD,
CharUnits CurStructOffset, std::array<Address, 1> Addrs) {
CGF->EmitNullInitialization(
getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD), QT);
}
void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, 1> Addrs) {
CGF->EmitNullInitialization(
getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD), QT);
}
template <class FieldKind, size_t... Is>
void visitArray(FieldKind FK, const ArrayType *AT, bool IsVolatile,
const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, 1> Addrs) {
if (!FK)
return visitTrivial(QualType(AT, 0), FD, CurStructOffset, Addrs);
ASTContext &Ctx = getContext();
CharUnits Size = Ctx.getTypeSizeInChars(QualType(AT, 0));
QualType EltTy = Ctx.getBaseElementType(QualType(AT, 0));
if (Size < CharUnits::fromQuantity(16) || EltTy->getAs<RecordType>()) {
GenFuncBaseTy::visitArray(FK, AT, IsVolatile, FD, CurStructOffset, Addrs);
return;
}
llvm::Constant *SizeVal = CGF->Builder.getInt64(Size.getQuantity());
Address DstAddr = getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD);
Address Loc = DstAddr.withElementType(CGF->Int8Ty);
CGF->Builder.CreateMemSet(Loc, CGF->Builder.getInt8(0), SizeVal,
IsVolatile);
}
void callSpecialFunction(QualType FT, CharUnits Offset,
std::array<Address, 1> Addrs) {
CGF->callCStructDefaultConstructor(
CGF->MakeAddrLValue(getAddrWithOffset(Addrs[DstIdx], Offset), FT));
}
};
struct GenCopyConstructor : GenBinaryFunc<GenCopyConstructor, false> {
GenCopyConstructor(ASTContext &Ctx)
: GenBinaryFunc<GenCopyConstructor, false>(Ctx) {}
void visitARCStrong(QualType QT, const FieldDecl *FD,
CharUnits CurStructOffset, std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStructOffset, FD);
llvm::Value *SrcVal = CGF->EmitLoadOfScalar(
Addrs[SrcIdx], QT.isVolatileQualified(), QT, SourceLocation());
llvm::Value *Val = CGF->EmitARCRetain(QT, SrcVal);
CGF->EmitStoreOfScalar(Val, CGF->MakeAddrLValue(Addrs[DstIdx], QT), true);
}
void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStructOffset, FD);
CGF->EmitARCCopyWeak(Addrs[DstIdx], Addrs[SrcIdx]);
}
void callSpecialFunction(QualType FT, CharUnits Offset,
std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], Offset);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], Offset);
CGF->callCStructCopyConstructor(CGF->MakeAddrLValue(Addrs[DstIdx], FT),
CGF->MakeAddrLValue(Addrs[SrcIdx], FT));
}
};
struct GenMoveConstructor : GenBinaryFunc<GenMoveConstructor, true> {
GenMoveConstructor(ASTContext &Ctx)
: GenBinaryFunc<GenMoveConstructor, true>(Ctx) {}
void visitARCStrong(QualType QT, const FieldDecl *FD,
CharUnits CurStructOffset, std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStructOffset, FD);
LValue SrcLV = CGF->MakeAddrLValue(Addrs[SrcIdx], QT);
llvm::Value *SrcVal =
CGF->EmitLoadOfLValue(SrcLV, SourceLocation()).getScalarVal();
CGF->EmitStoreOfScalar(getNullForVariable(SrcLV.getAddress()), SrcLV);
CGF->EmitStoreOfScalar(SrcVal, CGF->MakeAddrLValue(Addrs[DstIdx], QT),
/* isInitialization */ true);
}
void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStructOffset, FD);
CGF->EmitARCMoveWeak(Addrs[DstIdx], Addrs[SrcIdx]);
}
void callSpecialFunction(QualType FT, CharUnits Offset,
std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], Offset);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], Offset);
CGF->callCStructMoveConstructor(CGF->MakeAddrLValue(Addrs[DstIdx], FT),
CGF->MakeAddrLValue(Addrs[SrcIdx], FT));
}
};
struct GenCopyAssignment : GenBinaryFunc<GenCopyAssignment, false> {
GenCopyAssignment(ASTContext &Ctx)
: GenBinaryFunc<GenCopyAssignment, false>(Ctx) {}
void visitARCStrong(QualType QT, const FieldDecl *FD,
CharUnits CurStructOffset, std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStructOffset, FD);
llvm::Value *SrcVal = CGF->EmitLoadOfScalar(
Addrs[SrcIdx], QT.isVolatileQualified(), QT, SourceLocation());
CGF->EmitARCStoreStrong(CGF->MakeAddrLValue(Addrs[DstIdx], QT), SrcVal,
false);
}
void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStructOffset, FD);
CGF->emitARCCopyAssignWeak(QT, Addrs[DstIdx], Addrs[SrcIdx]);
}
void callSpecialFunction(QualType FT, CharUnits Offset,
std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], Offset);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], Offset);
CGF->callCStructCopyAssignmentOperator(
CGF->MakeAddrLValue(Addrs[DstIdx], FT),
CGF->MakeAddrLValue(Addrs[SrcIdx], FT));
}
};
struct GenMoveAssignment : GenBinaryFunc<GenMoveAssignment, true> {
GenMoveAssignment(ASTContext &Ctx)
: GenBinaryFunc<GenMoveAssignment, true>(Ctx) {}
void visitARCStrong(QualType QT, const FieldDecl *FD,
CharUnits CurStructOffset, std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStructOffset, FD);
LValue SrcLV = CGF->MakeAddrLValue(Addrs[SrcIdx], QT);
llvm::Value *SrcVal =
CGF->EmitLoadOfLValue(SrcLV, SourceLocation()).getScalarVal();
CGF->EmitStoreOfScalar(getNullForVariable(SrcLV.getAddress()), SrcLV);
LValue DstLV = CGF->MakeAddrLValue(Addrs[DstIdx], QT);
llvm::Value *DstVal =
CGF->EmitLoadOfLValue(DstLV, SourceLocation()).getScalarVal();
CGF->EmitStoreOfScalar(SrcVal, DstLV);
CGF->EmitARCRelease(DstVal, ARCImpreciseLifetime);
}
void visitARCWeak(QualType QT, const FieldDecl *FD, CharUnits CurStructOffset,
std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], CurStructOffset, FD);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], CurStructOffset, FD);
CGF->emitARCMoveAssignWeak(QT, Addrs[DstIdx], Addrs[SrcIdx]);
}
void callSpecialFunction(QualType FT, CharUnits Offset,
std::array<Address, 2> Addrs) {
Addrs[DstIdx] = getAddrWithOffset(Addrs[DstIdx], Offset);
Addrs[SrcIdx] = getAddrWithOffset(Addrs[SrcIdx], Offset);
CGF->callCStructMoveAssignmentOperator(
CGF->MakeAddrLValue(Addrs[DstIdx], FT),
CGF->MakeAddrLValue(Addrs[SrcIdx], FT));
}
};
} // namespace
void CodeGenFunction::destroyNonTrivialCStruct(CodeGenFunction &CGF,
Address Addr, QualType Type) {
CGF.callCStructDestructor(CGF.MakeAddrLValue(Addr, Type));
}
// Default-initialize a variable that is a non-trivial struct or an array of
// such structure.
void CodeGenFunction::defaultInitNonTrivialCStructVar(LValue Dst) {
GenDefaultInitialize Gen(getContext());
Address DstPtr = Dst.getAddress().withElementType(CGM.Int8PtrTy);
Gen.setCGF(this);
QualType QT = Dst.getType();
QT = Dst.isVolatile() ? QT.withVolatile() : QT;
Gen.visit(QT, nullptr, CharUnits::Zero(), std::array<Address, 1>({{DstPtr}}));
}
template <class G, size_t N>
static void callSpecialFunction(G &&Gen, StringRef FuncName, QualType QT,
bool IsVolatile, CodeGenFunction &CGF,
std::array<Address, N> Addrs) {
auto SetArtificialLoc = ApplyDebugLocation::CreateArtificial(CGF);
for (unsigned I = 0; I < N; ++I)
Addrs[I] = Addrs[I].withElementType(CGF.CGM.Int8PtrTy);
QT = IsVolatile ? QT.withVolatile() : QT;
Gen.callFunc(FuncName, QT, Addrs, CGF);
}
template <class G, size_t N>
static llvm::Function *
getSpecialFunction(G &&Gen, StringRef FuncName, QualType QT, bool IsVolatile,
std::array<CharUnits, N> Alignments, CodeGenModule &CGM) {
QT = IsVolatile ? QT.withVolatile() : QT;
// The following call requires an array of addresses as arguments, but doesn't
// actually use them (it overwrites them with the addresses of the arguments
// of the created function).
return Gen.getFunction(FuncName, QT, Alignments, CGM);
}
// Functions to emit calls to the special functions of a non-trivial C struct.
void CodeGenFunction::callCStructDefaultConstructor(LValue Dst) {
bool IsVolatile = Dst.isVolatile();
Address DstPtr = Dst.getAddress();
QualType QT = Dst.getType();
GenDefaultInitializeFuncName GenName(DstPtr.getAlignment(), getContext());
std::string FuncName = GenName.getName(QT, IsVolatile);
callSpecialFunction(GenDefaultInitialize(getContext()), FuncName, QT,
IsVolatile, *this, std::array<Address, 1>({{DstPtr}}));
}
std::string CodeGenFunction::getNonTrivialCopyConstructorStr(
QualType QT, CharUnits Alignment, bool IsVolatile, ASTContext &Ctx) {
GenBinaryFuncName<false> GenName("", Alignment, Alignment, Ctx);
return GenName.getName(QT, IsVolatile);
}
std::string CodeGenFunction::getNonTrivialDestructorStr(QualType QT,
CharUnits Alignment,
bool IsVolatile,
ASTContext &Ctx) {
GenDestructorFuncName GenName("", Alignment, Ctx);
return GenName.getName(QT, IsVolatile);
}
void CodeGenFunction::callCStructDestructor(LValue Dst) {
bool IsVolatile = Dst.isVolatile();
Address DstPtr = Dst.getAddress();
QualType QT = Dst.getType();
GenDestructorFuncName GenName("__destructor_", DstPtr.getAlignment(),
getContext());
std::string FuncName = GenName.getName(QT, IsVolatile);
callSpecialFunction(GenDestructor(getContext()), FuncName, QT, IsVolatile,
*this, std::array<Address, 1>({{DstPtr}}));
}
void CodeGenFunction::callCStructCopyConstructor(LValue Dst, LValue Src) {
bool IsVolatile = Dst.isVolatile() || Src.isVolatile();
Address DstPtr = Dst.getAddress(), SrcPtr = Src.getAddress();
QualType QT = Dst.getType();
GenBinaryFuncName<false> GenName("__copy_constructor_", DstPtr.getAlignment(),
SrcPtr.getAlignment(), getContext());
std::string FuncName = GenName.getName(QT, IsVolatile);
callSpecialFunction(GenCopyConstructor(getContext()), FuncName, QT,
IsVolatile, *this,
std::array<Address, 2>({{DstPtr, SrcPtr}}));
}
void CodeGenFunction::callCStructCopyAssignmentOperator(LValue Dst, LValue Src
) {
bool IsVolatile = Dst.isVolatile() || Src.isVolatile();
Address DstPtr = Dst.getAddress(), SrcPtr = Src.getAddress();
QualType QT = Dst.getType();
GenBinaryFuncName<false> GenName("__copy_assignment_", DstPtr.getAlignment(),
SrcPtr.getAlignment(), getContext());
std::string FuncName = GenName.getName(QT, IsVolatile);
callSpecialFunction(GenCopyAssignment(getContext()), FuncName, QT, IsVolatile,
*this, std::array<Address, 2>({{DstPtr, SrcPtr}}));
}
void CodeGenFunction::callCStructMoveConstructor(LValue Dst, LValue Src) {
bool IsVolatile = Dst.isVolatile() || Src.isVolatile();
Address DstPtr = Dst.getAddress(), SrcPtr = Src.getAddress();
QualType QT = Dst.getType();
GenBinaryFuncName<true> GenName("__move_constructor_", DstPtr.getAlignment(),
SrcPtr.getAlignment(), getContext());
std::string FuncName = GenName.getName(QT, IsVolatile);
callSpecialFunction(GenMoveConstructor(getContext()), FuncName, QT,
IsVolatile, *this,
std::array<Address, 2>({{DstPtr, SrcPtr}}));
}
void CodeGenFunction::callCStructMoveAssignmentOperator(LValue Dst, LValue Src
) {
bool IsVolatile = Dst.isVolatile() || Src.isVolatile();
Address DstPtr = Dst.getAddress(), SrcPtr = Src.getAddress();
QualType QT = Dst.getType();
GenBinaryFuncName<true> GenName("__move_assignment_", DstPtr.getAlignment(),
SrcPtr.getAlignment(), getContext());
std::string FuncName = GenName.getName(QT, IsVolatile);
callSpecialFunction(GenMoveAssignment(getContext()), FuncName, QT, IsVolatile,
*this, std::array<Address, 2>({{DstPtr, SrcPtr}}));
}
llvm::Function *clang::CodeGen::getNonTrivialCStructDefaultConstructor(
CodeGenModule &CGM, CharUnits DstAlignment, bool IsVolatile, QualType QT) {
ASTContext &Ctx = CGM.getContext();
GenDefaultInitializeFuncName GenName(DstAlignment, Ctx);
std::string FuncName = GenName.getName(QT, IsVolatile);
return getSpecialFunction(GenDefaultInitialize(Ctx), FuncName, QT, IsVolatile,
std::array<CharUnits, 1>({{DstAlignment}}), CGM);
}
llvm::Function *clang::CodeGen::getNonTrivialCStructCopyConstructor(
CodeGenModule &CGM, CharUnits DstAlignment, CharUnits SrcAlignment,
bool IsVolatile, QualType QT) {
ASTContext &Ctx = CGM.getContext();
GenBinaryFuncName<false> GenName("__copy_constructor_", DstAlignment,
SrcAlignment, Ctx);
std::string FuncName = GenName.getName(QT, IsVolatile);
return getSpecialFunction(
GenCopyConstructor(Ctx), FuncName, QT, IsVolatile,
std::array<CharUnits, 2>({{DstAlignment, SrcAlignment}}), CGM);
}
llvm::Function *clang::CodeGen::getNonTrivialCStructMoveConstructor(
CodeGenModule &CGM, CharUnits DstAlignment, CharUnits SrcAlignment,
bool IsVolatile, QualType QT) {
ASTContext &Ctx = CGM.getContext();
GenBinaryFuncName<true> GenName("__move_constructor_", DstAlignment,
SrcAlignment, Ctx);
std::string FuncName = GenName.getName(QT, IsVolatile);
return getSpecialFunction(
GenMoveConstructor(Ctx), FuncName, QT, IsVolatile,
std::array<CharUnits, 2>({{DstAlignment, SrcAlignment}}), CGM);
}
llvm::Function *clang::CodeGen::getNonTrivialCStructCopyAssignmentOperator(
CodeGenModule &CGM, CharUnits DstAlignment, CharUnits SrcAlignment,
bool IsVolatile, QualType QT) {
ASTContext &Ctx = CGM.getContext();
GenBinaryFuncName<false> GenName("__copy_assignment_", DstAlignment,
SrcAlignment, Ctx);
std::string FuncName = GenName.getName(QT, IsVolatile);
return getSpecialFunction(
GenCopyAssignment(Ctx), FuncName, QT, IsVolatile,
std::array<CharUnits, 2>({{DstAlignment, SrcAlignment}}), CGM);
}
llvm::Function *clang::CodeGen::getNonTrivialCStructMoveAssignmentOperator(
CodeGenModule &CGM, CharUnits DstAlignment, CharUnits SrcAlignment,
bool IsVolatile, QualType QT) {
ASTContext &Ctx = CGM.getContext();
GenBinaryFuncName<true> GenName("__move_assignment_", DstAlignment,
SrcAlignment, Ctx);
std::string FuncName = GenName.getName(QT, IsVolatile);
return getSpecialFunction(
GenMoveAssignment(Ctx), FuncName, QT, IsVolatile,
std::array<CharUnits, 2>({{DstAlignment, SrcAlignment}}), CGM);
}
llvm::Function *clang::CodeGen::getNonTrivialCStructDestructor(
CodeGenModule &CGM, CharUnits DstAlignment, bool IsVolatile, QualType QT) {
ASTContext &Ctx = CGM.getContext();
GenDestructorFuncName GenName("__destructor_", DstAlignment, Ctx);
std::string FuncName = GenName.getName(QT, IsVolatile);
return getSpecialFunction(GenDestructor(Ctx), FuncName, QT, IsVolatile,
std::array<CharUnits, 1>({{DstAlignment}}), CGM);
}