-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[clang] Add builtin_get_vtable_pointer and virtual_member_address #135469
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
#include "CGDebugInfo.h" | ||
#include "CGObjCRuntime.h" | ||
#include "CGOpenCLRuntime.h" | ||
#include "CGPointerAuthInfo.h" | ||
#include "CGRecordLayout.h" | ||
#include "CGValue.h" | ||
#include "CodeGenFunction.h" | ||
|
@@ -5349,6 +5350,40 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, | |
return RValue::get(Result); | ||
} | ||
|
||
case Builtin::BI__builtin_virtual_member_address: { | ||
Address This = EmitLValue(E->getArg(0)).getAddress(); | ||
APValue ConstMemFun; | ||
E->getArg(1)->isCXX11ConstantExpr(getContext(), &ConstMemFun, nullptr); | ||
const CXXMethodDecl *CXXMethod = | ||
cast<CXXMethodDecl>(ConstMemFun.getMemberPointerDecl()); | ||
const CGFunctionInfo &FInfo = | ||
CGM.getTypes().arrangeCXXMethodDeclaration(CXXMethod); | ||
llvm::FunctionType *Ty = CGM.getTypes().GetFunctionType(FInfo); | ||
CGCallee VCallee = CGM.getCXXABI().getVirtualFunctionPointer( | ||
*this, CXXMethod, This, Ty, E->getBeginLoc()); | ||
llvm::Value *Callee = VCallee.getFunctionPointer(); | ||
if (const CGPointerAuthInfo &Schema = VCallee.getPointerAuthInfo()) | ||
Callee = EmitPointerAuthAuth(Schema, Callee); | ||
return RValue::get(Callee); | ||
} | ||
|
||
case Builtin::BI__builtin_get_vtable_pointer: { | ||
const Expr *Target = E->getArg(0); | ||
QualType TargetType = Target->getType(); | ||
QualType RecordType = TargetType; | ||
if (RecordType->isPointerOrReferenceType()) | ||
RecordType = RecordType->getPointeeType(); | ||
const CXXRecordDecl *Decl = RecordType->getAsCXXRecordDecl(); | ||
assert(Decl); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I note you are asserting pointers here but not in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Different authors over time, I'll unify on not asserting as null deref is the downstream outcome. |
||
auto ThisAddress = TargetType->isPointerType() | ||
? EmitPointerWithAlignment(Target) | ||
: EmitLValue(Target).getAddress(); | ||
assert(ThisAddress.isValid()); | ||
llvm::Value *VTablePointer = | ||
GetVTablePtr(ThisAddress, Int8PtrTy, Decl, VTableAuthMode::MustTrap); | ||
return RValue::get(VTablePointer); | ||
} | ||
|
||
case Builtin::BI__exception_code: | ||
case Builtin::BI_exception_code: | ||
return RValue::get(EmitSEHExceptionCode()); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1782,6 +1782,92 @@ static ExprResult PointerAuthStringDiscriminator(Sema &S, CallExpr *Call) { | |
return Call; | ||
} | ||
|
||
static ExprResult VirtualMemberAddress(Sema &S, CallExpr *Call) { | ||
if (S.checkArgCount(Call, 2)) | ||
return ExprError(); | ||
|
||
for (int i = 0; i < 2; ++i) { | ||
ExprResult ArgRValue = | ||
S.DefaultFunctionArrayLvalueConversion(Call->getArg(1)); | ||
if (ArgRValue.isInvalid()) | ||
return ExprError(); | ||
Call->setArg(1, ArgRValue.get()); | ||
} | ||
|
||
if (Call->getArg(0)->isTypeDependent() || Call->getArg(1)->isValueDependent()) | ||
return Call; | ||
|
||
const Expr *ThisArg = Call->getArg(0); | ||
QualType ThisTy = ThisArg->getType(); | ||
if (ThisTy->isPointerOrReferenceType()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll update this to make it warn on a reference parameter |
||
ThisTy = ThisTy->getPointeeType(); | ||
if (!ThisTy->getAsCXXRecordDecl()) { | ||
S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_lhs_cxxrec); | ||
return ExprError(); | ||
} | ||
|
||
const Expr *MemFunArg = Call->getArg(1); | ||
APValue Result; | ||
if (!MemFunArg->isCXX11ConstantExpr(S.getASTContext(), &Result, nullptr)) { | ||
S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof); | ||
return ExprError(); | ||
} | ||
|
||
if (!Result.isMemberPointer() || | ||
!isa<CXXMethodDecl>(Result.getMemberPointerDecl())) { | ||
S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof); | ||
return ExprError(); | ||
} | ||
|
||
const CXXMethodDecl *CXXMethod = | ||
cast<CXXMethodDecl>(Result.getMemberPointerDecl()); | ||
if (!CXXMethod->isVirtual()) { | ||
S.Diag(MemFunArg->getExprLoc(), diag::err_virtual_member_addrof); | ||
return ExprError(); | ||
} | ||
|
||
if (ThisTy->getAsCXXRecordDecl() != CXXMethod->getParent() && | ||
!S.IsDerivedFrom(Call->getBeginLoc(), ThisTy, | ||
CXXMethod->getFunctionObjectParameterType())) { | ||
S.Diag(ThisArg->getExprLoc(), diag::err_virtual_member_inherit); | ||
return ExprError(); | ||
} | ||
return Call; | ||
} | ||
|
||
static ExprResult GetVTablePointer(Sema &S, CallExpr *Call) { | ||
if (S.checkArgCount(Call, 1)) | ||
return ExprError(); | ||
ExprResult ThisArg = S.DefaultFunctionArrayLvalueConversion(Call->getArg(0)); | ||
if (ThisArg.isInvalid()) | ||
return ExprError(); | ||
Call->setArg(0, ThisArg.get()); | ||
const Expr *Subject = Call->getArg(0); | ||
QualType SubjectType = Subject->getType(); | ||
if (SubjectType->isPointerOrReferenceType()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will be reverting this to require a pointer arg |
||
SubjectType = SubjectType->getPointeeType(); | ||
const CXXRecordDecl *SubjectRecord = SubjectType->getAsCXXRecordDecl(); | ||
if (!SubjectRecord) { | ||
S.Diag(Subject->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type) | ||
<< 0 << Subject->getType(); | ||
return ExprError(); | ||
} | ||
if (S.RequireCompleteType( | ||
Subject->getBeginLoc(), SubjectType, | ||
diag::err_get_vtable_pointer_requires_complete_type)) { | ||
return ExprError(); | ||
} | ||
|
||
if (!SubjectRecord->isPolymorphic()) { | ||
S.Diag(Subject->getBeginLoc(), diag::err_get_vtable_pointer_incorrect_type) | ||
<< 1 << SubjectType; | ||
return ExprError(); | ||
} | ||
QualType ReturnType = S.Context.getPointerType(S.Context.VoidTy.withConst()); | ||
Call->setType(ReturnType); | ||
return Call; | ||
} | ||
|
||
static ExprResult BuiltinLaunder(Sema &S, CallExpr *TheCall) { | ||
if (S.checkArgCount(TheCall, 1)) | ||
return ExprError(); | ||
|
@@ -2625,6 +2711,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, | |
return PointerAuthAuthAndResign(*this, TheCall); | ||
case Builtin::BI__builtin_ptrauth_string_discriminator: | ||
return PointerAuthStringDiscriminator(*this, TheCall); | ||
|
||
case Builtin::BI__builtin_get_vtable_pointer: | ||
return GetVTablePointer(*this, TheCall); | ||
case Builtin::BI__builtin_virtual_member_address: | ||
return VirtualMemberAddress(*this, TheCall); | ||
|
||
// OpenCL v2.0, s6.13.16 - Pipe functions | ||
case Builtin::BIread_pipe: | ||
case Builtin::BIwrite_pipe: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we don't care about the return value of this call? I guess b/c we verify in
VirtualMemberAddress
, so we are using this for a side effect?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the isCXX11ConstantExpr gives us the target through ConstMemFun, but I'll see if there's a cleaner way to do this.