blob: 0c63b9d6bb7e75946977fb5a516d0d450ad4e1f0 [file] [log] [blame]
//===--- CGPointerAuth.cpp - IR generation for pointer authentication -----===//
//
// 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 contains common routines relating to the emission of
// pointer authentication operations.
//
//===----------------------------------------------------------------------===//
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "clang/CodeGen/CodeGenABITypes.h"
#include "clang/CodeGen/ConstantInitBuilder.h"
#include "llvm/Analysis/ValueTracking.h"
#include "llvm/Support/SipHash.h"
using namespace clang;
using namespace CodeGen;
/// Given a pointer-authentication schema, return a concrete "other"
/// discriminator for it.
llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator(
const PointerAuthSchema &Schema, GlobalDecl Decl, QualType Type) {
switch (Schema.getOtherDiscrimination()) {
case PointerAuthSchema::Discrimination::None:
return nullptr;
case PointerAuthSchema::Discrimination::Type:
assert(!Type.isNull() && "type not provided for type-discriminated schema");
return llvm::ConstantInt::get(
IntPtrTy, getContext().getPointerAuthTypeDiscriminator(Type));
case PointerAuthSchema::Discrimination::Decl:
assert(Decl.getDecl() &&
"declaration not provided for decl-discriminated schema");
return llvm::ConstantInt::get(IntPtrTy,
getPointerAuthDeclDiscriminator(Decl));
case PointerAuthSchema::Discrimination::Constant:
return llvm::ConstantInt::get(IntPtrTy, Schema.getConstantDiscrimination());
}
llvm_unreachable("bad discrimination kind");
}
uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM,
QualType FunctionType) {
return CGM.getContext().getPointerAuthTypeDiscriminator(FunctionType);
}
uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM,
GlobalDecl Declaration) {
return CGM.getPointerAuthDeclDiscriminator(Declaration);
}
/// Return the "other" decl-specific discriminator for the given decl.
uint16_t
CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) {
uint16_t &EntityHash = PtrAuthDiscriminatorHashes[Declaration];
if (EntityHash == 0) {
StringRef Name = getMangledName(Declaration);
EntityHash = llvm::getPointerAuthStableSipHash(Name);
}
return EntityHash;
}
/// Return the abstract pointer authentication schema for a pointer to the given
/// function type.
CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) {
const auto &Schema = getCodeGenOpts().PointerAuth.FunctionPointers;
if (!Schema)
return CGPointerAuthInfo();
assert(!Schema.isAddressDiscriminated() &&
"function pointers cannot use address-specific discrimination");
llvm::Constant *Discriminator = nullptr;
if (T->isFunctionPointerType() || T->isFunctionReferenceType())
T = T->getPointeeType();
if (T->isFunctionType())
Discriminator = getPointerAuthOtherDiscriminator(Schema, GlobalDecl(), T);
return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(),
/*IsaPointer=*/false, /*AuthenticatesNull=*/false,
Discriminator);
}
llvm::Value *
CodeGenFunction::EmitPointerAuthBlendDiscriminator(llvm::Value *StorageAddress,
llvm::Value *Discriminator) {
StorageAddress = Builder.CreatePtrToInt(StorageAddress, IntPtrTy);
auto Intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_blend);
return Builder.CreateCall(Intrinsic, {StorageAddress, Discriminator});
}
/// Emit the concrete pointer authentication informaton for the
/// given authentication schema.
CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo(
const PointerAuthSchema &Schema, llvm::Value *StorageAddress,
GlobalDecl SchemaDecl, QualType SchemaType) {
if (!Schema)
return CGPointerAuthInfo();
llvm::Value *Discriminator =
CGM.getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType);
if (Schema.isAddressDiscriminated()) {
assert(StorageAddress &&
"address not provided for address-discriminated schema");
if (Discriminator)
Discriminator =
EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator);
else
Discriminator = Builder.CreatePtrToInt(StorageAddress, IntPtrTy);
}
return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(),
Schema.isIsaPointer(),
Schema.authenticatesNullValues(), Discriminator);
}
/// Return the natural pointer authentication for values of the given
/// pointee type.
static CGPointerAuthInfo
getPointerAuthInfoForPointeeType(CodeGenModule &CGM, QualType PointeeType) {
if (PointeeType.isNull())
return CGPointerAuthInfo();
// Function pointers use the function-pointer schema by default.
if (PointeeType->isFunctionType())
return CGM.getFunctionPointerAuthInfo(PointeeType);
// Normal data pointers never use direct pointer authentication by default.
return CGPointerAuthInfo();
}
CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForPointeeType(QualType T) {
return ::getPointerAuthInfoForPointeeType(*this, T);
}
/// Return the natural pointer authentication for values of the given
/// pointer type.
static CGPointerAuthInfo getPointerAuthInfoForType(CodeGenModule &CGM,
QualType PointerType) {
assert(PointerType->isSignableType());
// Block pointers are currently not signed.
if (PointerType->isBlockPointerType())
return CGPointerAuthInfo();
auto PointeeType = PointerType->getPointeeType();
if (PointeeType.isNull())
return CGPointerAuthInfo();
return ::getPointerAuthInfoForPointeeType(CGM, PointeeType);
}
CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForType(QualType T) {
return ::getPointerAuthInfoForType(*this, T);
}
static bool isZeroConstant(const llvm::Value *Value) {
if (const auto *CI = dyn_cast<llvm::ConstantInt>(Value))
return CI->isZero();
return false;
}
static bool equalAuthPolicies(const CGPointerAuthInfo &Left,
const CGPointerAuthInfo &Right) {
assert((Left.isSigned() || Right.isSigned()) &&
"shouldn't be called if neither is signed");
if (Left.isSigned() != Right.isSigned())
return false;
return Left.getKey() == Right.getKey() &&
Left.getAuthenticationMode() == Right.getAuthenticationMode();
}
// Return the discriminator or return zero if the discriminator is null.
static llvm::Value *getDiscriminatorOrZero(const CGPointerAuthInfo &Info,
CGBuilderTy &Builder) {
llvm::Value *Discriminator = Info.getDiscriminator();
return Discriminator ? Discriminator : Builder.getSize(0);
}
llvm::Value *
CodeGenFunction::emitPointerAuthResignCall(llvm::Value *Value,
const CGPointerAuthInfo &CurAuth,
const CGPointerAuthInfo &NewAuth) {
assert(CurAuth && NewAuth);
if (CurAuth.getAuthenticationMode() !=
PointerAuthenticationMode::SignAndAuth ||
NewAuth.getAuthenticationMode() !=
PointerAuthenticationMode::SignAndAuth) {
llvm::Value *AuthedValue = EmitPointerAuthAuth(CurAuth, Value);
return EmitPointerAuthSign(NewAuth, AuthedValue);
}
// Convert the pointer to intptr_t before signing it.
auto *OrigType = Value->getType();
Value = Builder.CreatePtrToInt(Value, IntPtrTy);
auto *CurKey = Builder.getInt32(CurAuth.getKey());
auto *NewKey = Builder.getInt32(NewAuth.getKey());
llvm::Value *CurDiscriminator = getDiscriminatorOrZero(CurAuth, Builder);
llvm::Value *NewDiscriminator = getDiscriminatorOrZero(NewAuth, Builder);
// call i64 @llvm.ptrauth.resign(i64 %pointer,
// i32 %curKey, i64 %curDiscriminator,
// i32 %newKey, i64 %newDiscriminator)
auto *Intrinsic = CGM.getIntrinsic(llvm::Intrinsic::ptrauth_resign);
Value = EmitRuntimeCall(
Intrinsic, {Value, CurKey, CurDiscriminator, NewKey, NewDiscriminator});
// Convert back to the original type.
Value = Builder.CreateIntToPtr(Value, OrigType);
return Value;
}
llvm::Value *CodeGenFunction::emitPointerAuthResign(
llvm::Value *Value, QualType Type, const CGPointerAuthInfo &CurAuthInfo,
const CGPointerAuthInfo &NewAuthInfo, bool IsKnownNonNull) {
// Fast path: if neither schema wants a signature, we're done.
if (!CurAuthInfo && !NewAuthInfo)
return Value;
llvm::Value *Null = nullptr;
// If the value is obviously null, we're done.
if (auto *PointerValue = dyn_cast<llvm::PointerType>(Value->getType())) {
Null = CGM.getNullPointer(PointerValue, Type);
} else {
assert(Value->getType()->isIntegerTy());
Null = llvm::ConstantInt::get(IntPtrTy, 0);
}
if (Value == Null)
return Value;
// If both schemas sign the same way, we're done.
if (equalAuthPolicies(CurAuthInfo, NewAuthInfo)) {
const llvm::Value *CurD = CurAuthInfo.getDiscriminator();
const llvm::Value *NewD = NewAuthInfo.getDiscriminator();
if (CurD == NewD)
return Value;
if ((CurD == nullptr && isZeroConstant(NewD)) ||
(NewD == nullptr && isZeroConstant(CurD)))
return Value;
}
llvm::BasicBlock *InitBB = Builder.GetInsertBlock();
llvm::BasicBlock *ResignBB = nullptr, *ContBB = nullptr;
// Null pointers have to be mapped to null, and the ptrauth_resign
// intrinsic doesn't do that.
if (!IsKnownNonNull && !llvm::isKnownNonZero(Value, CGM.getDataLayout())) {
ContBB = createBasicBlock("resign.cont");
ResignBB = createBasicBlock("resign.nonnull");
auto *IsNonNull = Builder.CreateICmpNE(Value, Null);
Builder.CreateCondBr(IsNonNull, ResignBB, ContBB);
EmitBlock(ResignBB);
}
// Perform the auth/sign/resign operation.
if (!NewAuthInfo)
Value = EmitPointerAuthAuth(CurAuthInfo, Value);
else if (!CurAuthInfo)
Value = EmitPointerAuthSign(NewAuthInfo, Value);
else
Value = emitPointerAuthResignCall(Value, CurAuthInfo, NewAuthInfo);
// Clean up with a phi if we branched before.
if (ContBB) {
EmitBlock(ContBB);
auto *Phi = Builder.CreatePHI(Value->getType(), 2);
Phi->addIncoming(Null, InitBB);
Phi->addIncoming(Value, ResignBB);
Value = Phi;
}
return Value;
}
llvm::Constant *
CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key,
llvm::Constant *StorageAddress,
llvm::ConstantInt *OtherDiscriminator) {
llvm::Constant *AddressDiscriminator;
if (StorageAddress) {
assert(StorageAddress->getType() == UnqualPtrTy);
AddressDiscriminator = StorageAddress;
} else {
AddressDiscriminator = llvm::Constant::getNullValue(UnqualPtrTy);
}
llvm::ConstantInt *IntegerDiscriminator;
if (OtherDiscriminator) {
assert(OtherDiscriminator->getType() == Int64Ty);
IntegerDiscriminator = OtherDiscriminator;
} else {
IntegerDiscriminator = llvm::ConstantInt::get(Int64Ty, 0);
}
return llvm::ConstantPtrAuth::get(Pointer,
llvm::ConstantInt::get(Int32Ty, Key),
IntegerDiscriminator, AddressDiscriminator);
}
/// Does a given PointerAuthScheme require us to sign a value
bool CodeGenModule::shouldSignPointer(const PointerAuthSchema &Schema) {
auto AuthenticationMode = Schema.getAuthenticationMode();
return AuthenticationMode == PointerAuthenticationMode::SignAndStrip ||
AuthenticationMode == PointerAuthenticationMode::SignAndAuth;
}
/// Sign a constant pointer using the given scheme, producing a constant
/// with the same IR type.
llvm::Constant *CodeGenModule::getConstantSignedPointer(
llvm::Constant *Pointer, const PointerAuthSchema &Schema,
llvm::Constant *StorageAddress, GlobalDecl SchemaDecl,
QualType SchemaType) {
assert(shouldSignPointer(Schema));
llvm::ConstantInt *OtherDiscriminator =
getPointerAuthOtherDiscriminator(Schema, SchemaDecl, SchemaType);
return getConstantSignedPointer(Pointer, Schema.getKey(), StorageAddress,
OtherDiscriminator);
}
/// If applicable, sign a given constant function pointer with the ABI rules for
/// functionType.
llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *Pointer,
QualType FunctionType) {
assert(FunctionType->isFunctionType() ||
FunctionType->isFunctionReferenceType() ||
FunctionType->isFunctionPointerType());
if (auto PointerAuth = getFunctionPointerAuthInfo(FunctionType))
return getConstantSignedPointer(
Pointer, PointerAuth.getKey(), /*StorageAddress=*/nullptr,
cast_or_null<llvm::ConstantInt>(PointerAuth.getDiscriminator()));
return Pointer;
}
llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD,
llvm::Type *Ty) {
const auto *FD = cast<FunctionDecl>(GD.getDecl());
QualType FuncType = FD->getType();
// Annoyingly, K&R functions have prototypes in the clang AST, but
// expressions referring to them are unprototyped.
if (!FD->hasPrototype())
if (const auto *Proto = FuncType->getAs<FunctionProtoType>())
FuncType = Context.getFunctionNoProtoType(Proto->getReturnType(),
Proto->getExtInfo());
return getFunctionPointer(getRawFunctionPointer(GD, Ty), FuncType);
}
CGPointerAuthInfo CodeGenModule::getMemberFunctionPointerAuthInfo(QualType FT) {
assert(FT->getAs<MemberPointerType>() && "MemberPointerType expected");
const auto &Schema = getCodeGenOpts().PointerAuth.CXXMemberFunctionPointers;
if (!Schema)
return CGPointerAuthInfo();
assert(!Schema.isAddressDiscriminated() &&
"function pointers cannot use address-specific discrimination");
llvm::ConstantInt *Discriminator =
getPointerAuthOtherDiscriminator(Schema, GlobalDecl(), FT);
return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(),
/* IsIsaPointer */ false,
/* AuthenticatesNullValues */ false, Discriminator);
}
llvm::Constant *CodeGenModule::getMemberFunctionPointer(llvm::Constant *Pointer,
QualType FT) {
if (CGPointerAuthInfo PointerAuth = getMemberFunctionPointerAuthInfo(FT))
return getConstantSignedPointer(
Pointer, PointerAuth.getKey(), nullptr,
cast_or_null<llvm::ConstantInt>(PointerAuth.getDiscriminator()));
return Pointer;
}
llvm::Constant *CodeGenModule::getMemberFunctionPointer(const FunctionDecl *FD,
llvm::Type *Ty) {
QualType FT = FD->getType();
FT = getContext().getMemberPointerType(
FT, cast<CXXMethodDecl>(FD)->getParent()->getTypeForDecl());
return getMemberFunctionPointer(getRawFunctionPointer(FD, Ty), FT);
}
std::optional<PointerAuthQualifier>
CodeGenModule::computeVTPointerAuthentication(const CXXRecordDecl *ThisClass) {
auto DefaultAuthentication = getCodeGenOpts().PointerAuth.CXXVTablePointers;
if (!DefaultAuthentication)
return std::nullopt;
const CXXRecordDecl *PrimaryBase =
Context.baseForVTableAuthentication(ThisClass);
unsigned Key = DefaultAuthentication.getKey();
bool AddressDiscriminated = DefaultAuthentication.isAddressDiscriminated();
auto DefaultDiscrimination = DefaultAuthentication.getOtherDiscrimination();
unsigned TypeBasedDiscriminator =
Context.getPointerAuthVTablePointerDiscriminator(PrimaryBase);
unsigned Discriminator;
if (DefaultDiscrimination == PointerAuthSchema::Discrimination::Type) {
Discriminator = TypeBasedDiscriminator;
} else if (DefaultDiscrimination ==
PointerAuthSchema::Discrimination::Constant) {
Discriminator = DefaultAuthentication.getConstantDiscrimination();
} else {
assert(DefaultDiscrimination == PointerAuthSchema::Discrimination::None);
Discriminator = 0;
}
if (auto ExplicitAuthentication =
PrimaryBase->getAttr<VTablePointerAuthenticationAttr>()) {
auto ExplicitAddressDiscrimination =
ExplicitAuthentication->getAddressDiscrimination();
auto ExplicitDiscriminator =
ExplicitAuthentication->getExtraDiscrimination();
unsigned ExplicitKey = ExplicitAuthentication->getKey();
if (ExplicitKey == VTablePointerAuthenticationAttr::NoKey)
return std::nullopt;
if (ExplicitKey != VTablePointerAuthenticationAttr::DefaultKey) {
if (ExplicitKey == VTablePointerAuthenticationAttr::ProcessIndependent)
Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDA;
else {
assert(ExplicitKey ==
VTablePointerAuthenticationAttr::ProcessDependent);
Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDB;
}
}
if (ExplicitAddressDiscrimination !=
VTablePointerAuthenticationAttr::DefaultAddressDiscrimination)
AddressDiscriminated =
ExplicitAddressDiscrimination ==
VTablePointerAuthenticationAttr::AddressDiscrimination;
if (ExplicitDiscriminator ==
VTablePointerAuthenticationAttr::TypeDiscrimination)
Discriminator = TypeBasedDiscriminator;
else if (ExplicitDiscriminator ==
VTablePointerAuthenticationAttr::CustomDiscrimination)
Discriminator = ExplicitAuthentication->getCustomDiscriminationValue();
else if (ExplicitDiscriminator ==
VTablePointerAuthenticationAttr::NoExtraDiscrimination)
Discriminator = 0;
}
return PointerAuthQualifier::Create(Key, AddressDiscriminated, Discriminator,
PointerAuthenticationMode::SignAndAuth,
/* IsIsaPointer */ false,
/* AuthenticatesNullValues */ false);
}
std::optional<PointerAuthQualifier>
CodeGenModule::getVTablePointerAuthentication(const CXXRecordDecl *Record) {
if (!Record->getDefinition() || !Record->isPolymorphic())
return std::nullopt;
auto Existing = VTablePtrAuthInfos.find(Record);
std::optional<PointerAuthQualifier> Authentication;
if (Existing != VTablePtrAuthInfos.end()) {
Authentication = Existing->getSecond();
} else {
Authentication = computeVTPointerAuthentication(Record);
VTablePtrAuthInfos.insert(std::make_pair(Record, Authentication));
}
return Authentication;
}
std::optional<CGPointerAuthInfo>
CodeGenModule::getVTablePointerAuthInfo(CodeGenFunction *CGF,
const CXXRecordDecl *Record,
llvm::Value *StorageAddress) {
auto Authentication = getVTablePointerAuthentication(Record);
if (!Authentication)
return std::nullopt;
llvm::Value *Discriminator = nullptr;
if (auto ExtraDiscriminator = Authentication->getExtraDiscriminator())
Discriminator = llvm::ConstantInt::get(IntPtrTy, ExtraDiscriminator);
if (Authentication->isAddressDiscriminated()) {
assert(StorageAddress &&
"address not provided for address-discriminated schema");
if (Discriminator)
Discriminator =
CGF->EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator);
else
Discriminator = CGF->Builder.CreatePtrToInt(StorageAddress, IntPtrTy);
}
return CGPointerAuthInfo(Authentication->getKey(),
PointerAuthenticationMode::SignAndAuth,
/* IsIsaPointer */ false,
/* AuthenticatesNullValues */ false, Discriminator);
}
llvm::Value *CodeGenFunction::authPointerToPointerCast(llvm::Value *ResultPtr,
QualType SourceType,
QualType DestType) {
CGPointerAuthInfo CurAuthInfo, NewAuthInfo;
if (SourceType->isSignableType())
CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType);
if (DestType->isSignableType())
NewAuthInfo = getPointerAuthInfoForType(CGM, DestType);
if (!CurAuthInfo && !NewAuthInfo)
return ResultPtr;
// If only one side of the cast is a function pointer, then we still need to
// resign to handle casts to/from opaque pointers.
if (!CurAuthInfo && DestType->isFunctionPointerType())
CurAuthInfo = CGM.getFunctionPointerAuthInfo(SourceType);
if (!NewAuthInfo && SourceType->isFunctionPointerType())
NewAuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
return emitPointerAuthResign(ResultPtr, DestType, CurAuthInfo, NewAuthInfo,
/*IsKnownNonNull=*/false);
}
Address CodeGenFunction::authPointerToPointerCast(Address Ptr,
QualType SourceType,
QualType DestType) {
CGPointerAuthInfo CurAuthInfo, NewAuthInfo;
if (SourceType->isSignableType())
CurAuthInfo = getPointerAuthInfoForType(CGM, SourceType);
if (DestType->isSignableType())
NewAuthInfo = getPointerAuthInfoForType(CGM, DestType);
if (!CurAuthInfo && !NewAuthInfo)
return Ptr;
if (!CurAuthInfo && DestType->isFunctionPointerType()) {
// When casting a non-signed pointer to a function pointer, just set the
// auth info on Ptr to the assumed schema. The pointer will be resigned to
// the effective type when used.
Ptr.setPointerAuthInfo(CGM.getFunctionPointerAuthInfo(SourceType));
return Ptr;
}
if (!NewAuthInfo && SourceType->isFunctionPointerType()) {
NewAuthInfo = CGM.getFunctionPointerAuthInfo(DestType);
Ptr = Ptr.getResignedAddress(NewAuthInfo, *this);
Ptr.setPointerAuthInfo(CGPointerAuthInfo());
return Ptr;
}
return Ptr;
}
Address CodeGenFunction::getAsNaturalAddressOf(Address Addr,
QualType PointeeTy) {
CGPointerAuthInfo Info =
PointeeTy.isNull() ? CGPointerAuthInfo()
: CGM.getPointerAuthInfoForPointeeType(PointeeTy);
return Addr.getResignedAddress(Info, *this);
}
Address Address::getResignedAddress(const CGPointerAuthInfo &NewInfo,
CodeGenFunction &CGF) const {
assert(isValid() && "pointer isn't valid");
CGPointerAuthInfo CurInfo = getPointerAuthInfo();
llvm::Value *Val;
// Nothing to do if neither the current or the new ptrauth info needs signing.
if (!CurInfo.isSigned() && !NewInfo.isSigned())
return Address(getBasePointer(), getElementType(), getAlignment(),
isKnownNonNull());
assert(ElementType && "Effective type has to be set");
assert(!Offset && "unexpected non-null offset");
// If the current and the new ptrauth infos are the same and the offset is
// null, just cast the base pointer to the effective type.
if (CurInfo == NewInfo && !hasOffset())
Val = getBasePointer();
else
Val = CGF.emitPointerAuthResign(getBasePointer(), QualType(), CurInfo,
NewInfo, isKnownNonNull());
Val = CGF.Builder.CreateBitCast(Val, getType());
return Address(Val, getElementType(), getAlignment(), NewInfo,
/*Offset=*/nullptr, isKnownNonNull());
}
llvm::Value *Address::emitRawPointerSlow(CodeGenFunction &CGF) const {
return CGF.getAsNaturalPointerTo(*this, QualType());
}
llvm::Value *LValue::getPointer(CodeGenFunction &CGF) const {
assert(isSimple());
return emitResignedPointer(getType(), CGF);
}
llvm::Value *LValue::emitResignedPointer(QualType PointeeTy,
CodeGenFunction &CGF) const {
assert(isSimple());
return CGF.getAsNaturalAddressOf(Addr, PointeeTy).getBasePointer();
}
llvm::Value *LValue::emitRawPointer(CodeGenFunction &CGF) const {
assert(isSimple());
return Addr.isValid() ? Addr.emitRawPointer(CGF) : nullptr;
}