diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index b0de05da2fd1f..c0567090fdd2a 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index 3d608c636a8f6..d649e86301ec3 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -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 ------------------------------ diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp index 30e935ea663f3..0680b2c558807 100644 --- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -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); diff --git a/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl-check-relative.ll b/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl-check-relative.ll index ba8c89d950ffb..03ec48a5b8467 100644 --- a/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl-check-relative.ll +++ b/llvm/test/Transforms/WholeProgramDevirt/devirt-single-impl-check-relative.ll @@ -3,18 +3,19 @@ target datalayout = "e-p:64:64" target triple = "x86_64-unknown-linux-gnu" -; CHECK: remark: :0:0: single-impl: devirtualized a call to vf -; CHECK: remark: :0:0: devirtualized vf +; CHECK: remark: :0:0: single-impl: devirtualized a call to vfunc1_live +; CHECK: remark: :0:0: single-impl: devirtualized a call to vfunc3_live +; CHECK: remark: :0:0: devirtualized vfunc1_live +; CHECK: remark: :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 @@ -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, @@ -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() diff --git a/llvm/test/Transforms/WholeProgramDevirt/expand-check-relative.ll b/llvm/test/Transforms/WholeProgramDevirt/expand-check-relative.ll index d67458952ba72..275b188f848cd 100644 --- a/llvm/test/Transforms/WholeProgramDevirt/expand-check-relative.ll +++ b/llvm/test/Transforms/WholeProgramDevirt/expand-check-relative.ll @@ -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 @@ -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