Skip to content

[llvm] Match llvm.type.checked.load.relative semantics to llvm.load.r… #129583

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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29561,9 +29561,10 @@ The ``llvm.type.checked.load.relative`` intrinsic loads a relative pointer to a
function from a virtual table pointer using metadata. Otherwise, its semantic is
identical to the ``llvm.type.checked.load`` intrinsic.

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

'``llvm.arithmetic.fence``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
3 changes: 3 additions & 0 deletions llvm/docs/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ Changes to the LLVM IR

* `mul`

* Updated semantics of `llvm.type.checked.load.relative` to match that of
`llvm.load.relative`.

Changes to LLVM infrastructure
------------------------------

Expand Down
9 changes: 3 additions & 6 deletions llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2100,12 +2100,9 @@ void DevirtModule::scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc) {
Value *LoadedValue = nullptr;
if (TypeCheckedLoadFunc->getIntrinsicID() ==
Intrinsic::type_checked_load_relative) {
Value *GEP = LoadB.CreatePtrAdd(Ptr, Offset);
LoadedValue = LoadB.CreateLoad(Int32Ty, GEP);
LoadedValue = LoadB.CreateSExt(LoadedValue, IntPtrTy);
GEP = LoadB.CreatePtrToInt(GEP, IntPtrTy);
LoadedValue = LoadB.CreateAdd(GEP, LoadedValue);
LoadedValue = LoadB.CreateIntToPtr(LoadedValue, Int8PtrTy);
Function *LoadRelFunc = Intrinsic::getOrInsertDeclaration(
&M, Intrinsic::load_relative, {Int32Ty});
LoadedValue = LoadB.CreateCall(LoadRelFunc, {Ptr, Offset});
} else {
Value *GEP = LoadB.CreatePtrAdd(Ptr, Offset);
LoadedValue = LoadB.CreateLoad(Int8PtrTy, GEP);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@
target datalayout = "e-p:64:64"
target triple = "x86_64-unknown-linux-gnu"

; CHECK: remark: <unknown>:0:0: single-impl: devirtualized a call to vf
; CHECK: remark: <unknown>:0:0: devirtualized vf
; CHECK: remark: <unknown>:0:0: single-impl: devirtualized a call to vfunc1_live
; CHECK: remark: <unknown>:0:0: single-impl: devirtualized a call to vfunc3_live
; CHECK: remark: <unknown>:0:0: devirtualized vfunc1_live
; CHECK: remark: <unknown>:0:0: devirtualized vfunc3_live
; CHECK-NOT: devirtualized

; 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.
@vtable = internal unnamed_addr constant { [2 x i32] } { [2 x i32] [
@vtable = internal unnamed_addr constant { [3 x i32] } { [3 x i32] [
i32 trunc (i64 sub (i64 ptrtoint (ptr @vfunc1_live to i64), i64 ptrtoint (ptr @vtable to i64)) to i32),
i32 trunc (i64 sub (i64 ptrtoint (ptr @vfunc2_dead to i64), i64 ptrtoint (ptr @vtable to i64)) to i32)
]}, align 8, !type !0, !type !1
;, !vcall_visibility !{i64 2}
!0 = !{i64 0, !"vfunc1.type"}
!1 = !{i64 4, !"vfunc2.type"}
i32 trunc (i64 sub (i64 ptrtoint (ptr @vfunc2_dead to i64), i64 ptrtoint (ptr @vtable to i64)) to i32),
i32 trunc (i64 sub (i64 ptrtoint (ptr @vfunc3_live to i64), i64 ptrtoint (ptr @vtable to i64)) to i32)
]}, align 8, !type !0
!0 = !{i64 0, !"vtable"}

define internal void @vfunc1_live() {
ret void
Expand All @@ -24,10 +25,14 @@ define internal void @vfunc2_dead() {
ret void
}

; CHECK: define void @call
define void @call(ptr %obj) {
define internal void @vfunc3_live() {
ret void
}

; CHECK: define void @call_vf1
define void @call_vf1(ptr %obj) {
%vtable = load ptr, ptr %obj
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 0, metadata !"vfunc1.type")
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 0, metadata !"vtable")
%fptr = extractvalue {ptr, i1} %pair, 0
%p = extractvalue {ptr, i1} %pair, 1
; CHECK: br i1 true,
Expand All @@ -43,5 +48,24 @@ trap:
unreachable
}

; CHECK: define void @call_vf3
define void @call_vf3(ptr %obj) {
%vtable = load ptr, ptr %obj
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 8, metadata !"vtable")
%fptr = extractvalue {ptr, i1} %pair, 0
%p = extractvalue {ptr, i1} %pair, 1
; CHECK: br i1 true,
br i1 %p, label %cont, label %trap

cont:
; CHECK: call void @vfunc3_live(
call void %fptr()
ret void

trap:
call void @llvm.trap()
unreachable
}

declare {ptr, i1} @llvm.type.checked.load.relative(ptr, i32, metadata)
declare void @llvm.trap()
46 changes: 31 additions & 15 deletions llvm/test/Transforms/WholeProgramDevirt/expand-check-relative.ll
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ target triple = "x86_64-unknown-linux-gnu"
@vt1 = constant { [2 x i32] } { [2 x i32] [
i32 trunc (i64 sub (i64 ptrtoint (ptr @vf1 to i64), i64 ptrtoint (ptr @vt1 to i64)) to i32),
i32 trunc (i64 sub (i64 ptrtoint (ptr @vf2 to i64), i64 ptrtoint (ptr @vt1 to i64)) to i32)
]}, align 8, !type !0, !type !1

!0 = !{i64 0, !"vfunc1.type"}
!1 = !{i64 4, !"vfunc2.type"}
]}, align 8, !type !0

!0 = !{i64 0, !"vtable"}

define void @vf1(ptr %this) {
ret void
Expand All @@ -23,24 +21,42 @@ define void @vf2(ptr %this) {
ret void
}

; CHECK: define void @call
; CHECK: [[TT:%.*]] = call i1 @llvm.type.test(ptr [[VT:%.*]], metadata !"vfunc1.type")
; CHECK: define void @call_vf1
; CHECK: [[TT:%.*]] = call i1 @llvm.type.test(ptr [[VT:%.*]], metadata !"vtable")
; CHECK: br i1 [[TT]]

; Relative pointer computation at the address of the i32 value to the i32 value
; Relative pointer computation at the vtable to the i32 value
; to get to the pointer value.

; CHECK: [[T0:%.*]] = getelementptr i8, ptr [[VT]], i32 0
; CHECK: [[T1:%.*]] = load i32, ptr [[T0]]
; CHECK: [[T2:%.*]] = sext i32 [[T1]] to i64
; CHECK: [[T3:%.*]] = ptrtoint ptr [[T0]] to i64
; CHECK: [[T4:%.*]] = add i64 [[T3]], [[T2]]
; CHECK: [[F:%.*]] = inttoptr i64 [[T4]] to ptr
; CHECK: [[F:%.*]] = call ptr @llvm.load.relative.i32(ptr [[VT]], i32 0)
; CHECK: call void [[F]](ptr

define void @call_vf1(ptr %obj) {
%vtable = load ptr, ptr %obj
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 0, metadata !"vtable")
%p = extractvalue {ptr, i1} %pair, 1
br i1 %p, label %cont, label %trap

cont:
%fptr = extractvalue {ptr, i1} %pair, 0
call void %fptr(ptr %obj)
ret void

trap:
call void @llvm.trap()
unreachable
}

; CHECK: define void @call_vf2
; CHECK: [[TT:%.*]] = call i1 @llvm.type.test(ptr [[VT:%.*]], metadata !"vtable")
; CHECK: br i1 [[TT]]

; CHECK: [[F:%.*]] = call ptr @llvm.load.relative.i32(ptr [[VT]], i32 4)
; CHECK: call void [[F]](ptr

define void @call(ptr %obj) {
define void @call_vf2(ptr %obj) {
%vtable = load ptr, ptr %obj
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 0, metadata !"vfunc1.type")
%pair = call {ptr, i1} @llvm.type.checked.load.relative(ptr %vtable, i32 4, metadata !"vtable")
%p = extractvalue {ptr, i1} %pair, 1
br i1 %p, label %cont, label %trap

Expand Down
Loading