From bfecb4640bbb8c34efd60d43fd1d098d07c742ca Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Fri, 10 Mar 2017 20:13:58 +0000 Subject: [PATCH] WholeProgramDevirt: Implement export/import support for VCP. Differential Revision: https://reviews.llvm.org/D30017 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@297503 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Transforms/IPO/WholeProgramDevirt.cpp | 40 ++++++++- .../WholeProgramDevirt/Inputs/import-vcp.yaml | 19 +++++ .../WholeProgramDevirt/export-vcp.ll | 83 +++++++++++++++++++ test/Transforms/WholeProgramDevirt/import.ll | 24 ++++++ 4 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 test/Transforms/WholeProgramDevirt/Inputs/import-vcp.yaml create mode 100644 test/Transforms/WholeProgramDevirt/export-vcp.ll diff --git a/lib/Transforms/IPO/WholeProgramDevirt.cpp b/lib/Transforms/IPO/WholeProgramDevirt.cpp index 6a9e0c9eab9..ba44921d4d5 100644 --- a/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -380,6 +380,7 @@ struct DevirtModule { PointerType *Int8PtrTy; IntegerType *Int32Ty; IntegerType *Int64Ty; + IntegerType *IntPtrTy; bool RemarksEnabled; @@ -402,6 +403,7 @@ struct DevirtModule { Int8PtrTy(Type::getInt8PtrTy(M.getContext())), Int32Ty(Type::getInt32Ty(M.getContext())), Int64Ty(Type::getInt64Ty(M.getContext())), + IntPtrTy(M.getDataLayout().getIntPtrType(M.getContext(), 0)), RemarksEnabled(areRemarksEnabled()) {} bool areRemarksEnabled(); @@ -448,7 +450,7 @@ struct DevirtModule { // This function is called during the import phase to create a reference to // the symbol definition created during the export phase. Constant *importGlobal(VTableSlot Slot, ArrayRef Args, - StringRef Name); + StringRef Name, unsigned AbsWidth = 0); void applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, bool IsOne, Constant *UniqueMemberAddr); @@ -802,12 +804,25 @@ void DevirtModule::exportGlobal(VTableSlot Slot, ArrayRef Args, } Constant *DevirtModule::importGlobal(VTableSlot Slot, ArrayRef Args, - StringRef Name) { + StringRef Name, unsigned AbsWidth) { Constant *C = M.getOrInsertGlobal(getGlobalName(Slot, Args, Name), Int8Ty); auto *GV = dyn_cast(C); - if (!GV) + // We only need to set metadata if the global is newly created, in which + // case it would not have hidden visibility. + if (!GV || GV->getVisibility() == GlobalValue::HiddenVisibility) return C; + GV->setVisibility(GlobalValue::HiddenVisibility); + auto SetAbsRange = [&](uint64_t Min, uint64_t Max) { + auto *MinC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Min)); + auto *MaxC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Max)); + GV->setMetadata(LLVMContext::MD_absolute_symbol, + MDNode::get(M.getContext(), {MinC, MaxC})); + }; + if (AbsWidth == IntPtrTy->getBitWidth()) + SetAbsRange(~0ull, ~0ull); // Full set. + else if (AbsWidth) + SetAbsRange(0, 1ull << AbsWidth); return GV; } @@ -895,6 +910,7 @@ void DevirtModule::applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName, Call.replaceAndErase("virtual-const-prop", FnName, RemarksEnabled, Val); } } + CSInfo.markDevirt(); } bool DevirtModule::tryVirtualConstProp( @@ -979,9 +995,18 @@ bool DevirtModule::tryVirtualConstProp( for (auto &&Target : TargetsForSlot) Target.WasDevirt = true; - // Rewrite each call to a load from OffsetByte/OffsetBit. Constant *ByteConst = ConstantInt::get(Int32Ty, OffsetByte); Constant *BitConst = ConstantInt::get(Int8Ty, 1ULL << OffsetBit); + + if (CSByConstantArg.second.isExported()) { + ResByArg->TheKind = WholeProgramDevirtResolution::ByArg::VirtualConstProp; + exportGlobal(Slot, CSByConstantArg.first, "byte", + ConstantExpr::getIntToPtr(ByteConst, Int8PtrTy)); + exportGlobal(Slot, CSByConstantArg.first, "bit", + ConstantExpr::getIntToPtr(BitConst, Int8PtrTy)); + } + + // Rewrite each call to a load from OffsetByte/OffsetBit. applyVirtualConstProp(CSByConstantArg.second, TargetsForSlot[0].Fn->getName(), ByteConst, BitConst); } @@ -1207,6 +1232,13 @@ void DevirtModule::importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo) { UniqueMemberAddr); break; } + case WholeProgramDevirtResolution::ByArg::VirtualConstProp: { + Constant *Byte = importGlobal(Slot, CSByConstantArg.first, "byte", 32); + Byte = ConstantExpr::getPtrToInt(Byte, Int32Ty); + Constant *Bit = importGlobal(Slot, CSByConstantArg.first, "bit", 8); + Bit = ConstantExpr::getPtrToInt(Bit, Int8Ty); + applyVirtualConstProp(CSByConstantArg.second, "", Byte, Bit); + } default: break; } diff --git a/test/Transforms/WholeProgramDevirt/Inputs/import-vcp.yaml b/test/Transforms/WholeProgramDevirt/Inputs/import-vcp.yaml new file mode 100644 index 00000000000..4fbee126d0e --- /dev/null +++ b/test/Transforms/WholeProgramDevirt/Inputs/import-vcp.yaml @@ -0,0 +1,19 @@ +--- +TypeIdMap: + typeid1: + WPDRes: + 0: + Kind: Indir + ResByArg: + 1: + Kind: VirtualConstProp + Info: 0 + typeid2: + WPDRes: + 8: + Kind: Indir + ResByArg: + 3: + Kind: VirtualConstProp + Info: 0 +... diff --git a/test/Transforms/WholeProgramDevirt/export-vcp.ll b/test/Transforms/WholeProgramDevirt/export-vcp.ll new file mode 100644 index 00000000000..8e6e69b9bd4 --- /dev/null +++ b/test/Transforms/WholeProgramDevirt/export-vcp.ll @@ -0,0 +1,83 @@ +; RUN: opt -wholeprogramdevirt -wholeprogramdevirt-summary-action=export -wholeprogramdevirt-read-summary=%S/Inputs/export.yaml -wholeprogramdevirt-write-summary=%t -S -o - %s | FileCheck %s +; RUN: FileCheck --check-prefix=SUMMARY %s < %t + +target datalayout = "e-p:64:64" +target triple = "x86_64-unknown-linux-gnu" + +; SUMMARY: TypeIdMap: +; SUMMARY-NEXT: typeid3: +; SUMMARY-NEXT: TTRes: +; SUMMARY-NEXT: Kind: Unsat +; SUMMARY-NEXT: SizeM1BitWidth: 0 +; SUMMARY-NEXT: WPDRes: +; SUMMARY-NEXT: 0: +; SUMMARY-NEXT: Kind: Indir +; SUMMARY-NEXT: SingleImplName: '' +; SUMMARY-NEXT: ResByArg: +; SUMMARY-NEXT: 12,24: +; SUMMARY-NEXT: Kind: VirtualConstProp +; SUMMARY-NEXT: Info: 0 +; SUMMARY-NEXT: typeid4: +; SUMMARY-NEXT: TTRes: +; SUMMARY-NEXT: Kind: Unsat +; SUMMARY-NEXT: SizeM1BitWidth: 0 +; SUMMARY-NEXT: WPDRes: +; SUMMARY-NEXT: 0: +; SUMMARY-NEXT: Kind: Indir +; SUMMARY-NEXT: SingleImplName: '' +; SUMMARY-NEXT: ResByArg: +; SUMMARY-NEXT: 24,12: +; SUMMARY-NEXT: Kind: VirtualConstProp +; SUMMARY-NEXT: Info: 0 + +; CHECK: [[CVT3A:.*]] = private constant { [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] } { [8 x i8] zeroinitializer, i1 (i8*, i32, i32)* @vf0i1, [0 x i8] zeroinitializer }, !type !0 +@vt3a = constant i1 (i8*, i32, i32)* @vf0i1, !type !0 + +; CHECK: [[CVT3B:.*]] = private constant { [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] } { [8 x i8] c"\00\00\00\00\00\00\00\01", i1 (i8*, i32, i32)* @vf1i1, [0 x i8] zeroinitializer }, !type !0 +@vt3b = constant i1 (i8*, i32, i32)* @vf1i1, !type !0 + +; CHECK: [[CVT3C:.*]] = private constant { [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] } { [8 x i8] zeroinitializer, i1 (i8*, i32, i32)* @vf0i1, [0 x i8] zeroinitializer }, !type !0 +@vt3c = constant i1 (i8*, i32, i32)* @vf0i1, !type !0 + +; CHECK: [[CVT3D:.*]] = private constant { [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] } { [8 x i8] c"\00\00\00\00\00\00\00\01", i1 (i8*, i32, i32)* @vf1i1, [0 x i8] zeroinitializer }, !type !0 +@vt3d = constant i1 (i8*, i32, i32)* @vf1i1, !type !0 + +; CHECK: [[CVT4A:.*]] = private constant { [8 x i8], i32 (i8*, i32, i32)*, [0 x i8] } { [8 x i8] c"\00\00\00\00\01\00\00\00", i32 (i8*, i32, i32)* @vf1i32, [0 x i8] zeroinitializer }, !type !1 +@vt4a = constant i32 (i8*, i32, i32)* @vf1i32, !type !1 + +; CHECK: [[CVT4B:.*]] = private constant { [8 x i8], i32 (i8*, i32, i32)*, [0 x i8] } { [8 x i8] c"\00\00\00\00\02\00\00\00", i32 (i8*, i32, i32)* @vf2i32, [0 x i8] zeroinitializer }, !type !1 +@vt4b = constant i32 (i8*, i32, i32)* @vf2i32, !type !1 + +; CHECK: @__typeid_typeid3_0_12_24_byte = hidden alias i8, inttoptr (i32 -1 to i8*) +; CHECK: @__typeid_typeid3_0_12_24_bit = hidden alias i8, inttoptr (i8 1 to i8*) +; CHECK: @__typeid_typeid4_0_24_12_byte = hidden alias i8, inttoptr (i32 -4 to i8*) +; CHECK: @__typeid_typeid4_0_24_12_bit = hidden alias i8, inttoptr (i8 1 to i8*) + +; CHECK: @vt3a = alias i1 (i8*, i32, i32)*, getelementptr inbounds ({ [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] }, { [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] }* [[CVT3A]], i32 0, i32 1) +; CHECK: @vt3b = alias i1 (i8*, i32, i32)*, getelementptr inbounds ({ [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] }, { [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] }* [[CVT3B]], i32 0, i32 1) +; CHECK: @vt3c = alias i1 (i8*, i32, i32)*, getelementptr inbounds ({ [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] }, { [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] }* [[CVT3C]], i32 0, i32 1) +; CHECK: @vt3d = alias i1 (i8*, i32, i32)*, getelementptr inbounds ({ [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] }, { [8 x i8], i1 (i8*, i32, i32)*, [0 x i8] }* [[CVT3D]], i32 0, i32 1) +; CHECK: @vt4a = alias i32 (i8*, i32, i32)*, getelementptr inbounds ({ [8 x i8], i32 (i8*, i32, i32)*, [0 x i8] }, { [8 x i8], i32 (i8*, i32, i32)*, [0 x i8] }* [[CVT4A]], i32 0, i32 1) +; CHECK: @vt4b = alias i32 (i8*, i32, i32)*, getelementptr inbounds ({ [8 x i8], i32 (i8*, i32, i32)*, [0 x i8] }, { [8 x i8], i32 (i8*, i32, i32)*, [0 x i8] }* [[CVT4B]], i32 0, i32 1) + +define i1 @vf0i1(i8* %this, i32, i32) readnone { + ret i1 0 +} + +define i1 @vf1i1(i8* %this, i32, i32) readnone { + ret i1 1 +} + +define i32 @vf1i32(i8* %this, i32, i32) readnone { + ret i32 1 +} + +define i32 @vf2i32(i8* %this, i32, i32) readnone { + ret i32 2 +} + +; CHECK: !0 = !{i32 8, !"typeid3"} +; CHECK: !1 = !{i32 8, !"typeid4"} + +!0 = !{i32 0, !"typeid3"} +!1 = !{i32 0, !"typeid4"} diff --git a/test/Transforms/WholeProgramDevirt/import.ll b/test/Transforms/WholeProgramDevirt/import.ll index 6057c68410c..7f34b04ce11 100644 --- a/test/Transforms/WholeProgramDevirt/import.ll +++ b/test/Transforms/WholeProgramDevirt/import.ll @@ -2,10 +2,17 @@ ; RUN: opt -S -wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-uniform-ret-val.yaml < %s | FileCheck --check-prefixes=CHECK,UNIFORM-RET-VAL %s ; RUN: opt -S -wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-unique-ret-val0.yaml < %s | FileCheck --check-prefixes=CHECK,UNIQUE-RET-VAL0 %s ; RUN: opt -S -wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-unique-ret-val1.yaml < %s | FileCheck --check-prefixes=CHECK,UNIQUE-RET-VAL1 %s +; RUN: opt -S -wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp.yaml < %s | FileCheck --check-prefixes=CHECK,VCP,VCP64 %s +; RUN: opt -S -wholeprogramdevirt -wholeprogramdevirt-summary-action=import -wholeprogramdevirt-read-summary=%S/Inputs/import-vcp.yaml -mtriple=i686-unknown-linux -data-layout=e-p:32:32 < %s | FileCheck --check-prefixes=CHECK,VCP,VCP32 %s target datalayout = "e-p:64:64" target triple = "x86_64-unknown-linux-gnu" +; VCP: @__typeid_typeid1_0_1_byte = external hidden global i8, !absolute_symbol !0 +; VCP: @__typeid_typeid1_0_1_bit = external hidden global i8, !absolute_symbol !1 +; VCP: @__typeid_typeid2_8_3_byte = external hidden global i8, !absolute_symbol !0 +; VCP: @__typeid_typeid2_8_3_bit = external hidden global i8, !absolute_symbol !1 + ; Test cases where the argument values are known and we can apply virtual ; constant propagation. @@ -22,6 +29,11 @@ define i32 @call1(i8* %obj) { ; SINGLE-IMPL: call i32 bitcast (void ()* @singleimpl1 to i32 (i8*, i32)*) %result = call i32 %fptr_casted(i8* %obj, i32 1) ; UNIFORM-RET-VAL: ret i32 42 + ; VCP: [[VT1:%.*]] = bitcast {{.*}} to i8* + ; VCP: [[GEP1:%.*]] = getelementptr i8, i8* [[VT1]], i32 ptrtoint (i8* @__typeid_typeid1_0_1_byte to i32) + ; VCP: [[BC1:%.*]] = bitcast i8* [[GEP1]] to i32* + ; VCP: [[LOAD1:%.*]] = load i32, i32* [[BC1]] + ; VCP: ret i32 [[LOAD1]] ret i32 %result } @@ -68,6 +80,12 @@ cont: %result = call i1 %fptr_casted(i8* %obj, i32 3) ; UNIQUE-RET-VAL0: icmp ne i8* %vtablei8, @__typeid_typeid2_8_3_unique_member ; UNIQUE-RET-VAL1: icmp eq i8* %vtablei8, @__typeid_typeid2_8_3_unique_member + ; VCP: [[VT2:%.*]] = bitcast {{.*}} to i8* + ; VCP: [[GEP2:%.*]] = getelementptr i8, i8* [[VT2]], i32 ptrtoint (i8* @__typeid_typeid2_8_3_byte to i32) + ; VCP: [[LOAD2:%.*]] = load i8, i8* [[GEP2]] + ; VCP: [[AND2:%.*]] = and i8 [[LOAD2]], ptrtoint (i8* @__typeid_typeid2_8_3_bit to i8) + ; VCP: [[ICMP2:%.*]] = icmp ne i8 [[AND2]], 0 + ; VCP: ret i1 [[ICMP2]] ret i1 %result trap: @@ -78,6 +96,12 @@ trap: ; SINGLE-IMPL-DAG: declare void @singleimpl1() ; SINGLE-IMPL-DAG: declare void @singleimpl2() +; VCP32: !0 = !{i32 -1, i32 -1} +; VCP64: !0 = !{i64 0, i64 4294967296} + +; VCP32: !1 = !{i32 0, i32 256} +; VCP64: !1 = !{i64 0, i64 256} + declare void @llvm.assume(i1) declare void @llvm.trap() declare {i8*, i1} @llvm.type.checked.load(i8*, i32, metadata) -- 2.50.1