Skip to content

Commit 0f8c075

Browse files
authored
[llvm] Match llvm.type.checked.load.relative semantics to llvm.load.r… (#129583)
…elative The semantics of `llvm.type.checked.load.relative` seem to be a little different from that of `llvm.load.relative`. It looks like the semantics for `llvm.type.checked.load.relative` is `ptr + offset + *(ptr + offset)` whereas the semantics for `llvm.load.relative` is `ptr + *(ptr + offset)`. That is, the offset for the former is added to the offset address whereas the later has the offset added to the original pointer. It really feels like the checked intrinsic was meant to match the semantics of the non-checked intrinsic, but I think for all cases the checked intrinsic is used (swift being the only use I know of), the calculation just happens to be the same because swift always uses an offset of zero. Likewise, all llvm tests for this intrinsic happen to use an offset of zero. Relative vtables in clang happens to be the first time where we're using this intrinsic and using it with non-zero values. This updates the semantics of the checked intrinsic to match the non-checked one. Effectively this shouldn't change any codegen by any users of this since all current users seem to use a zero offset. This PR also updates some tests with non-zero offsets.
1 parent dfb661c commit 0f8c075

File tree

5 files changed

+76
-35
lines changed

5 files changed

+76
-35
lines changed

llvm/docs/LangRef.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29561,9 +29561,10 @@ The ``llvm.type.checked.load.relative`` intrinsic loads a relative pointer to a
2956129561
function from a virtual table pointer using metadata. Otherwise, its semantic is
2956229562
identical to the ``llvm.type.checked.load`` intrinsic.
2956329563

29564-
A relative pointer is a pointer to an offset to the pointed to value. The
29565-
address of the underlying pointer of the relative pointer is obtained by adding
29566-
the offset to the address of the offset value.
29564+
A relative pointer is a pointer to an offset. This is the offset between the destination
29565+
pointer and the original pointer. The address of the destination pointer is obtained
29566+
by loading this offset and adding it to the original pointer. This calculation is the
29567+
same as that of the ``llvm.load.relative`` intrinsic.
2956729568

2956829569
'``llvm.arithmetic.fence``' Intrinsic
2956929570
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

llvm/docs/ReleaseNotes.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ Changes to the LLVM IR
6262

6363
* `mul`
6464

65+
* Updated semantics of `llvm.type.checked.load.relative` to match that of
66+
`llvm.load.relative`.
67+
6568
Changes to LLVM infrastructure
6669
------------------------------
6770

llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,12 +2100,9 @@ void DevirtModule::scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc) {
21002100
Value *LoadedValue = nullptr;
21012101
if (TypeCheckedLoadFunc->getIntrinsicID() ==
21022102
Intrinsic::type_checked_load_relative) {
2103-
Value *GEP = LoadB.CreatePtrAdd(Ptr, Offset);
2104-
LoadedValue = LoadB.CreateLoad(Int32Ty, GEP);
2105-
LoadedValue = LoadB.CreateSExt(LoadedValue, IntPtrTy);
2106-
GEP = LoadB.CreatePtrToInt(GEP, IntPtrTy);
2107-
LoadedValue = LoadB.CreateAdd(GEP, LoadedValue);
2108-
LoadedValue = LoadB.CreateIntToPtr(LoadedValue, Int8PtrTy);
2103+
Function *LoadRelFunc = Intrinsic::getOrInsertDeclaration(
2104+
&M, Intrinsic::load_relative, {Int32Ty});
2105+
LoadedValue = LoadB.CreateCall(LoadRelFunc, {Ptr, Offset});
21092106
} else {
21102107
Value *GEP = LoadB.CreatePtrAdd(Ptr, Offset);
21112108
LoadedValue = LoadB.CreateLoad(Int8PtrTy, GEP);

llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl-check-relative.ll

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@
33
target datalayout = "e-p:64:64"
44
target triple = "x86_64-unknown-linux-gnu"
55

6-
; CHECK: remark: <unknown>:0:0: single-impl: devirtualized a call to vf
7-
; CHECK: remark: <unknown>:0:0: devirtualized vf
6+
; CHECK: remark: <unknown>:0:0: single-impl: devirtualized a call to vfunc1_live
7+
; CHECK: remark: <unknown>:0:0: single-impl: devirtualized a call to vfunc3_live
8+
; CHECK: remark: <unknown>:0:0: devirtualized vfunc1_live
9+
; CHECK: remark: <unknown>:0:0: devirtualized vfunc3_live
810
; CHECK-NOT: devirtualized
911

1012
; A vtable with "relative pointers", slots don't contain pointers to implementations, but instead have an i32 offset from the vtable itself to the implementation.
11-
@vtable = internal unnamed_addr constant { [2 x i32] } { [2 x i32] [
13+
@vtable = internal unnamed_addr constant { [3 x i32] } { [3 x i32] [
1214
i32 trunc (i64 sub (i64 ptrtoint (ptr @vfunc1_live to i64), i64 ptrtoint (ptr @vtable to i64)) to i32),
13-
i32 trunc (i64 sub (i64 ptrtoint (ptr @vfunc2_dead to i64), i64 ptrtoint (ptr @vtable to i64)) to i32)
14-
]}, align 8, !type !0, !type !1
15-
;, !vcall_visibility !{i64 2}
16-
!0 = !{i64 0, !"vfunc1.type"}
17-
!1 = !{i64 4, !"vfunc2.type"}
15+
i32 trunc (i64 sub (i64 ptrtoint (ptr @vfunc2_dead to i64), i64 ptrtoint (ptr @vtable to i64)) to i32),
16+
i32 trunc (i64 sub (i64 ptrtoint (ptr @vfunc3_live to i64), i64 ptrtoint (ptr @vtable to i64)) to i32)
17+
]}, align 8, !type !0
18+
!0 = !{i64 0, !"vtable"}
1819

1920
define internal void @vfunc1_live() {
2021
ret void
@@ -24,10 +25,14 @@ define internal void @vfunc2_dead() {
2425
ret void
2526
}
2627

27-
; CHECK: define void @call
28-
define void @call(ptr %obj) {
28+
define internal void @vfunc3_live() {
29+
ret void
30+
}
31+
32+
; CHECK: define void @call_vf1
33+
define void @call_vf1(ptr %obj) {
2934
%vtable = load ptr, ptr %obj
30-
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 0, metadata !"vfunc1.type")
35+
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 0, metadata !"vtable")
3136
%fptr = extractvalue {ptr, i1} %pair, 0
3237
%p = extractvalue {ptr, i1} %pair, 1
3338
; CHECK: br i1 true,
@@ -43,5 +48,24 @@ trap:
4348
unreachable
4449
}
4550

51+
; CHECK: define void @call_vf3
52+
define void @call_vf3(ptr %obj) {
53+
%vtable = load ptr, ptr %obj
54+
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 8, metadata !"vtable")
55+
%fptr = extractvalue {ptr, i1} %pair, 0
56+
%p = extractvalue {ptr, i1} %pair, 1
57+
; CHECK: br i1 true,
58+
br i1 %p, label %cont, label %trap
59+
60+
cont:
61+
; CHECK: call void @vfunc3_live(
62+
call void %fptr()
63+
ret void
64+
65+
trap:
66+
call void @llvm.trap()
67+
unreachable
68+
}
69+
4670
declare {ptr, i1} @llvm.type.checked.load.relative(ptr, i32, metadata)
4771
declare void @llvm.trap()

llvm/test/Transforms/WholeProgramDevirt/expand-check-relative.ll

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,9 @@ target triple = "x86_64-unknown-linux-gnu"
99
@vt1 = constant { [2 x i32] } { [2 x i32] [
1010
i32 trunc (i64 sub (i64 ptrtoint (ptr @vf1 to i64), i64 ptrtoint (ptr @vt1 to i64)) to i32),
1111
i32 trunc (i64 sub (i64 ptrtoint (ptr @vf2 to i64), i64 ptrtoint (ptr @vt1 to i64)) to i32)
12-
]}, align 8, !type !0, !type !1
13-
14-
!0 = !{i64 0, !"vfunc1.type"}
15-
!1 = !{i64 4, !"vfunc2.type"}
12+
]}, align 8, !type !0
1613

14+
!0 = !{i64 0, !"vtable"}
1715

1816
define void @vf1(ptr %this) {
1917
ret void
@@ -23,24 +21,42 @@ define void @vf2(ptr %this) {
2321
ret void
2422
}
2523

26-
; CHECK: define void @call
27-
; CHECK: [[TT:%.*]] = call i1 @llvm.type.test(ptr [[VT:%.*]], metadata !"vfunc1.type")
24+
; CHECK: define void @call_vf1
25+
; CHECK: [[TT:%.*]] = call i1 @llvm.type.test(ptr [[VT:%.*]], metadata !"vtable")
2826
; CHECK: br i1 [[TT]]
2927

30-
; Relative pointer computation at the address of the i32 value to the i32 value
28+
; Relative pointer computation at the vtable to the i32 value
3129
; to get to the pointer value.
3230

33-
; CHECK: [[T0:%.*]] = getelementptr i8, ptr [[VT]], i32 0
34-
; CHECK: [[T1:%.*]] = load i32, ptr [[T0]]
35-
; CHECK: [[T2:%.*]] = sext i32 [[T1]] to i64
36-
; CHECK: [[T3:%.*]] = ptrtoint ptr [[T0]] to i64
37-
; CHECK: [[T4:%.*]] = add i64 [[T3]], [[T2]]
38-
; CHECK: [[F:%.*]] = inttoptr i64 [[T4]] to ptr
31+
; CHECK: [[F:%.*]] = call ptr @llvm.load.relative.i32(ptr [[VT]], i32 0)
32+
; CHECK: call void [[F]](ptr
33+
34+
define void @call_vf1(ptr %obj) {
35+
%vtable = load ptr, ptr %obj
36+
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 0, metadata !"vtable")
37+
%p = extractvalue {ptr, i1} %pair, 1
38+
br i1 %p, label %cont, label %trap
39+
40+
cont:
41+
%fptr = extractvalue {ptr, i1} %pair, 0
42+
call void %fptr(ptr %obj)
43+
ret void
44+
45+
trap:
46+
call void @llvm.trap()
47+
unreachable
48+
}
49+
50+
; CHECK: define void @call_vf2
51+
; CHECK: [[TT:%.*]] = call i1 @llvm.type.test(ptr [[VT:%.*]], metadata !"vtable")
52+
; CHECK: br i1 [[TT]]
53+
54+
; CHECK: [[F:%.*]] = call ptr @llvm.load.relative.i32(ptr [[VT]], i32 4)
3955
; CHECK: call void [[F]](ptr
4056

41-
define void @call(ptr %obj) {
57+
define void @call_vf2(ptr %obj) {
4258
%vtable = load ptr, ptr %obj
43-
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 0, metadata !"vfunc1.type")
59+
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 4, metadata !"vtable")
4460
%p = extractvalue {ptr, i1} %pair, 1
4561
br i1 %p, label %cont, label %trap
4662

0 commit comments

Comments
 (0)