Skip to content

Commit 78221e8

Browse files
committed
[llvm] Ensure propagated constants in the vtable are aligned
1 parent 58b9b86 commit 78221e8

File tree

7 files changed

+121
-83
lines changed

7 files changed

+121
-83
lines changed

llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,9 @@ wholeprogramdevirt::findLowestOffset(ArrayRef<VirtualCallTarget> Targets,
298298
++Byte;
299299
}
300300
}
301-
return (MinByte + I) * 8;
301+
// Rounding up ensures the constant is always stored at address we
302+
// can directly load from without misalignment.
303+
return alignTo((MinByte + I) * 8, Size);
302304
NextI:;
303305
}
304306
}
@@ -1834,9 +1836,19 @@ bool DevirtModule::tryVirtualConstProp(
18341836
if (!RetType)
18351837
return false;
18361838
unsigned BitWidth = RetType->getBitWidth();
1839+
1840+
// TODO: Since we can evaluated these constants at compile-time, we can save
1841+
// some space by calculating the smallest range of values that all these
1842+
// constants can fit in, then only allocate enough space to fit those values.
1843+
// At each callsite, we can get the original type by doing a sign/zero
1844+
// extension. For example, if we would store an i64, but we can see that all
1845+
// the values fit into an i16, then we can store an i16 before/after the
1846+
// vtable and at each callsite do a s/zext.
18371847
if (BitWidth > 64)
18381848
return false;
18391849

1850+
Align TypeAlignment = M.getDataLayout().getPrefTypeAlign(RetType);
1851+
18401852
// Make sure that each function is defined, does not access memory, takes at
18411853
// least one argument, does not use its first argument (which we assume is
18421854
// 'this'), and has the same return type.
@@ -1861,6 +1873,18 @@ bool DevirtModule::tryVirtualConstProp(
18611873
Fn->arg_empty() || !Fn->arg_begin()->use_empty() ||
18621874
Fn->getReturnType() != RetType)
18631875
return false;
1876+
1877+
// This only works if the integer size is at most the alignment of the
1878+
// vtable. If the table is underaligned, then we can't guarantee that the
1879+
// constant will always be aligned to the integer type alignment. For
1880+
// example, if the table is `align 1`, we can never guarantee that an i32
1881+
// stored before/after the vtable is 32-bit aligned without changing the
1882+
// alignment of the new global.
1883+
GlobalVariable *GV = Target.TM->Bits->GV;
1884+
Align TableAlignment = M.getDataLayout().getValueOrABITypeAlignment(
1885+
GV->getAlign(), GV->getValueType());
1886+
if (TypeAlignment > TableAlignment)
1887+
return false;
18641888
}
18651889

18661890
for (auto &&CSByConstantArg : SlotInfo.ConstCSInfo) {
@@ -1880,6 +1904,9 @@ bool DevirtModule::tryVirtualConstProp(
18801904

18811905
// Find an allocation offset in bits in all vtables associated with the
18821906
// type.
1907+
// TODO: If there would be "holes" in the vtable that were added by
1908+
// padding, we could place i1s there to reduce any extra padding that
1909+
// would be introduced by the i1s.
18831910
uint64_t AllocBefore =
18841911
findLowestOffset(TargetsForSlot, /*IsAfter=*/false, BitWidth);
18851912
uint64_t AllocAfter =
@@ -1911,6 +1938,14 @@ bool DevirtModule::tryVirtualConstProp(
19111938
setAfterReturnValues(TargetsForSlot, AllocAfter, BitWidth, OffsetByte,
19121939
OffsetBit);
19131940

1941+
// In an earlier check we forbade constant propagation from operating on
1942+
// tables whose alignment is less than the alignment needed for loading
1943+
// the constant. Thus, the address we take the offset from will always be
1944+
// aligned to at least this integer alignment. Now, we need to ensure that
1945+
// the offset is also aligned to this integer alignment to ensure we always
1946+
// have an aligned load.
1947+
assert(OffsetByte % TypeAlignment.value() == 0);
1948+
19141949
if (RemarksEnabled || AreStatisticsEnabled())
19151950
for (auto &&Target : TargetsForSlot)
19161951
Target.WasDevirt = true;

llvm/test/Transforms/WholeProgramDevirt/virtual-const-prop-begin.ll

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,28 @@ target datalayout = "e-p:64:64"
99
;; preserve alignment. Making them i16s allows them to stay at the beginning of
1010
;; the vtable. There are other tests where there's a mix of constants before and
1111
;; after the vtable but for this file we just want everything before the vtable.
12-
; CHECK: [[VT1DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [0 x i8] } { [8 x i8] c"\00\00\00\00\00\03\00\02", [3 x ptr] [ptr @vf0i1, ptr @vf1i1, ptr @vf1i16], [0 x i8] zeroinitializer }, section "vt1sec", !type [[T8:![0-9]+]]
12+
; CHECK: [[VT1DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [0 x i8] } { [8 x i8] c"\00\00\00\00\03\00\00\02", [3 x ptr] [ptr @vf0i1, ptr @vf1i1, ptr @vf1i16], [0 x i8] zeroinitializer }, section "vt1sec", !type [[T8:![0-9]+]]
1313
@vt1 = constant [3 x ptr] [
1414
ptr @vf0i1,
1515
ptr @vf1i1,
1616
ptr @vf1i16
1717
], section "vt1sec", !type !0
1818

19-
; CHECK: [[VT2DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [0 x i8] } { [8 x i8] c"\00\00\00\00\00\04\00\01", [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf2i16], [0 x i8] zeroinitializer }, !type [[T8]]
19+
; CHECK: [[VT2DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [0 x i8] } { [8 x i8] c"\00\00\00\00\04\00\00\01", [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf2i16], [0 x i8] zeroinitializer }, !type [[T8]]
2020
@vt2 = constant [3 x ptr] [
2121
ptr @vf1i1,
2222
ptr @vf0i1,
2323
ptr @vf2i16
2424
], !type !0
2525

26-
; CHECK: [[VT3DATA:@[^ ]*]] = private constant { [4 x i8], [3 x ptr], [0 x i8] } { [4 x i8] c"\00\05\00\02", [3 x ptr] [ptr @vf0i1, ptr @vf1i1, ptr @vf3i16], [0 x i8] zeroinitializer }, align 2, !type [[T5:![0-9]+]]
26+
; CHECK: [[VT3DATA:@[^ ]*]] = private constant { [4 x i8], [3 x ptr], [0 x i8] } { [4 x i8] c"\05\00\00\02", [3 x ptr] [ptr @vf0i1, ptr @vf1i1, ptr @vf3i16], [0 x i8] zeroinitializer }, align 2, !type [[T5:![0-9]+]]
2727
@vt3 = constant [3 x ptr] [
2828
ptr @vf0i1,
2929
ptr @vf1i1,
3030
ptr @vf3i16
3131
], align 2, !type !0
3232

33-
; CHECK: [[VT4DATA:@[^ ]*]] = private constant { [16 x i8], [3 x ptr], [0 x i8] } { [16 x i8] c"\00\00\00\00\00\00\00\00\00\00\00\00\00\06\00\01", [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf4i16], [0 x i8] zeroinitializer }, align 16, !type [[T16:![0-9]+]]
33+
; CHECK: [[VT4DATA:@[^ ]*]] = private constant { [16 x i8], [3 x ptr], [0 x i8] } { [16 x i8] c"\00\00\00\00\00\00\00\00\00\00\00\00\06\00\00\01", [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf4i16], [0 x i8] zeroinitializer }, align 16, !type [[T16:![0-9]+]]
3434
@vt4 = constant [3 x ptr] [
3535
ptr @vf1i1,
3636
ptr @vf0i1,
@@ -136,7 +136,7 @@ define i16 @call3(ptr %obj) {
136136
call void @llvm.assume(i1 %p)
137137
%fptrptr = getelementptr [3 x ptr], ptr %vtable, i16 0, i16 2
138138
%fptr = load ptr, ptr %fptrptr
139-
; CHECK: [[VTGEP3:%[^ ]*]] = getelementptr i8, ptr %vtable, i32 -3
139+
; CHECK: [[VTGEP3:%[^ ]*]] = getelementptr i8, ptr %vtable, i32 -4
140140
; CHECK: [[VTLOAD3:%[^ ]*]] = load i16, ptr [[VTGEP3]]
141141
%result = call i16 %fptr(ptr %obj)
142142
; CHECK: ret i16 [[VTLOAD3]]

llvm/test/Transforms/WholeProgramDevirt/virtual-const-prop-check.ll

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,28 +37,28 @@ target triple = "x86_64-unknown-linux-gnu"
3737

3838
; SKIP-ALL-NOT: devirtualized
3939

40-
; CHECK: [[VT1DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [0 x i8] } { [8 x i8] c"\00\00\00\01\00\00\00\02", [3 x ptr] [ptr @vf0i1, ptr @vf1i1, ptr @vf1i32], [0 x i8] zeroinitializer }, section "vt1sec", !type [[T8:![0-9]+]]
40+
; CHECK: [[VT1DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [4 x i8] } { [8 x i8] c"\00\00\00\00\00\00\00\02", [3 x ptr] [ptr @vf0i1, ptr @vf1i1, ptr @vf1i32], [4 x i8] c"\01\00\00\00" }, section "vt1sec", !type [[T8:![0-9]+]]
4141
@vt1 = constant [3 x ptr] [
4242
ptr @vf0i1,
4343
ptr @vf1i1,
4444
ptr @vf1i32
4545
], section "vt1sec", !type !0
4646

47-
; CHECK: [[VT2DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [0 x i8] } { [8 x i8] c"\00\00\00\02\00\00\00\01", [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf2i32], [0 x i8] zeroinitializer }, !type [[T8]]
47+
; CHECK: [[VT2DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [4 x i8] } { [8 x i8] c"\00\00\00\00\00\00\00\01", [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf2i32], [4 x i8] c"\02\00\00\00" }, !type [[T8]]
4848
@vt2 = constant [3 x ptr] [
4949
ptr @vf1i1,
5050
ptr @vf0i1,
5151
ptr @vf2i32
5252
], !type !0
5353

54-
; CHECK: [[VT3DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [0 x i8] } { [8 x i8] c"\00\00\00\03\00\00\00\02", [3 x ptr] [ptr @vf0i1, ptr @vf1i1, ptr @vf3i32], [0 x i8] zeroinitializer }, !type [[T8]]
54+
; CHECK: [[VT3DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [4 x i8] } { [8 x i8] c"\00\00\00\00\00\00\00\02", [3 x ptr] [ptr @vf0i1, ptr @vf1i1, ptr @vf3i32], [4 x i8] c"\03\00\00\00" }, !type [[T8]]
5555
@vt3 = constant [3 x ptr] [
5656
ptr @vf0i1,
5757
ptr @vf1i1,
5858
ptr @vf3i32
5959
], !type !0
6060

61-
; CHECK: [[VT4DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [0 x i8] } { [8 x i8] c"\00\00\00\04\00\00\00\01", [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf4i32], [0 x i8] zeroinitializer }, !type [[T8]]
61+
; CHECK: [[VT4DATA:@[^ ]*]] = private constant { [8 x i8], [3 x ptr], [4 x i8] } { [8 x i8] c"\00\00\00\00\00\00\00\01", [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf4i32], [4 x i8] c"\04\00\00\00" }, !type [[T8]]
6262
@vt4 = constant [3 x ptr] [
6363
ptr @vf1i1,
6464
ptr @vf0i1,
@@ -95,10 +95,10 @@ i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vf0i1 to i64), i64 p
9595
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vf4i32 to i64), i64 ptrtoint (ptr @vt7_rel to i64)) to i32)
9696
], !type !1
9797

98-
; CHECK: @vt1 = alias [3 x ptr], getelementptr inbounds ({ [8 x i8], [3 x ptr], [0 x i8] }, ptr [[VT1DATA]], i32 0, i32 1)
99-
; CHECK: @vt2 = alias [3 x ptr], getelementptr inbounds ({ [8 x i8], [3 x ptr], [0 x i8] }, ptr [[VT2DATA]], i32 0, i32 1)
100-
; CHECK: @vt3 = alias [3 x ptr], getelementptr inbounds ({ [8 x i8], [3 x ptr], [0 x i8] }, ptr [[VT3DATA]], i32 0, i32 1)
101-
; CHECK: @vt4 = alias [3 x ptr], getelementptr inbounds ({ [8 x i8], [3 x ptr], [0 x i8] }, ptr [[VT4DATA]], i32 0, i32 1)
98+
; CHECK: @vt1 = alias [3 x ptr], getelementptr inbounds ({ [8 x i8], [3 x ptr], [4 x i8] }, ptr [[VT1DATA]], i32 0, i32 1)
99+
; CHECK: @vt2 = alias [3 x ptr], getelementptr inbounds ({ [8 x i8], [3 x ptr], [4 x i8] }, ptr [[VT2DATA]], i32 0, i32 1)
100+
; CHECK: @vt3 = alias [3 x ptr], getelementptr inbounds ({ [8 x i8], [3 x ptr], [4 x i8] }, ptr [[VT3DATA]], i32 0, i32 1)
101+
; CHECK: @vt4 = alias [3 x ptr], getelementptr inbounds ({ [8 x i8], [3 x ptr], [4 x i8] }, ptr [[VT4DATA]], i32 0, i32 1)
102102
; CHECK: @vt6_rel = alias [3 x i32], getelementptr inbounds ({ [4 x i8], [3 x i32], [0 x i8] }, ptr [[VT6RELDATA]], i32 0, i32 1)
103103
; CHECK: @vt7_rel = alias [3 x i32], getelementptr inbounds ({ [4 x i8], [3 x i32], [0 x i8] }, ptr [[VT7RELDATA]], i32 0, i32 1)
104104

@@ -165,7 +165,7 @@ define i32 @call3(ptr %obj) {
165165
%vtable = load ptr, ptr %obj
166166
%pair = call {ptr, i1} @llvm.type.checked.load(ptr %vtable, i32 16, metadata !"typeid")
167167
%fptr = extractvalue {ptr, i1} %pair, 0
168-
; CHECK: [[VTGEP3:%[^ ]*]] = getelementptr i8, ptr %vtable, i32 -5
168+
; CHECK: [[VTGEP3:%[^ ]*]] = getelementptr i8, ptr %vtable, i32 24
169169
; CHECK: [[VTLOAD3:%[^ ]*]] = load i32, ptr [[VTGEP3]]
170170
%result = call i32 %fptr(ptr %obj)
171171
; CHECK: ret i32 [[VTLOAD3]]

llvm/test/Transforms/WholeProgramDevirt/virtual-const-prop-end.ll

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,30 @@
22

33
target datalayout = "e-p:64:64"
44

5-
; CHECK: [[VT1DATA:@[^ ]*]] = private constant { [0 x i8], [4 x ptr], [5 x i8] } { [0 x i8] zeroinitializer, [4 x ptr] [ptr null, ptr @vf0i1, ptr @vf1i1, ptr @vf1i32], [5 x i8] c"\02\03\00\00\00" }, !type [[T8:![0-9]+]]
5+
; CHECK: [[VT1DATA:@[^ ]*]] = private constant { [0 x i8], [4 x ptr], [8 x i8] } { [0 x i8] zeroinitializer, [4 x ptr] [ptr null, ptr @vf0i1, ptr @vf1i1, ptr @vf1i32], [8 x i8] c"\02\00\00\00\03\00\00\00" }, !type [[T8:![0-9]+]]
66
@vt1 = constant [4 x ptr] [
77
ptr null,
88
ptr @vf0i1,
99
ptr @vf1i1,
1010
ptr @vf1i32
1111
], !type !1
1212

13-
; CHECK: [[VT2DATA:@[^ ]*]] = private constant { [0 x i8], [3 x ptr], [5 x i8] } { [0 x i8] zeroinitializer, [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf2i32], [5 x i8] c"\01\04\00\00\00" }, !type [[T0:![0-9]+]]
13+
; CHECK: [[VT2DATA:@[^ ]*]] = private constant { [0 x i8], [3 x ptr], [8 x i8] } { [0 x i8] zeroinitializer, [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf2i32], [8 x i8] c"\01\00\00\00\04\00\00\00" }, !type [[T0:![0-9]+]]
1414
@vt2 = constant [3 x ptr] [
1515
ptr @vf1i1,
1616
ptr @vf0i1,
1717
ptr @vf2i32
1818
], !type !0
1919

20-
; CHECK: [[VT3DATA:@[^ ]*]] = private constant { [0 x i8], [4 x ptr], [5 x i8] } { [0 x i8] zeroinitializer, [4 x ptr] [ptr null, ptr @vf0i1, ptr @vf1i1, ptr @vf3i32], [5 x i8] c"\02\05\00\00\00" }, !type [[T8]]
20+
; CHECK: [[VT3DATA:@[^ ]*]] = private constant { [0 x i8], [4 x ptr], [8 x i8] } { [0 x i8] zeroinitializer, [4 x ptr] [ptr null, ptr @vf0i1, ptr @vf1i1, ptr @vf3i32], [8 x i8] c"\02\00\00\00\05\00\00\00" }, !type [[T8]]
2121
@vt3 = constant [4 x ptr] [
2222
ptr null,
2323
ptr @vf0i1,
2424
ptr @vf1i1,
2525
ptr @vf3i32
2626
], !type !1
2727

28-
; CHECK: [[VT4DATA:@[^ ]*]] = private constant { [0 x i8], [3 x ptr], [5 x i8] } { [0 x i8] zeroinitializer, [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf4i32], [5 x i8] c"\01\06\00\00\00" }, !type [[T0]]
28+
; CHECK: [[VT4DATA:@[^ ]*]] = private constant { [0 x i8], [3 x ptr], [8 x i8] } { [0 x i8] zeroinitializer, [3 x ptr] [ptr @vf1i1, ptr @vf0i1, ptr @vf4i32], [8 x i8] c"\01\00\00\00\06\00\00\00" }, !type [[T0]]
2929
@vt4 = constant [3 x ptr] [
3030
ptr @vf1i1,
3131
ptr @vf0i1,
@@ -57,10 +57,10 @@ i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vf1i1 to i64), i64 p
5757
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vf4i32 to i64), i64 ptrtoint (ptr @vt6_rel to i64)) to i32)
5858
], !type !2
5959

60-
; CHECK: @vt1 = alias [4 x ptr], getelementptr inbounds ({ [0 x i8], [4 x ptr], [5 x i8] }, ptr [[VT1DATA]], i32 0, i32 1)
61-
; CHECK: @vt2 = alias [3 x ptr], getelementptr inbounds ({ [0 x i8], [3 x ptr], [5 x i8] }, ptr [[VT2DATA]], i32 0, i32 1)
62-
; CHECK: @vt3 = alias [4 x ptr], getelementptr inbounds ({ [0 x i8], [4 x ptr], [5 x i8] }, ptr [[VT3DATA]], i32 0, i32 1)
63-
; CHECK: @vt4 = alias [3 x ptr], getelementptr inbounds ({ [0 x i8], [3 x ptr], [5 x i8] }, ptr [[VT4DATA]], i32 0, i32 1)
60+
; CHECK: @vt1 = alias [4 x ptr], getelementptr inbounds ({ [0 x i8], [4 x ptr], [8 x i8] }, ptr [[VT1DATA]], i32 0, i32 1)
61+
; CHECK: @vt2 = alias [3 x ptr], getelementptr inbounds ({ [0 x i8], [3 x ptr], [8 x i8] }, ptr [[VT2DATA]], i32 0, i32 1)
62+
; CHECK: @vt3 = alias [4 x ptr], getelementptr inbounds ({ [0 x i8], [4 x ptr], [8 x i8] }, ptr [[VT3DATA]], i32 0, i32 1)
63+
; CHECK: @vt4 = alias [3 x ptr], getelementptr inbounds ({ [0 x i8], [3 x ptr], [8 x i8] }, ptr [[VT4DATA]], i32 0, i32 1)
6464

6565
define i1 @vf0i1(ptr %this) readnone {
6666
ret i1 0
@@ -124,7 +124,7 @@ define i32 @call3(ptr %obj) {
124124
call void @llvm.assume(i1 %p)
125125
%fptrptr = getelementptr [3 x ptr], ptr %vtable, i32 0, i32 2
126126
%fptr = load ptr, ptr %fptrptr
127-
; CHECK: [[VTGEP3:%[^ ]*]] = getelementptr i8, ptr %vtable, i32 25
127+
; CHECK: [[VTGEP3:%[^ ]*]] = getelementptr i8, ptr %vtable, i32 28
128128
; CHECK: [[VTLOAD3:%[^ ]*]] = load i32, ptr [[VTGEP3]]
129129
%result = call i32 %fptr(ptr %obj)
130130
; CHECK: ret i32 [[VTLOAD3]]

0 commit comments

Comments
 (0)