forked from OSchip/llvm-project
Abstract out member-pointer creation. I'm really unhappy about the current
duplication between the constant and non-constant paths in all of this. Implement ARM ABI semantics for member pointer constants and conversion. llvm-svn: 111772
This commit is contained in:
parent
84fa510aa9
commit
1c456c89dc
|
@ -287,44 +287,6 @@ CodeGenModule::GetAddrOfCXXDestructor(const CXXDestructorDecl *D,
|
||||||
return cast<llvm::Function>(GetOrCreateLLVMFunction(Name, FTy, GD));
|
return cast<llvm::Function>(GetOrCreateLLVMFunction(Name, FTy, GD));
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Constant *
|
|
||||||
CodeGenModule::GetCXXMemberFunctionPointerValue(const CXXMethodDecl *MD) {
|
|
||||||
assert(MD->isInstance() && "Member function must not be static!");
|
|
||||||
|
|
||||||
MD = MD->getCanonicalDecl();
|
|
||||||
|
|
||||||
const llvm::Type *PtrDiffTy = Types.ConvertType(Context.getPointerDiffType());
|
|
||||||
|
|
||||||
// Get the function pointer (or index if this is a virtual function).
|
|
||||||
if (MD->isVirtual()) {
|
|
||||||
uint64_t Index = VTables.getMethodVTableIndex(MD);
|
|
||||||
|
|
||||||
// FIXME: We shouldn't use / 8 here.
|
|
||||||
uint64_t PointerWidthInBytes = Context.Target.getPointerWidth(0) / 8;
|
|
||||||
|
|
||||||
// Itanium C++ ABI 2.3:
|
|
||||||
// For a non-virtual function, this field is a simple function pointer.
|
|
||||||
// For a virtual function, it is 1 plus the virtual table offset
|
|
||||||
// (in bytes) of the function, represented as a ptrdiff_t.
|
|
||||||
return llvm::ConstantInt::get(PtrDiffTy, (Index * PointerWidthInBytes) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
|
|
||||||
const llvm::Type *Ty;
|
|
||||||
// Check whether the function has a computable LLVM signature.
|
|
||||||
if (!CodeGenTypes::VerifyFuncTypeComplete(FPT)) {
|
|
||||||
// The function has a computable LLVM signature; use the correct type.
|
|
||||||
Ty = Types.GetFunctionType(Types.getFunctionInfo(MD), FPT->isVariadic());
|
|
||||||
} else {
|
|
||||||
// Use an arbitrary non-function type to tell GetAddrOfFunction that the
|
|
||||||
// function type is incomplete.
|
|
||||||
Ty = PtrDiffTy;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::Constant *FuncPtr = GetAddrOfFunction(MD, Ty);
|
|
||||||
return llvm::ConstantExpr::getPtrToInt(FuncPtr, PtrDiffTy);
|
|
||||||
}
|
|
||||||
|
|
||||||
static llvm::Value *BuildVirtualCall(CodeGenFunction &CGF, uint64_t VTableIndex,
|
static llvm::Value *BuildVirtualCall(CodeGenFunction &CGF, uint64_t VTableIndex,
|
||||||
llvm::Value *This, const llvm::Type *Ty) {
|
llvm::Value *This, const llvm::Type *Ty) {
|
||||||
Ty = Ty->getPointerTo()->getPointerTo()->getPointerTo();
|
Ty = Ty->getPointerTo()->getPointerTo()->getPointerTo();
|
||||||
|
@ -400,6 +362,13 @@ void CGCXXABI::EmitNullMemberFunctionPointer(CodeGenFunction &CGF,
|
||||||
ErrorUnsupportedABI(CGF, "null member function pointers");
|
ErrorUnsupportedABI(CGF, "null member function pointers");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CGCXXABI::EmitMemberFunctionPointer(CodeGenFunction &CGF,
|
||||||
|
const CXXMethodDecl *MD,
|
||||||
|
llvm::Value *DestPtr,
|
||||||
|
bool VolatileDest) {
|
||||||
|
ErrorUnsupportedABI(CGF, "member function pointers");
|
||||||
|
}
|
||||||
|
|
||||||
llvm::Constant *
|
llvm::Constant *
|
||||||
CGCXXABI::EmitMemberFunctionPointerConversion(llvm::Constant *C,
|
CGCXXABI::EmitMemberFunctionPointerConversion(llvm::Constant *C,
|
||||||
const CastExpr *E) {
|
const CastExpr *E) {
|
||||||
|
@ -411,6 +380,10 @@ CGCXXABI::EmitNullMemberFunctionPointer(const MemberPointerType *MPT) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::Constant *CGCXXABI::EmitMemberFunctionPointer(const CXXMethodDecl *MD) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool CGCXXABI::RequiresNonZeroInitializer(QualType T) {
|
bool CGCXXABI::RequiresNonZeroInitializer(QualType T) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ namespace llvm {
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
class CastExpr;
|
class CastExpr;
|
||||||
|
class CXXMethodDecl;
|
||||||
class CXXRecordDecl;
|
class CXXRecordDecl;
|
||||||
class MemberPointerType;
|
class MemberPointerType;
|
||||||
class QualType;
|
class QualType;
|
||||||
|
@ -70,6 +71,12 @@ public:
|
||||||
|
|
||||||
virtual llvm::Constant *
|
virtual llvm::Constant *
|
||||||
EmitNullMemberFunctionPointer(const MemberPointerType *MPT);
|
EmitNullMemberFunctionPointer(const MemberPointerType *MPT);
|
||||||
|
|
||||||
|
virtual llvm::Constant *EmitMemberFunctionPointer(const CXXMethodDecl *MD);
|
||||||
|
virtual void EmitMemberFunctionPointer(CodeGenFunction &CGF,
|
||||||
|
const CXXMethodDecl *MD,
|
||||||
|
llvm::Value *DestPtr,
|
||||||
|
bool VolatileDest);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Creates an instance of a C++ ABI class.
|
/// Creates an instance of a C++ ABI class.
|
||||||
|
|
|
@ -364,9 +364,8 @@ void AggExprEmitter::VisitBinComma(const BinaryOperator *E) {
|
||||||
|
|
||||||
void AggExprEmitter::VisitUnaryAddrOf(const UnaryOperator *E) {
|
void AggExprEmitter::VisitUnaryAddrOf(const UnaryOperator *E) {
|
||||||
// We have a member function pointer.
|
// We have a member function pointer.
|
||||||
const MemberPointerType *MPT = E->getType()->getAs<MemberPointerType>();
|
assert(E->getType()->getAs<MemberPointerType>()
|
||||||
(void) MPT;
|
->getPointeeType()->isFunctionProtoType() &&
|
||||||
assert(MPT->getPointeeType()->isFunctionProtoType() &&
|
|
||||||
"Unexpected member pointer type!");
|
"Unexpected member pointer type!");
|
||||||
|
|
||||||
// The creation of member function pointers has no side effects; if
|
// The creation of member function pointers has no side effects; if
|
||||||
|
@ -375,20 +374,9 @@ void AggExprEmitter::VisitUnaryAddrOf(const UnaryOperator *E) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const DeclRefExpr *DRE = cast<DeclRefExpr>(E->getSubExpr());
|
const DeclRefExpr *DRE = cast<DeclRefExpr>(E->getSubExpr());
|
||||||
const CXXMethodDecl *MD =
|
const CXXMethodDecl *MD = cast<CXXMethodDecl>(DRE->getDecl());
|
||||||
cast<CXXMethodDecl>(DRE->getDecl())->getCanonicalDecl();
|
|
||||||
|
|
||||||
const llvm::Type *PtrDiffTy =
|
CGF.CGM.getCXXABI().EmitMemberFunctionPointer(CGF, MD, DestPtr, VolatileDest);
|
||||||
CGF.ConvertType(CGF.getContext().getPointerDiffType());
|
|
||||||
|
|
||||||
llvm::Value *DstPtr = Builder.CreateStructGEP(DestPtr, 0, "dst.ptr");
|
|
||||||
llvm::Value *FuncPtr = CGF.CGM.GetCXXMemberFunctionPointerValue(MD);
|
|
||||||
Builder.CreateStore(FuncPtr, DstPtr, VolatileDest);
|
|
||||||
|
|
||||||
llvm::Value *AdjPtr = Builder.CreateStructGEP(DestPtr, 1, "dst.adj");
|
|
||||||
// The adjustment will always be 0.
|
|
||||||
Builder.CreateStore(llvm::ConstantInt::get(PtrDiffTy, 0), AdjPtr,
|
|
||||||
VolatileDest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AggExprEmitter::VisitStmtExpr(const StmtExpr *E) {
|
void AggExprEmitter::VisitStmtExpr(const StmtExpr *E) {
|
||||||
|
|
|
@ -455,22 +455,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Constant *EmitMemberFunctionPointer(CXXMethodDecl *MD) {
|
llvm::Constant *EmitMemberFunctionPointer(CXXMethodDecl *MD) {
|
||||||
assert(MD->isInstance() && "Member function must not be static!");
|
return CGM.getCXXABI().EmitMemberFunctionPointer(MD);
|
||||||
|
|
||||||
MD = MD->getCanonicalDecl();
|
|
||||||
|
|
||||||
const llvm::Type *PtrDiffTy =
|
|
||||||
CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType());
|
|
||||||
|
|
||||||
llvm::Constant *Values[2];
|
|
||||||
|
|
||||||
Values[0] = CGM.GetCXXMemberFunctionPointerValue(MD);
|
|
||||||
|
|
||||||
// The adjustment will always be 0.
|
|
||||||
Values[1] = llvm::ConstantInt::get(PtrDiffTy, 0);
|
|
||||||
|
|
||||||
return llvm::ConstantStruct::get(CGM.getLLVMContext(),
|
|
||||||
Values, 2, /*Packed=*/false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Constant *VisitUnaryAddrOf(UnaryOperator *E) {
|
llvm::Constant *VisitUnaryAddrOf(UnaryOperator *E) {
|
||||||
|
|
|
@ -371,10 +371,6 @@ public:
|
||||||
llvm::GlobalValue *GetAddrOfCXXDestructor(const CXXDestructorDecl *D,
|
llvm::GlobalValue *GetAddrOfCXXDestructor(const CXXDestructorDecl *D,
|
||||||
CXXDtorType Type);
|
CXXDtorType Type);
|
||||||
|
|
||||||
// GetCXXMemberFunctionPointerValue - Given a method declaration, return the
|
|
||||||
// integer used in a member function pointer to refer to that value.
|
|
||||||
llvm::Constant *GetCXXMemberFunctionPointerValue(const CXXMethodDecl *MD);
|
|
||||||
|
|
||||||
/// getBuiltinLibFunction - Given a builtin id for a function like
|
/// getBuiltinLibFunction - Given a builtin id for a function like
|
||||||
/// "__builtin_fabsf", return a Function* for "fabsf".
|
/// "__builtin_fabsf", return a Function* for "fabsf".
|
||||||
llvm::Value *getBuiltinLibFunction(const FunctionDecl *FD,
|
llvm::Value *getBuiltinLibFunction(const FunctionDecl *FD,
|
||||||
|
|
|
@ -66,6 +66,16 @@ public:
|
||||||
|
|
||||||
llvm::Constant *EmitNullMemberFunctionPointer(const MemberPointerType *MPT);
|
llvm::Constant *EmitNullMemberFunctionPointer(const MemberPointerType *MPT);
|
||||||
|
|
||||||
|
void EmitMemberFunctionPointer(CodeGenFunction &CGF,
|
||||||
|
const CXXMethodDecl *MD,
|
||||||
|
llvm::Value *Dest,
|
||||||
|
bool VolatileDest);
|
||||||
|
|
||||||
|
llvm::Constant *EmitMemberFunctionPointer(const CXXMethodDecl *MD);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void GetMemberFunctionPointer(const CXXMethodDecl *MD,
|
||||||
|
llvm::Constant *(&Array)[2]);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ARMCXXABI : public ItaniumCXXABI {
|
class ARMCXXABI : public ItaniumCXXABI {
|
||||||
|
@ -82,6 +92,62 @@ CodeGen::CGCXXABI *CodeGen::CreateARMCXXABI(CodeGenModule &CGM) {
|
||||||
return new ARMCXXABI(CGM);
|
return new ARMCXXABI(CGM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ItaniumCXXABI::GetMemberFunctionPointer(const CXXMethodDecl *MD,
|
||||||
|
llvm::Constant *(&MemPtr)[2]) {
|
||||||
|
assert(MD->isInstance() && "Member function must not be static!");
|
||||||
|
|
||||||
|
MD = MD->getCanonicalDecl();
|
||||||
|
|
||||||
|
CodeGenTypes &Types = CGM.getTypes();
|
||||||
|
const llvm::Type *ptrdiff_t =
|
||||||
|
Types.ConvertType(CGM.getContext().getPointerDiffType());
|
||||||
|
|
||||||
|
// Get the function pointer (or index if this is a virtual function).
|
||||||
|
if (MD->isVirtual()) {
|
||||||
|
uint64_t Index = CGM.getVTables().getMethodVTableIndex(MD);
|
||||||
|
|
||||||
|
// FIXME: We shouldn't use / 8 here.
|
||||||
|
uint64_t PointerWidthInBytes =
|
||||||
|
CGM.getContext().Target.getPointerWidth(0) / 8;
|
||||||
|
uint64_t VTableOffset = (Index * PointerWidthInBytes);
|
||||||
|
|
||||||
|
if (IsARM) {
|
||||||
|
// ARM C++ ABI 3.2.1:
|
||||||
|
// This ABI specifies that adj contains twice the this
|
||||||
|
// adjustment, plus 1 if the member function is virtual. The
|
||||||
|
// least significant bit of adj then makes exactly the same
|
||||||
|
// discrimination as the least significant bit of ptr does for
|
||||||
|
// Itanium.
|
||||||
|
MemPtr[0] = llvm::ConstantInt::get(ptrdiff_t, VTableOffset);
|
||||||
|
MemPtr[1] = llvm::ConstantInt::get(ptrdiff_t, 1);
|
||||||
|
} else {
|
||||||
|
// Itanium C++ ABI 2.3:
|
||||||
|
// For a virtual function, [the pointer field] is 1 plus the
|
||||||
|
// virtual table offset (in bytes) of the function,
|
||||||
|
// represented as a ptrdiff_t.
|
||||||
|
MemPtr[0] = llvm::ConstantInt::get(ptrdiff_t, VTableOffset + 1);
|
||||||
|
MemPtr[1] = llvm::ConstantInt::get(ptrdiff_t, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
|
||||||
|
const llvm::Type *Ty;
|
||||||
|
// Check whether the function has a computable LLVM signature.
|
||||||
|
if (!CodeGenTypes::VerifyFuncTypeComplete(FPT)) {
|
||||||
|
// The function has a computable LLVM signature; use the correct type.
|
||||||
|
Ty = Types.GetFunctionType(Types.getFunctionInfo(MD), FPT->isVariadic());
|
||||||
|
} else {
|
||||||
|
// Use an arbitrary non-function type to tell GetAddrOfFunction that the
|
||||||
|
// function type is incomplete.
|
||||||
|
Ty = ptrdiff_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Constant *Addr = CGM.GetAddrOfFunction(MD, Ty);
|
||||||
|
MemPtr[0] = llvm::ConstantExpr::getPtrToInt(Addr, ptrdiff_t);
|
||||||
|
MemPtr[1] = llvm::ConstantInt::get(ptrdiff_t, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// In the Itanium and ARM ABIs, method pointers have the form:
|
/// In the Itanium and ARM ABIs, method pointers have the form:
|
||||||
/// struct { ptrdiff_t ptr; ptrdiff_t adj; } memptr;
|
/// struct { ptrdiff_t ptr; ptrdiff_t adj; } memptr;
|
||||||
///
|
///
|
||||||
|
@ -231,6 +297,13 @@ void ItaniumCXXABI::EmitMemberFunctionPointerConversion(CodeGenFunction &CGF,
|
||||||
CGF.CGM.GetNonVirtualBaseClassOffset(DerivedDecl,
|
CGF.CGM.GetNonVirtualBaseClassOffset(DerivedDecl,
|
||||||
E->path_begin(),
|
E->path_begin(),
|
||||||
E->path_end())) {
|
E->path_end())) {
|
||||||
|
// The this-adjustment is left-shifted by 1 on ARM.
|
||||||
|
if (IsARM) {
|
||||||
|
uint64_t Offset = cast<llvm::ConstantInt>(Adj)->getZExtValue();
|
||||||
|
Offset <<= 1;
|
||||||
|
Adj = llvm::ConstantInt::get(Adj->getType(), Offset);
|
||||||
|
}
|
||||||
|
|
||||||
if (DerivedToBase)
|
if (DerivedToBase)
|
||||||
SrcAdj = Builder.CreateSub(SrcAdj, Adj, "adj");
|
SrcAdj = Builder.CreateSub(SrcAdj, Adj, "adj");
|
||||||
else
|
else
|
||||||
|
@ -265,6 +338,13 @@ ItaniumCXXABI::EmitMemberFunctionPointerConversion(llvm::Constant *C,
|
||||||
// If there's no offset, we're done.
|
// If there's no offset, we're done.
|
||||||
if (!Offset) return C;
|
if (!Offset) return C;
|
||||||
|
|
||||||
|
// The this-adjustment is left-shifted by 1 on ARM.
|
||||||
|
if (IsARM) {
|
||||||
|
uint64_t OffsetV = cast<llvm::ConstantInt>(Offset)->getZExtValue();
|
||||||
|
OffsetV <<= 1;
|
||||||
|
Offset = llvm::ConstantInt::get(Offset->getType(), OffsetV);
|
||||||
|
}
|
||||||
|
|
||||||
llvm::ConstantStruct *CS = cast<llvm::ConstantStruct>(C);
|
llvm::ConstantStruct *CS = cast<llvm::ConstantStruct>(C);
|
||||||
|
|
||||||
llvm::Constant *Values[2] = {
|
llvm::Constant *Values[2] = {
|
||||||
|
@ -300,6 +380,32 @@ ItaniumCXXABI::EmitNullMemberFunctionPointer(const MemberPointerType *MPT) {
|
||||||
return CGM.EmitNullConstant(QualType(MPT, 0));
|
return CGM.EmitNullConstant(QualType(MPT, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::Constant *
|
||||||
|
ItaniumCXXABI::EmitMemberFunctionPointer(const CXXMethodDecl *MD) {
|
||||||
|
llvm::Constant *Values[2];
|
||||||
|
GetMemberFunctionPointer(MD, Values);
|
||||||
|
|
||||||
|
return llvm::ConstantStruct::get(CGM.getLLVMContext(),
|
||||||
|
Values, 2, /*Packed=*/false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ItaniumCXXABI::EmitMemberFunctionPointer(CodeGenFunction &CGF,
|
||||||
|
const CXXMethodDecl *MD,
|
||||||
|
llvm::Value *DestPtr,
|
||||||
|
bool VolatileDest) {
|
||||||
|
llvm::Constant *Values[2];
|
||||||
|
GetMemberFunctionPointer(MD, Values);
|
||||||
|
|
||||||
|
CGBuilderTy &Builder = CGF.Builder;
|
||||||
|
|
||||||
|
llvm::Value *DstPtr = Builder.CreateStructGEP(DestPtr, 0, "memptr.ptr");
|
||||||
|
Builder.CreateStore(Values[0], DstPtr, VolatileDest);
|
||||||
|
|
||||||
|
llvm::Value *AdjPtr = Builder.CreateStructGEP(DestPtr, 1, "memptr.adj");
|
||||||
|
Builder.CreateStore(Values[1], AdjPtr, VolatileDest);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ItaniumCXXABI::RequiresNonZeroInitializer(QualType T) {
|
bool ItaniumCXXABI::RequiresNonZeroInitializer(QualType T) {
|
||||||
return CGM.getTypes().ContainsPointerToDataMember(T);
|
return CGM.getTypes().ContainsPointerToDataMember(T);
|
||||||
}
|
}
|
||||||
|
|
|
@ -484,7 +484,7 @@ ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename,
|
||||||
// FIXME: This is broken, we should store the TargetOptions in the AST file.
|
// FIXME: This is broken, we should store the TargetOptions in the AST file.
|
||||||
TargetOptions TargetOpts;
|
TargetOptions TargetOpts;
|
||||||
TargetOpts.ABI = "";
|
TargetOpts.ABI = "";
|
||||||
TargetOpts.CXXABI = "itanium";
|
TargetOpts.CXXABI = "";
|
||||||
TargetOpts.CPU = "";
|
TargetOpts.CPU = "";
|
||||||
TargetOpts.Features.clear();
|
TargetOpts.Features.clear();
|
||||||
TargetOpts.Triple = TargetTriple;
|
TargetOpts.Triple = TargetTriple;
|
||||||
|
|
|
@ -713,8 +713,10 @@ static void TargetOptsToArgs(const TargetOptions &Opts,
|
||||||
Res.push_back("-target-linker-version");
|
Res.push_back("-target-linker-version");
|
||||||
Res.push_back(Opts.LinkerVersion);
|
Res.push_back(Opts.LinkerVersion);
|
||||||
}
|
}
|
||||||
Res.push_back("-cxx-abi");
|
if (!Opts.CXXABI.empty()) {
|
||||||
Res.push_back(Opts.CXXABI);
|
Res.push_back("-cxx-abi");
|
||||||
|
Res.push_back(Opts.CXXABI);
|
||||||
|
}
|
||||||
for (unsigned i = 0, e = Opts.Features.size(); i != e; ++i) {
|
for (unsigned i = 0, e = Opts.Features.size(); i != e; ++i) {
|
||||||
Res.push_back("-target-feature");
|
Res.push_back("-target-feature");
|
||||||
Res.push_back(Opts.Features[i]);
|
Res.push_back(Opts.Features[i]);
|
||||||
|
@ -1471,10 +1473,6 @@ static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args) {
|
||||||
// Use the host triple if unspecified.
|
// Use the host triple if unspecified.
|
||||||
if (Opts.Triple.empty())
|
if (Opts.Triple.empty())
|
||||||
Opts.Triple = llvm::sys::getHostTriple();
|
Opts.Triple = llvm::sys::getHostTriple();
|
||||||
|
|
||||||
// Use the Itanium C++ ABI if unspecified.
|
|
||||||
if (Opts.CXXABI.empty())
|
|
||||||
Opts.CXXABI = "itanium";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-apple-darwin9 | FileCheck %s
|
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-apple-darwin9 | FileCheck %s
|
||||||
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=i386-apple-darwin9 | FileCheck -check-prefix LP32 %s
|
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=i386-apple-darwin9 | FileCheck -check-prefix LP32 %s
|
||||||
|
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=armv7-unknown-unknown | FileCheck -check-prefix ARM %s
|
||||||
|
|
||||||
struct A { int a; void f(); virtual void vf1(); virtual void vf2(); };
|
struct A { int a; void f(); virtual void vf1(); virtual void vf2(); };
|
||||||
struct B { int b; virtual void g(); };
|
struct B { int b; virtual void g(); };
|
||||||
|
@ -190,3 +191,22 @@ namespace PR7027 {
|
||||||
struct X { void test( ); };
|
struct X { void test( ); };
|
||||||
void testX() { &X::test; }
|
void testX() { &X::test; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace test7 {
|
||||||
|
struct A { void foo(); virtual void vfoo(); };
|
||||||
|
struct B { void foo(); virtual void vfoo(); };
|
||||||
|
struct C : A, B { void foo(); virtual void vfoo(); };
|
||||||
|
|
||||||
|
// CHECK-ARM: @_ZN5test74ptr0E = global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test71A3fooEv to i32), i32 0 }
|
||||||
|
// CHECK-ARM: @_ZN5test74ptr1E = global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test71B3fooEv to i32), i32 8 }
|
||||||
|
// CHECK-ARM: @_ZN5test74ptr2E = global {{.*}} { i32 ptrtoint ({{.*}}* @_ZN5test71C3fooEv to i32), i32 0 }
|
||||||
|
// CHECK-ARM: @_ZN5test74ptr3E = global {{.*}} { i32 0, i32 1 }
|
||||||
|
// CHECK-ARM: @_ZN5test74ptr4E = global {{.*}} { i32 0, i32 9 }
|
||||||
|
// CHECK-ARM: @_ZN5test74ptr5E = global {{.*}} { i32 0, i32 1 }
|
||||||
|
void (C::*ptr0)() = &A::foo;
|
||||||
|
void (C::*ptr1)() = &B::foo;
|
||||||
|
void (C::*ptr2)() = &C::foo;
|
||||||
|
void (C::*ptr3)() = &A::vfoo;
|
||||||
|
void (C::*ptr4)() = &B::vfoo;
|
||||||
|
void (C::*ptr5)() = &C::vfoo;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue