Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit cf50344

Browse files
committed
Alter the iOS/tvOS ARM64 C++ ABI to ignore the upper half of the
virtual table offset in a member function pointer. We are reserving this space for future ABI use relating to alternative v-table configurations. In the meantime, continue to zero-initialize this space when actually emitting a member pointer literal. This will successfully interoperate with existing compilers. Future versions of the compiler may place additional data in this location, and at that point, code emitted by compilers prior to this patch will fail if exposed to such a member pointer. This is therefore a somewhat hard ABI break. However, because it is limited to an uncommon case of an uncommon language feature, and especially because interoperation with the standard library does not depend on member pointers, we believe that with a sufficiently advance compiler change the impact of this break will be minimal in practice. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@281693 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 9e22199 commit cf50344

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

Diff for: lib/CodeGen/ItaniumCXXABI.cpp

+12-2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
4545
protected:
4646
bool UseARMMethodPtrABI;
4747
bool UseARMGuardVarABI;
48+
bool Use32BitVTableOffsetABI;
4849

4950
ItaniumMangleContext &getMangleContext() {
5051
return cast<ItaniumMangleContext>(CodeGen::CGCXXABI::getMangleContext());
@@ -55,7 +56,8 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
5556
bool UseARMMethodPtrABI = false,
5657
bool UseARMGuardVarABI = false) :
5758
CGCXXABI(CGM), UseARMMethodPtrABI(UseARMMethodPtrABI),
58-
UseARMGuardVarABI(UseARMGuardVarABI) { }
59+
UseARMGuardVarABI(UseARMGuardVarABI),
60+
Use32BitVTableOffsetABI(false) { }
5961

6062
bool classifyReturnType(CGFunctionInfo &FI) const override;
6163

@@ -425,7 +427,9 @@ class ARMCXXABI : public ItaniumCXXABI {
425427

426428
class iOS64CXXABI : public ARMCXXABI {
427429
public:
428-
iOS64CXXABI(CodeGen::CodeGenModule &CGM) : ARMCXXABI(CGM) {}
430+
iOS64CXXABI(CodeGen::CodeGenModule &CGM) : ARMCXXABI(CGM) {
431+
Use32BitVTableOffsetABI = true;
432+
}
429433

430434
// ARM64 libraries are prepared for non-unique RTTI.
431435
bool shouldRTTIBeUnique() const override { return false; }
@@ -579,9 +583,15 @@ llvm::Value *ItaniumCXXABI::EmitLoadOfMemberFunctionPointer(
579583
CGF.GetVTablePtr(Address(This, VTablePtrAlign), VTableTy, RD);
580584

581585
// Apply the offset.
586+
// On ARM64, to reserve extra space in virtual member function pointers,
587+
// we only pay attention to the low 32 bits of the offset.
582588
llvm::Value *VTableOffset = FnAsInt;
583589
if (!UseARMMethodPtrABI)
584590
VTableOffset = Builder.CreateSub(VTableOffset, ptrdiff_1);
591+
if (Use32BitVTableOffsetABI) {
592+
VTableOffset = Builder.CreateTrunc(VTableOffset, CGF.Int32Ty);
593+
VTableOffset = Builder.CreateZExt(VTableOffset, CGM.PtrDiffTy);
594+
}
585595
VTable = Builder.CreateGEP(VTable, VTableOffset);
586596

587597
// Load the virtual function to call.

Diff for: test/CodeGenCXX/arm64.cpp

+36
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,39 @@ namespace test2 {
8484
// CHECK-GLOBALS-DAG: @_ZTIN5test21EIiEE = weak_odr constant { {{.*}}, i8* inttoptr (i64 add (i64 ptrtoint ([14 x i8]* @_ZTSN5test21EIiEE to i64), i64 -9223372036854775808) to i8*) }
8585

8686
}
87+
88+
// ARM64 reserves the top half of the vtable offset in virtual
89+
// member pointers.
90+
namespace test3 {
91+
struct A {
92+
virtual void foo();
93+
virtual void bar();
94+
};
95+
96+
// The offset half of the pointer is still initialized to zero.
97+
// CHECK-GLOBALS-DAG: @_ZN5test34mptrE = global { i64, i64 } { i64 0, i64 1 }
98+
void (A::*mptr)() = &A::foo;
99+
100+
// CHECK-LABEL: define void @_ZN5test34testEv()
101+
// CHECK: [[TEMP:%.*]] = alloca [[A:.*]], align 8
102+
// CHECK: [[MEMPTR:%.*]] = load { i64, i64 }, { i64, i64 }* @_ZN5test34mptrE, align 8
103+
// CHECK: [[ADJUST_AND_IS_VIRTUAL:%.*]] = extractvalue { i64, i64 } [[MEMPTR]], 1
104+
// CHECK: [[ADJUST:%.*]] = ashr i64 [[ADJUST_AND_IS_VIRTUAL]], 1
105+
// CHECK: [[T0:%.*]] = bitcast [[A]]* [[TEMP]] to i8*
106+
// CHECK: [[T1:%.*]] = getelementptr inbounds i8, i8* [[T0]], i64 [[ADJUST]]
107+
// CHECK: [[ADJUSTED:%.*]] = bitcast i8* [[T1]] to [[A]]*
108+
// CHECK: [[MEMBER:%.*]] = extractvalue { i64, i64 } [[MEMPTR]], 0
109+
// CHECK: [[T0:%.*]] = and i64 [[ADJUST_AND_IS_VIRTUAL]], 1
110+
// CHECK: [[IS_VIRTUAL:%.*]] = icmp ne i64 [[T0]], 0
111+
// CHECK: br i1 [[IS_VIRTUAL]],
112+
// CHECK: [[T0:%.*]] = bitcast [[A]]* [[ADJUSTED]] to i8**
113+
// CHECK: [[VPTR:%.*]] = load i8*, i8** [[T0]], align 8
114+
// CHECK: [[TRUNC:%.*]] = trunc i64 [[MEMBER]] to i32
115+
// CHECK: [[ZEXT:%.*]] = zext i32 [[TRUNC]] to i64
116+
// CHECK: [[T0:%.*]] = getelementptr i8, i8* [[VPTR]], i64 [[ZEXT]]
117+
// CHECK: [[T1:%.*]] = bitcast i8* [[T0]] to void ([[A]]*)**
118+
// CHECK: load void ([[A]]*)*, void ([[A]]*)** [[T1]],
119+
void test() {
120+
(A().*mptr)();
121+
}
122+
}

0 commit comments

Comments
 (0)