Skip to content

Commit 5968d33

Browse files
committed
Set no_preserve_tags for copies of structs without capabilities
This allows inlining of structure assignments for structs that are at least capability size but do not contain any capabilities (e.g. `struct { long a; long b; }`). We can also set the attribute for all trivial auto var-init cases since those patterns never contain valid capabilities. Due to C's effective type rules, we have to be careful when setting the attribute and only perform the type-base tag-preservation analysis if we know the effective type. For example, marking a memcpy() to/from `long*` as not tag-preserving could result in tag stripping for code that uses type casts. Such code is correct even under strict aliasing rules since the first store to a memory location determines the type. Example from #506: ``` void *malloc(__SIZE_TYPE__); void *memcpy(void *, const void *, __SIZE_TYPE__); void foo(long **p, long **q) { *p = malloc(32); *q = malloc(32); (*p)[0] = 1; (*p)[1] = 2; *(void (**)(long **, long **))(*p + 2) = &foo; memcpy(*q, *p, 32); } ``` Despite the memcpy() argument being a long* (and therefore intuitively not tag preserving), we can't add the attribute since we don't actually know the type of the underlying object (malloc creates an allocated with no declared type). From C99: ``` The effective type of an object for an access to its stored value is the declared type of the object, if any (footnote 75: Allocated objects have no declared type). If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value. If a value is copied into an object having no declared type using memcpy or memmove, or is copied as an array of character type, then the effective type of the modified object for that access and for subsequent accesses that do not modify the value is the effective type of the object from which the value is copied, if it has one. For all other accesses to an object having no declared type, the effective type of the object is simply the type of the lvalue used for the access. ``` There is another important caveat: we have to conservatively assume that the copy affects adjacent data (e.g. C++ subclass fields) that could hold capabilities if we don't know the copy size. If the copy size is <= sizeof(T), we can mark copies as non-tag-preserving since it cannot affect trailing fields (even if we are actually copying a subclass). We are also conservative if the structure contains an array of type ((un)signed) char or std::byte since those are often used to store arbitrary data (including capabilities). We could make this check more strict and require the array to be capability aligned, but that could be done as a follow-up change.
1 parent 8a3ebe2 commit 5968d33

18 files changed

+777
-239
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2210,6 +2210,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
22102210
private:
22112211
/// Map storing whether a type contains capabilities.
22122212
mutable llvm::DenseMap<void*, bool> ContainsCapabilities;
2213+
mutable llvm::DenseMap<void *, bool> CannotContainCapabilities;
22132214

22142215
CanQualType getFromTargetType(unsigned Type) const;
22152216
TypeInfo getTypeInfoImpl(const Type *T) const;
@@ -2478,6 +2479,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
24782479
/// capability or an aggregate type that contains one or more capabilities.
24792480
bool containsCapabilities(QualType Ty) const;
24802481

2482+
/// Returns true if the record type cannot contain capabilities.
2483+
/// NB: this is a conservative analysis that treats overaligned char arrays as
2484+
/// potentially containing capabilities.
2485+
bool cannotContainCapabilities(const RecordDecl *RD) const;
2486+
/// Returns true if the type is a scalar type that has a representationa
2487+
/// that cannot be used to (legally) store capabilities.
2488+
/// NB: this is a conservative analysis that treats overaligned char arrays as
2489+
/// potentially containing capabilities.
2490+
bool cannotContainCapabilities(QualType Ty) const;
2491+
24812492
/// Return true if the specified type has unique object representations
24822493
/// according to (C++17 [meta.unary.prop]p9)
24832494
bool hasUniqueObjectRepresentations(QualType Ty) const;

clang/lib/AST/ASTContext.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11730,6 +11730,58 @@ bool ASTContext::containsCapabilities(QualType Ty) const {
1173011730
return Ret;
1173111731
}
1173211732

11733+
bool ASTContext::cannotContainCapabilities(const RecordDecl *RD) const {
11734+
for (auto i = RD->field_begin(), e = RD->field_end(); i != e; ++i) {
11735+
const QualType Ty = i->getType();
11736+
if (Ty->isCHERICapabilityType(*this))
11737+
return false;
11738+
else if (const RecordType *RT = Ty->getAs<RecordType>()) {
11739+
if (!cannotContainCapabilities(RT->getDecl()))
11740+
return false;
11741+
} else if (!cannotContainCapabilities(Ty))
11742+
return false;
11743+
}
11744+
// In the case of C++ classes, also check base classes
11745+
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD)) {
11746+
for (auto i = CRD->bases_begin(), e = CRD->bases_end(); i != e; ++i) {
11747+
const QualType Ty = i->getType();
11748+
if (const RecordType *RT = Ty->getAs<RecordType>())
11749+
if (!cannotContainCapabilities(RT->getDecl()))
11750+
return false;
11751+
}
11752+
}
11753+
return true; // Check all types that could contain capabilities
11754+
}
11755+
11756+
bool ASTContext::cannotContainCapabilities(QualType Ty) const {
11757+
// If we've already looked up this type, then return the cached value.
11758+
auto Cached = CannotContainCapabilities.find(Ty.getAsOpaquePtr());
11759+
if (Cached != CannotContainCapabilities.end())
11760+
return Cached->second;
11761+
// Don't bother caching the trivial cases.
11762+
if (containsCapabilities(Ty))
11763+
return false;
11764+
if (Ty->isArrayType()) {
11765+
QualType ElTy(Ty->getBaseElementTypeUnsafe(), 0);
11766+
// We have to be conservative here and assume that (unsigned) char[] as
11767+
// well as std::byte can be used for buffers that store capabilities.
11768+
// TODO: we could restrict this to buffers that are large enough and
11769+
// sufficiently aligned to store a capability.
11770+
if (ElTy->isCharType() || ElTy->isStdByteType())
11771+
return false;
11772+
return cannotContainCapabilities(ElTy);
11773+
}
11774+
const RecordType *RT = Ty->getAs<RecordType>();
11775+
if (!RT) {
11776+
// Not a record type, and the check above ensured this is not a capability
11777+
// type, so this type can't contain capabilities.
11778+
return true;
11779+
}
11780+
bool Ret = cannotContainCapabilities(RT->getDecl());
11781+
CannotContainCapabilities[Ty.getAsOpaquePtr()] = Ret;
11782+
return Ret;
11783+
}
11784+
1173311785
QualType ASTContext::getCorrespondingSaturatedType(QualType Ty) const {
1173411786
assert(Ty->isFixedPointType());
1173511787

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2164,18 +2164,17 @@ RValue CodeGenFunction::emitRotate(const CallExpr *E, bool IsRotateRight) {
21642164
}
21652165

21662166
// Diagnose misaligned copies (memmove/memcpy) of source types that contain
2167-
// capabilities to a dst buffer that is less than capability aligned.
2168-
// This can result in tags being lost at runtime if the buffer is not actually
2169-
// capability aligned. Furthermore, if the user adds a __builtin_assume_aligned()
2170-
// or a cast to a capability we can assume it is capability aligned an use
2171-
// csc/clc if the memcpy()/memmove() is expanded inline.
2167+
// capabilities to a dst buffer that is less than capability aligned. This can
2168+
// result in tags being lost at runtime if the buffer is not actually capability
2169+
// aligned. Another benefit of this diagnostic is that it can cause the the user
2170+
// to add __builtin_assume_aligned() or a cast to a capability. This allows us
2171+
// to potentially expand the memcpy()/memmove() inline.
21722172
// TODO: maybe there needs to be an attribute __memmove_like__ or similar to
21732173
// indicate that a function behaves like memmove/memcpy and we can use that
21742174
// to diagnose unaligned copies.
2175-
static void
2176-
diagnoseMisalignedCapabiliyCopyDest(CodeGenFunction &CGF, StringRef Function,
2177-
const Expr *Src, const CharUnits DstAlignCU,
2178-
AnyMemTransferInst *MemInst = nullptr) {
2175+
static void checkCapabilityCopy(CodeGenFunction &CGF, StringRef Function,
2176+
const Expr *Src, const CharUnits DstAlignCU,
2177+
AnyMemTransferInst *MemInst = nullptr) {
21792178
// we want the real type not the implicit conversion to void*
21802179
// TODO: ignore the first explicit cast to void*?
21812180
auto UnderlyingSrcTy = Src->IgnoreParenImpCasts()->getType();
@@ -2187,18 +2186,15 @@ diagnoseMisalignedCapabiliyCopyDest(CodeGenFunction &CGF, StringRef Function,
21872186
if (!Ctx.containsCapabilities(UnderlyingSrcTy))
21882187
return;
21892188

2190-
// Add a must_preserve_cheri_tags attribute to the memcpy/memmove
2191-
// intrinsic to ensure that the backend will not lower it to an inlined
2192-
// sequence of 1/2/4/8 byte loads and stores which would strip the tag bits.
2193-
// TODO: a clc/csc that works on unaligned data but traps for a csc
2194-
// with a tagged value and unaligned address could also prevent tags
2195-
// from being lost.
2189+
// If we have a memory intrinsic, we let the backend diagnose this issue
2190+
// since the clang frontend rarely has enough information to correctly infer
2191+
// the alignment.
21962192
if (MemInst) {
2197-
// If we have a memory intrinsic let the backend diagnose this issue:
2198-
// First, tell the backend that this copy must preserve tags
2199-
MemInst->addAttribute(llvm::AttributeList::FunctionIndex,
2200-
llvm::Attribute::MustPreserveCheriTags);
2201-
// And also tell it what the underlying type was for improved diagnostics.
2193+
// No need to diagnose anything if we aren't preserving tags.
2194+
if (MemInst->shouldPreserveCheriTags() == PreserveCheriTags::Unnecessary)
2195+
return;
2196+
// Add a "frontend-memtransfer-type" attribute to the intrinsic
2197+
// to ensure that the backend can diagnose misaligned capability copies.
22022198
std::string TypeName = UnderlyingSrcTy.getAsString();
22032199
std::string CanonicalStr = UnderlyingSrcTy.getCanonicalType().getAsString();
22042200
if (CanonicalStr != TypeName)
@@ -2255,23 +2251,20 @@ diagnoseMisalignedCapabiliyCopyDest(CodeGenFunction &CGF, StringRef Function,
22552251
}
22562252
}
22572253

2258-
static void diagnoseMisalignedCapabiliyCopyDest(CodeGenFunction &CGF,
2259-
StringRef Function,
2260-
const Expr *Src, CallInst *CI) {
2254+
static void checkCapabilityCopy(CodeGenFunction &CGF, StringRef Function,
2255+
const Expr *Src, CallInst *CI) {
22612256
AnyMemTransferInst *MemInst = cast<AnyMemTransferInst>(CI);
2262-
diagnoseMisalignedCapabiliyCopyDest(
2263-
CGF, Function, Src, CharUnits::fromQuantity(MemInst->getDestAlignment()),
2264-
MemInst);
2257+
checkCapabilityCopy(CGF, Function, Src,
2258+
CharUnits::fromQuantity(MemInst->getDestAlignment()),
2259+
MemInst);
22652260
}
22662261

2267-
static void diagnoseMisalignedCapabiliyCopyDest(CodeGenFunction &CGF,
2268-
StringRef Function,
2269-
const Expr *Src,
2270-
const Expr *Dst) {
2262+
static void checkCapabilityCopy(CodeGenFunction &CGF, StringRef Function,
2263+
const Expr *Src, const Expr *Dst) {
22712264
auto UnderlyingDstTy = QualType(
22722265
Dst->IgnoreImpCasts()->getType()->getPointeeOrArrayElementType(), 0);
2273-
diagnoseMisalignedCapabiliyCopyDest(
2274-
CGF, Function, Src, CGF.CGM.getNaturalTypeAlignment(UnderlyingDstTy));
2266+
checkCapabilityCopy(CGF, Function, Src,
2267+
CGF.CGM.getNaturalTypeAlignment(UnderlyingDstTy));
22752268
}
22762269

22772270
// Map math builtins for long-double to f128 version.
@@ -3490,9 +3483,11 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
34903483
E->getArg(0)->getExprLoc(), FD, 0);
34913484
EmitNonNullArgCheck(RValue::get(Src.getPointer()), E->getArg(1)->getType(),
34923485
E->getArg(1)->getExprLoc(), FD, 1);
3493-
auto CI = Builder.CreateMemCpy(Dest, Src, SizeVal,
3494-
llvm::PreserveCheriTags::TODO, false);
3495-
diagnoseMisalignedCapabiliyCopyDest(*this, "memcpy", E->getArg(1), CI);
3486+
auto CI = Builder.CreateMemCpy(
3487+
Dest, Src, SizeVal,
3488+
getTypes().copyShouldPreserveTags(E->getArg(0), E->getArg(1), SizeVal),
3489+
false);
3490+
checkCapabilityCopy(*this, "memcpy", E->getArg(1), CI);
34963491
if (BuiltinID == Builtin::BImempcpy ||
34973492
BuiltinID == Builtin::BI__builtin_mempcpy)
34983493
return RValue::get(Builder.CreateInBoundsGEP(Dest.getElementType(),
@@ -3511,7 +3506,10 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
35113506
E->getArg(0)->getExprLoc(), FD, 0);
35123507
EmitNonNullArgCheck(RValue::get(Src.getPointer()), E->getArg(1)->getType(),
35133508
E->getArg(1)->getExprLoc(), FD, 1);
3514-
Builder.CreateMemCpyInline(Dest, Src, Size);
3509+
Builder.CreateMemCpyInline(
3510+
Dest, Src, Size,
3511+
getTypes().copyShouldPreserveTags(E->getArg(0), E->getArg(1),
3512+
CharUnits::fromQuantity(Size)));
35153513
return RValue::get(nullptr);
35163514
}
35173515

@@ -3524,23 +3522,23 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
35243522
Expr::EvalResult SizeResult, DstSizeResult;
35253523
if (!E->getArg(2)->EvaluateAsInt(SizeResult, CGM.getContext()) ||
35263524
!E->getArg(3)->EvaluateAsInt(DstSizeResult, CGM.getContext())) {
3527-
diagnoseMisalignedCapabiliyCopyDest(*this, "__memcpy_chk", E->getArg(1),
3528-
E->getArg(0));
3525+
checkCapabilityCopy(*this, "__memcpy_chk", E->getArg(1), E->getArg(0));
35293526
break;
35303527
}
35313528
llvm::APSInt Size = SizeResult.Val.getInt();
35323529
llvm::APSInt DstSize = DstSizeResult.Val.getInt();
35333530
if (Size.ugt(DstSize)) {
3534-
diagnoseMisalignedCapabiliyCopyDest(*this, "__memcpy_chk", E->getArg(1),
3535-
E->getArg(0));
3531+
checkCapabilityCopy(*this, "__memcpy_chk", E->getArg(1), E->getArg(0));
35363532
break;
35373533
}
35383534
Address Dest = EmitPointerWithAlignment(E->getArg(0));
35393535
Address Src = EmitPointerWithAlignment(E->getArg(1));
35403536
Value *SizeVal = llvm::ConstantInt::get(Builder.getContext(), Size);
3541-
auto CI = Builder.CreateMemCpy(Dest, Src, SizeVal,
3542-
llvm::PreserveCheriTags::TODO, false);
3543-
diagnoseMisalignedCapabiliyCopyDest(*this, "memcpy", E->getArg(1), CI);
3537+
auto CI = Builder.CreateMemCpy(
3538+
Dest, Src, SizeVal,
3539+
getTypes().copyShouldPreserveTags(E->getArg(0), E->getArg(1), SizeVal),
3540+
false);
3541+
checkCapabilityCopy(*this, "memcpy", E->getArg(1), CI);
35443542
return RValue::get(Dest.getPointer(), Dest.getAlignment().getQuantity());
35453543
}
35463544

@@ -3559,23 +3557,23 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
35593557
Expr::EvalResult SizeResult, DstSizeResult;
35603558
if (!E->getArg(2)->EvaluateAsInt(SizeResult, CGM.getContext()) ||
35613559
!E->getArg(3)->EvaluateAsInt(DstSizeResult, CGM.getContext())) {
3562-
diagnoseMisalignedCapabiliyCopyDest(*this, "__memmove_chk", E->getArg(1),
3563-
E->getArg(0));
3560+
checkCapabilityCopy(*this, "__memmove_chk", E->getArg(1), E->getArg(0));
35643561
break;
35653562
}
35663563
llvm::APSInt Size = SizeResult.Val.getInt();
35673564
llvm::APSInt DstSize = DstSizeResult.Val.getInt();
35683565
if (Size.ugt(DstSize)) {
3569-
diagnoseMisalignedCapabiliyCopyDest(*this, "__memmove_chk", E->getArg(1),
3570-
E->getArg(0));
3566+
checkCapabilityCopy(*this, "__memmove_chk", E->getArg(1), E->getArg(0));
35713567
break;
35723568
}
35733569
Address Dest = EmitPointerWithAlignment(E->getArg(0));
35743570
Address Src = EmitPointerWithAlignment(E->getArg(1));
35753571
Value *SizeVal = llvm::ConstantInt::get(Builder.getContext(), Size);
3576-
auto CI = Builder.CreateMemMove(Dest, Src, SizeVal,
3577-
llvm::PreserveCheriTags::TODO, false);
3578-
diagnoseMisalignedCapabiliyCopyDest(*this, "memmove", E->getArg(1), CI);
3572+
auto CI = Builder.CreateMemMove(
3573+
Dest, Src, SizeVal,
3574+
getTypes().copyShouldPreserveTags(E->getArg(0), E->getArg(1), SizeVal),
3575+
false);
3576+
checkCapabilityCopy(*this, "memmove", E->getArg(1), CI);
35793577
return RValue::get(Dest.getPointer(), Dest.getAlignment().getQuantity());
35803578
}
35813579

@@ -3588,9 +3586,11 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
35883586
E->getArg(0)->getExprLoc(), FD, 0);
35893587
EmitNonNullArgCheck(RValue::get(Src.getPointer()), E->getArg(1)->getType(),
35903588
E->getArg(1)->getExprLoc(), FD, 1);
3591-
auto CI = Builder.CreateMemMove(Dest, Src, SizeVal,
3592-
llvm::PreserveCheriTags::TODO, false);
3593-
diagnoseMisalignedCapabiliyCopyDest(*this, "memmove", E->getArg(1), CI);
3589+
auto CI = Builder.CreateMemMove(
3590+
Dest, Src, SizeVal,
3591+
getTypes().copyShouldPreserveTags(E->getArg(0), E->getArg(1), SizeVal),
3592+
false);
3593+
checkCapabilityCopy(*this, "memmove", E->getArg(1), CI);
35943594
return RValue::get(Dest.getPointer(), Dest.getAlignment().getQuantity());
35953595
}
35963596
case Builtin::BImemset:

clang/lib/CodeGen/CGClass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,10 +980,13 @@ namespace {
980980
LValue SrcLV = CGF.MakeNaturalAlignAddrLValue(SrcPtr, RecordTy);
981981
LValue Src = CGF.EmitLValueForFieldInitialization(SrcLV, FirstField);
982982

983+
// We can pass EffectiveTypeKnown=true since this a C++ field copy.
984+
auto PreserveTags = CGF.getTypes().copyShouldPreserveTagsForPointee(
985+
RecordTy, /*EffectiveTypeKnown=*/true, MemcpySize);
983986
emitMemcpyIR(
984987
Dest.isBitField() ? Dest.getBitFieldAddress() : Dest.getAddress(CGF),
985988
Src.isBitField() ? Src.getBitFieldAddress() : Src.getAddress(CGF),
986-
MemcpySize, llvm::PreserveCheriTags::TODO);
989+
MemcpySize, PreserveTags);
987990
reset();
988991
}
989992

clang/lib/CodeGen/CGDecl.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1265,12 +1265,18 @@ static void emitStoresForConstant(CodeGenModule &CGM, const VarDecl &D,
12651265
}
12661266
}
12671267

1268-
// Copy from a global.
1268+
// Copy from a global (and therefore the effective type of the variable is
1269+
// known).
1270+
auto PreserveTags =
1271+
IsAutoInit || !ContainsCaps
1272+
? llvm::PreserveCheriTags::Unnecessary
1273+
: CGM.getTypes().copyShouldPreserveTagsForPointee(
1274+
D.getType(), /*EffectiveTypeKnown=*/true, SizeVal);
12691275
auto *I =
12701276
Builder.CreateMemCpy(Loc,
12711277
createUnnamedGlobalForMemcpyFrom(
12721278
CGM, D, Builder, constant, Loc.getAlignment()),
1273-
SizeVal, llvm::PreserveCheriTags::TODO, isVolatile);
1279+
SizeVal, PreserveTags, isVolatile);
12741280
if (IsAutoInit)
12751281
I->addAnnotationMetadata("auto-init");
12761282
}
@@ -1799,11 +1805,13 @@ void CodeGenFunction::emitZeroOrPatternForAutoVarInit(QualType type,
17991805
llvm::PHINode *Cur = Builder.CreatePHI(Begin.getType(), 2, "vla.cur");
18001806
Cur->addIncoming(Begin.getPointer(), OriginBB);
18011807
CharUnits CurAlign = Loc.getAlignment().alignmentOfArrayElement(EltSize);
1808+
// Pattern init never writes valid tags, so we can pass
1809+
// PreserveCheriTags::Unnecessary to the CreateMemCpy() call
18021810
auto *I = Builder.CreateMemCpy(
18031811
Address(Cur, CurAlign),
18041812
createUnnamedGlobalForMemcpyFrom(CGM, D, Builder, Constant,
18051813
ConstantAlign),
1806-
BaseSizeInChars, llvm::PreserveCheriTags::TODO, isVolatile);
1814+
BaseSizeInChars, llvm::PreserveCheriTags::Unnecessary, isVolatile);
18071815
I->addAnnotationMetadata("auto-init");
18081816
llvm::Value *Next =
18091817
Builder.CreateInBoundsGEP(Int8Ty, Cur, BaseSizeInChars, "vla.next");

clang/lib/CodeGen/CGExprAgg.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,9 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) {
747747
llvm::Value *SizeVal = llvm::ConstantInt::get(
748748
CGF.SizeTy,
749749
CGF.getContext().getTypeSizeInChars(E->getType()).getQuantity());
750-
Builder.CreateMemCpy(DestAddress, SourceAddress, SizeVal);
750+
Builder.CreateMemCpy(
751+
DestAddress, SourceAddress, SizeVal,
752+
CGF.getTypes().copyShouldPreserveTags(E, E->getSubExpr(), SizeVal));
751753
break;
752754
}
753755

@@ -2168,9 +2170,13 @@ void CodeGenFunction::EmitAggregateCopy(LValue Dest, LValue Src, QualType Ty,
21682170
}
21692171
}
21702172
}
2171-
2172-
auto Inst = Builder.CreateMemCpy(DestPtr, SrcPtr, SizeVal,
2173-
llvm::PreserveCheriTags::TODO, isVolatile);
2173+
// Note: this is used for expressions such as x = y, and not memcpy() calls,
2174+
// so according to C2x 6.5 "the effective type of the object is simply
2175+
// the type of the lvalue used for the access."
2176+
auto PreserveTags = getTypes().copyShouldPreserveTagsForPointee(
2177+
Ty, /*EffectiveTypeKnown=*/true, SizeVal);
2178+
auto Inst =
2179+
Builder.CreateMemCpy(DestPtr, SrcPtr, SizeVal, PreserveTags, isVolatile);
21742180

21752181
// Determine the metadata to describe the position of any padding in this
21762182
// memcpy, as well as the TBAA tags for the members of the struct, in case

0 commit comments

Comments
 (0)