| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1 | //===- WholeProgramDevirt.cpp - Whole program virtual call optimization ---===// | 
|  | 2 | // | 
| Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | 4 | // See https://llvm.org/LICENSE.txt for license information. | 
|  | 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 6 | // | 
|  | 7 | //===----------------------------------------------------------------------===// | 
|  | 8 | // | 
|  | 9 | // This pass implements whole program optimization of virtual calls in cases | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 10 | // where we know (via !type metadata) that the list of callees is fixed. This | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 11 | // includes the following: | 
|  | 12 | // - Single implementation devirtualization: if a virtual call has a single | 
|  | 13 | //   possible callee, replace all calls with a direct call to that callee. | 
|  | 14 | // - Virtual constant propagation: if the virtual function's return type is an | 
|  | 15 | //   integer <=64 bits and all possible callees are readnone, for each class and | 
|  | 16 | //   each list of constant arguments: evaluate the function, store the return | 
|  | 17 | //   value alongside the virtual table, and rewrite each virtual call as a load | 
|  | 18 | //   from the virtual table. | 
|  | 19 | // - Uniform return value optimization: if the conditions for virtual constant | 
|  | 20 | //   propagation hold and each function returns the same constant value, replace | 
|  | 21 | //   each virtual call with that constant. | 
|  | 22 | // - Unique return value optimization for i1 return values: if the conditions | 
|  | 23 | //   for virtual constant propagation hold and a single vtable's function | 
|  | 24 | //   returns 0, or a single vtable's function returns 1, replace each virtual | 
|  | 25 | //   call with a comparison of the vptr against that vtable's address. | 
|  | 26 | // | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 27 | // This pass is intended to be used during the regular and thin LTO pipelines: | 
|  | 28 | // | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 29 | // During regular LTO, the pass determines the best optimization for each | 
|  | 30 | // virtual call and applies the resolutions directly to virtual calls that are | 
|  | 31 | // eligible for virtual call optimization (i.e. calls that use either of the | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 32 | // llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics). | 
|  | 33 | // | 
|  | 34 | // During hybrid Regular/ThinLTO, the pass operates in two phases: | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 35 | // - Export phase: this is run during the thin link over a single merged module | 
|  | 36 | //   that contains all vtables with !type metadata that participate in the link. | 
|  | 37 | //   The pass computes a resolution for each virtual call and stores it in the | 
|  | 38 | //   type identifier summary. | 
|  | 39 | // - Import phase: this is run during the thin backends over the individual | 
|  | 40 | //   modules. The pass applies the resolutions previously computed during the | 
|  | 41 | //   import phase to each eligible virtual call. | 
|  | 42 | // | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 43 | // During ThinLTO, the pass operates in two phases: | 
|  | 44 | // - Export phase: this is run during the thin link over the index which | 
|  | 45 | //   contains a summary of all vtables with !type metadata that participate in | 
|  | 46 | //   the link. It computes a resolution for each virtual call and stores it in | 
|  | 47 | //   the type identifier summary. Only single implementation devirtualization | 
|  | 48 | //   is supported. | 
|  | 49 | // - Import phase: (same as with hybrid case above). | 
|  | 50 | // | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 51 | //===----------------------------------------------------------------------===// | 
|  | 52 |  | 
|  | 53 | #include "llvm/Transforms/IPO/WholeProgramDevirt.h" | 
| Mehdi Amini | b550cb1 | 2016-04-18 09:17:29 +0000 | [diff] [blame] | 54 | #include "llvm/ADT/ArrayRef.h" | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 55 | #include "llvm/ADT/DenseMap.h" | 
|  | 56 | #include "llvm/ADT/DenseMapInfo.h" | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 57 | #include "llvm/ADT/DenseSet.h" | 
|  | 58 | #include "llvm/ADT/MapVector.h" | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 59 | #include "llvm/ADT/SmallVector.h" | 
| Chandler Carruth | 6bda14b | 2017-06-06 11:49:48 +0000 | [diff] [blame] | 60 | #include "llvm/ADT/iterator_range.h" | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 61 | #include "llvm/Analysis/AliasAnalysis.h" | 
|  | 62 | #include "llvm/Analysis/BasicAliasAnalysis.h" | 
| Adam Nemet | 0965da2 | 2017-10-09 23:19:02 +0000 | [diff] [blame] | 63 | #include "llvm/Analysis/OptimizationRemarkEmitter.h" | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 64 | #include "llvm/Analysis/TypeMetadataUtils.h" | 
| Evgeny Leviant | 8973fae | 2020-01-24 00:31:39 -0800 | [diff] [blame^] | 65 | #include "llvm/Bitcode/BitcodeReader.h" | 
|  | 66 | #include "llvm/Bitcode/BitcodeWriter.h" | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 67 | #include "llvm/IR/CallSite.h" | 
|  | 68 | #include "llvm/IR/Constants.h" | 
|  | 69 | #include "llvm/IR/DataLayout.h" | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 70 | #include "llvm/IR/DebugLoc.h" | 
|  | 71 | #include "llvm/IR/DerivedTypes.h" | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 72 | #include "llvm/IR/Dominators.h" | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 73 | #include "llvm/IR/Function.h" | 
|  | 74 | #include "llvm/IR/GlobalAlias.h" | 
|  | 75 | #include "llvm/IR/GlobalVariable.h" | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 76 | #include "llvm/IR/IRBuilder.h" | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 77 | #include "llvm/IR/InstrTypes.h" | 
|  | 78 | #include "llvm/IR/Instruction.h" | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 79 | #include "llvm/IR/Instructions.h" | 
|  | 80 | #include "llvm/IR/Intrinsics.h" | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 81 | #include "llvm/IR/LLVMContext.h" | 
|  | 82 | #include "llvm/IR/Metadata.h" | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 83 | #include "llvm/IR/Module.h" | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 84 | #include "llvm/IR/ModuleSummaryIndexYAML.h" | 
| Reid Kleckner | 05da2fe | 2019-11-13 13:15:01 -0800 | [diff] [blame] | 85 | #include "llvm/InitializePasses.h" | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 86 | #include "llvm/Pass.h" | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 87 | #include "llvm/PassRegistry.h" | 
|  | 88 | #include "llvm/PassSupport.h" | 
|  | 89 | #include "llvm/Support/Casting.h" | 
| Reid Kleckner | 4c1a1d3 | 2019-11-14 15:15:48 -0800 | [diff] [blame] | 90 | #include "llvm/Support/CommandLine.h" | 
| Evgeny Leviant | 8973fae | 2020-01-24 00:31:39 -0800 | [diff] [blame^] | 91 | #include "llvm/Support/Errc.h" | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 92 | #include "llvm/Support/Error.h" | 
|  | 93 | #include "llvm/Support/FileSystem.h" | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 94 | #include "llvm/Support/MathExtras.h" | 
| Mehdi Amini | b550cb1 | 2016-04-18 09:17:29 +0000 | [diff] [blame] | 95 | #include "llvm/Transforms/IPO.h" | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 96 | #include "llvm/Transforms/IPO/FunctionAttrs.h" | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 97 | #include "llvm/Transforms/Utils/Evaluator.h" | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 98 | #include <algorithm> | 
|  | 99 | #include <cstddef> | 
|  | 100 | #include <map> | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 101 | #include <set> | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 102 | #include <string> | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 103 |  | 
|  | 104 | using namespace llvm; | 
|  | 105 | using namespace wholeprogramdevirt; | 
|  | 106 |  | 
|  | 107 | #define DEBUG_TYPE "wholeprogramdevirt" | 
|  | 108 |  | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 109 | static cl::opt<PassSummaryAction> ClSummaryAction( | 
|  | 110 | "wholeprogramdevirt-summary-action", | 
|  | 111 | cl::desc("What to do with the summary when running this pass"), | 
|  | 112 | cl::values(clEnumValN(PassSummaryAction::None, "none", "Do nothing"), | 
|  | 113 | clEnumValN(PassSummaryAction::Import, "import", | 
|  | 114 | "Import typeid resolutions from summary and globals"), | 
|  | 115 | clEnumValN(PassSummaryAction::Export, "export", | 
|  | 116 | "Export typeid resolutions to summary and globals")), | 
|  | 117 | cl::Hidden); | 
|  | 118 |  | 
|  | 119 | static cl::opt<std::string> ClReadSummary( | 
|  | 120 | "wholeprogramdevirt-read-summary", | 
| Evgeny Leviant | 8973fae | 2020-01-24 00:31:39 -0800 | [diff] [blame^] | 121 | cl::desc( | 
|  | 122 | "Read summary from given bitcode or YAML file before running pass"), | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 123 | cl::Hidden); | 
|  | 124 |  | 
|  | 125 | static cl::opt<std::string> ClWriteSummary( | 
|  | 126 | "wholeprogramdevirt-write-summary", | 
| Evgeny Leviant | 8973fae | 2020-01-24 00:31:39 -0800 | [diff] [blame^] | 127 | cl::desc("Write summary to given bitcode or YAML file after running pass. " | 
|  | 128 | "Output file format is deduced from extension: *.bc means writing " | 
|  | 129 | "bitcode, otherwise YAML"), | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 130 | cl::Hidden); | 
|  | 131 |  | 
| Vitaly Buka | 9cb59b9 | 2018-04-06 21:41:17 +0000 | [diff] [blame] | 132 | static cl::opt<unsigned> | 
|  | 133 | ClThreshold("wholeprogramdevirt-branch-funnel-threshold", cl::Hidden, | 
|  | 134 | cl::init(10), cl::ZeroOrMore, | 
|  | 135 | cl::desc("Maximum number of call targets per " | 
|  | 136 | "call site to enable branch funnels")); | 
| Vitaly Buka | 66f53d7 | 2018-04-06 21:32:36 +0000 | [diff] [blame] | 137 |  | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 138 | static cl::opt<bool> | 
|  | 139 | PrintSummaryDevirt("wholeprogramdevirt-print-index-based", cl::Hidden, | 
|  | 140 | cl::init(false), cl::ZeroOrMore, | 
|  | 141 | cl::desc("Print index-based devirtualization messages")); | 
|  | 142 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 143 | // Find the minimum offset that we may store a value of size Size bits at. If | 
|  | 144 | // IsAfter is set, look for an offset before the object, otherwise look for an | 
|  | 145 | // offset after the object. | 
|  | 146 | uint64_t | 
|  | 147 | wholeprogramdevirt::findLowestOffset(ArrayRef<VirtualCallTarget> Targets, | 
|  | 148 | bool IsAfter, uint64_t Size) { | 
|  | 149 | // Find a minimum offset taking into account only vtable sizes. | 
|  | 150 | uint64_t MinByte = 0; | 
|  | 151 | for (const VirtualCallTarget &Target : Targets) { | 
|  | 152 | if (IsAfter) | 
|  | 153 | MinByte = std::max(MinByte, Target.minAfterBytes()); | 
|  | 154 | else | 
|  | 155 | MinByte = std::max(MinByte, Target.minBeforeBytes()); | 
|  | 156 | } | 
|  | 157 |  | 
|  | 158 | // Build a vector of arrays of bytes covering, for each target, a slice of the | 
|  | 159 | // used region (see AccumBitVector::BytesUsed in | 
|  | 160 | // llvm/Transforms/IPO/WholeProgramDevirt.h) starting at MinByte. Effectively, | 
|  | 161 | // this aligns the used regions to start at MinByte. | 
|  | 162 | // | 
|  | 163 | // In this example, A, B and C are vtables, # is a byte already allocated for | 
|  | 164 | // a virtual function pointer, AAAA... (etc.) are the used regions for the | 
|  | 165 | // vtables and Offset(X) is the value computed for the Offset variable below | 
|  | 166 | // for X. | 
|  | 167 | // | 
|  | 168 | //                    Offset(A) | 
|  | 169 | //                    |       | | 
|  | 170 | //                            |MinByte | 
|  | 171 | // A: ################AAAAAAAA|AAAAAAAA | 
|  | 172 | // B: ########BBBBBBBBBBBBBBBB|BBBB | 
|  | 173 | // C: ########################|CCCCCCCCCCCCCCCC | 
|  | 174 | //            |   Offset(B)   | | 
|  | 175 | // | 
|  | 176 | // This code produces the slices of A, B and C that appear after the divider | 
|  | 177 | // at MinByte. | 
|  | 178 | std::vector<ArrayRef<uint8_t>> Used; | 
|  | 179 | for (const VirtualCallTarget &Target : Targets) { | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 180 | ArrayRef<uint8_t> VTUsed = IsAfter ? Target.TM->Bits->After.BytesUsed | 
|  | 181 | : Target.TM->Bits->Before.BytesUsed; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 182 | uint64_t Offset = IsAfter ? MinByte - Target.minAfterBytes() | 
|  | 183 | : MinByte - Target.minBeforeBytes(); | 
|  | 184 |  | 
|  | 185 | // Disregard used regions that are smaller than Offset. These are | 
|  | 186 | // effectively all-free regions that do not need to be checked. | 
|  | 187 | if (VTUsed.size() > Offset) | 
|  | 188 | Used.push_back(VTUsed.slice(Offset)); | 
|  | 189 | } | 
|  | 190 |  | 
|  | 191 | if (Size == 1) { | 
|  | 192 | // Find a free bit in each member of Used. | 
|  | 193 | for (unsigned I = 0;; ++I) { | 
|  | 194 | uint8_t BitsUsed = 0; | 
|  | 195 | for (auto &&B : Used) | 
|  | 196 | if (I < B.size()) | 
|  | 197 | BitsUsed |= B[I]; | 
|  | 198 | if (BitsUsed != 0xff) | 
|  | 199 | return (MinByte + I) * 8 + | 
|  | 200 | countTrailingZeros(uint8_t(~BitsUsed), ZB_Undefined); | 
|  | 201 | } | 
|  | 202 | } else { | 
|  | 203 | // Find a free (Size/8) byte region in each member of Used. | 
|  | 204 | // FIXME: see if alignment helps. | 
|  | 205 | for (unsigned I = 0;; ++I) { | 
|  | 206 | for (auto &&B : Used) { | 
|  | 207 | unsigned Byte = 0; | 
|  | 208 | while ((I + Byte) < B.size() && Byte < (Size / 8)) { | 
|  | 209 | if (B[I + Byte]) | 
|  | 210 | goto NextI; | 
|  | 211 | ++Byte; | 
|  | 212 | } | 
|  | 213 | } | 
|  | 214 | return (MinByte + I) * 8; | 
|  | 215 | NextI:; | 
|  | 216 | } | 
|  | 217 | } | 
|  | 218 | } | 
|  | 219 |  | 
|  | 220 | void wholeprogramdevirt::setBeforeReturnValues( | 
|  | 221 | MutableArrayRef<VirtualCallTarget> Targets, uint64_t AllocBefore, | 
|  | 222 | unsigned BitWidth, int64_t &OffsetByte, uint64_t &OffsetBit) { | 
|  | 223 | if (BitWidth == 1) | 
|  | 224 | OffsetByte = -(AllocBefore / 8 + 1); | 
|  | 225 | else | 
|  | 226 | OffsetByte = -((AllocBefore + 7) / 8 + (BitWidth + 7) / 8); | 
|  | 227 | OffsetBit = AllocBefore % 8; | 
|  | 228 |  | 
|  | 229 | for (VirtualCallTarget &Target : Targets) { | 
|  | 230 | if (BitWidth == 1) | 
|  | 231 | Target.setBeforeBit(AllocBefore); | 
|  | 232 | else | 
|  | 233 | Target.setBeforeBytes(AllocBefore, (BitWidth + 7) / 8); | 
|  | 234 | } | 
|  | 235 | } | 
|  | 236 |  | 
|  | 237 | void wholeprogramdevirt::setAfterReturnValues( | 
|  | 238 | MutableArrayRef<VirtualCallTarget> Targets, uint64_t AllocAfter, | 
|  | 239 | unsigned BitWidth, int64_t &OffsetByte, uint64_t &OffsetBit) { | 
|  | 240 | if (BitWidth == 1) | 
|  | 241 | OffsetByte = AllocAfter / 8; | 
|  | 242 | else | 
|  | 243 | OffsetByte = (AllocAfter + 7) / 8; | 
|  | 244 | OffsetBit = AllocAfter % 8; | 
|  | 245 |  | 
|  | 246 | for (VirtualCallTarget &Target : Targets) { | 
|  | 247 | if (BitWidth == 1) | 
|  | 248 | Target.setAfterBit(AllocAfter); | 
|  | 249 | else | 
|  | 250 | Target.setAfterBytes(AllocAfter, (BitWidth + 7) / 8); | 
|  | 251 | } | 
|  | 252 | } | 
|  | 253 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 254 | VirtualCallTarget::VirtualCallTarget(Function *Fn, const TypeMemberInfo *TM) | 
|  | 255 | : Fn(Fn), TM(TM), | 
| Ivan Krasin | 89439a7 | 2016-08-12 01:40:10 +0000 | [diff] [blame] | 256 | IsBigEndian(Fn->getParent()->getDataLayout().isBigEndian()), WasDevirt(false) {} | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 257 |  | 
|  | 258 | namespace { | 
|  | 259 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 260 | // A slot in a set of virtual tables. The TypeID identifies the set of virtual | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 261 | // tables, and the ByteOffset is the offset in bytes from the address point to | 
|  | 262 | // the virtual function pointer. | 
|  | 263 | struct VTableSlot { | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 264 | Metadata *TypeID; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 265 | uint64_t ByteOffset; | 
|  | 266 | }; | 
|  | 267 |  | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 268 | } // end anonymous namespace | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 269 |  | 
| Peter Collingbourne | 9b65652 | 2016-02-09 23:01:38 +0000 | [diff] [blame] | 270 | namespace llvm { | 
|  | 271 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 272 | template <> struct DenseMapInfo<VTableSlot> { | 
|  | 273 | static VTableSlot getEmptyKey() { | 
|  | 274 | return {DenseMapInfo<Metadata *>::getEmptyKey(), | 
|  | 275 | DenseMapInfo<uint64_t>::getEmptyKey()}; | 
|  | 276 | } | 
|  | 277 | static VTableSlot getTombstoneKey() { | 
|  | 278 | return {DenseMapInfo<Metadata *>::getTombstoneKey(), | 
|  | 279 | DenseMapInfo<uint64_t>::getTombstoneKey()}; | 
|  | 280 | } | 
|  | 281 | static unsigned getHashValue(const VTableSlot &I) { | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 282 | return DenseMapInfo<Metadata *>::getHashValue(I.TypeID) ^ | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 283 | DenseMapInfo<uint64_t>::getHashValue(I.ByteOffset); | 
|  | 284 | } | 
|  | 285 | static bool isEqual(const VTableSlot &LHS, | 
|  | 286 | const VTableSlot &RHS) { | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 287 | return LHS.TypeID == RHS.TypeID && LHS.ByteOffset == RHS.ByteOffset; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 288 | } | 
|  | 289 | }; | 
|  | 290 |  | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 291 | template <> struct DenseMapInfo<VTableSlotSummary> { | 
|  | 292 | static VTableSlotSummary getEmptyKey() { | 
|  | 293 | return {DenseMapInfo<StringRef>::getEmptyKey(), | 
|  | 294 | DenseMapInfo<uint64_t>::getEmptyKey()}; | 
|  | 295 | } | 
|  | 296 | static VTableSlotSummary getTombstoneKey() { | 
|  | 297 | return {DenseMapInfo<StringRef>::getTombstoneKey(), | 
|  | 298 | DenseMapInfo<uint64_t>::getTombstoneKey()}; | 
|  | 299 | } | 
|  | 300 | static unsigned getHashValue(const VTableSlotSummary &I) { | 
|  | 301 | return DenseMapInfo<StringRef>::getHashValue(I.TypeID) ^ | 
|  | 302 | DenseMapInfo<uint64_t>::getHashValue(I.ByteOffset); | 
|  | 303 | } | 
|  | 304 | static bool isEqual(const VTableSlotSummary &LHS, | 
|  | 305 | const VTableSlotSummary &RHS) { | 
|  | 306 | return LHS.TypeID == RHS.TypeID && LHS.ByteOffset == RHS.ByteOffset; | 
|  | 307 | } | 
|  | 308 | }; | 
|  | 309 |  | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 310 | } // end namespace llvm | 
| Peter Collingbourne | 9b65652 | 2016-02-09 23:01:38 +0000 | [diff] [blame] | 311 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 312 | namespace { | 
|  | 313 |  | 
|  | 314 | // A virtual call site. VTable is the loaded virtual table pointer, and CS is | 
|  | 315 | // the indirect virtual call. | 
|  | 316 | struct VirtualCallSite { | 
|  | 317 | Value *VTable; | 
|  | 318 | CallSite CS; | 
|  | 319 |  | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 320 | // If non-null, this field points to the associated unsafe use count stored in | 
|  | 321 | // the DevirtModule::NumUnsafeUsesForTypeTest map below. See the description | 
|  | 322 | // of that field for details. | 
|  | 323 | unsigned *NumUnsafeUses; | 
|  | 324 |  | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 325 | void | 
|  | 326 | emitRemark(const StringRef OptName, const StringRef TargetName, | 
|  | 327 | function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter) { | 
| Ivan Krasin | 5474645 | 2016-07-12 02:38:37 +0000 | [diff] [blame] | 328 | Function *F = CS.getCaller(); | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 329 | DebugLoc DLoc = CS->getDebugLoc(); | 
|  | 330 | BasicBlock *Block = CS.getParent(); | 
|  | 331 |  | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 332 | using namespace ore; | 
| Peter Collingbourne | 9110cb4 | 2018-01-05 00:27:51 +0000 | [diff] [blame] | 333 | OREGetter(F).emit(OptimizationRemark(DEBUG_TYPE, OptName, DLoc, Block) | 
|  | 334 | << NV("Optimization", OptName) | 
|  | 335 | << ": devirtualized a call to " | 
|  | 336 | << NV("FunctionName", TargetName)); | 
| Ivan Krasin | 5474645 | 2016-07-12 02:38:37 +0000 | [diff] [blame] | 337 | } | 
|  | 338 |  | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 339 | void replaceAndErase( | 
|  | 340 | const StringRef OptName, const StringRef TargetName, bool RemarksEnabled, | 
|  | 341 | function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter, | 
|  | 342 | Value *New) { | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 343 | if (RemarksEnabled) | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 344 | emitRemark(OptName, TargetName, OREGetter); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 345 | CS->replaceAllUsesWith(New); | 
|  | 346 | if (auto II = dyn_cast<InvokeInst>(CS.getInstruction())) { | 
|  | 347 | BranchInst::Create(II->getNormalDest(), CS.getInstruction()); | 
|  | 348 | II->getUnwindDest()->removePredecessor(II->getParent()); | 
|  | 349 | } | 
|  | 350 | CS->eraseFromParent(); | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 351 | // This use is no longer unsafe. | 
|  | 352 | if (NumUnsafeUses) | 
|  | 353 | --*NumUnsafeUses; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 354 | } | 
|  | 355 | }; | 
|  | 356 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 357 | // Call site information collected for a specific VTableSlot and possibly a list | 
|  | 358 | // of constant integer arguments. The grouping by arguments is handled by the | 
|  | 359 | // VTableSlotInfo class. | 
|  | 360 | struct CallSiteInfo { | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 361 | /// The set of call sites for this slot. Used during regular LTO and the | 
|  | 362 | /// import phase of ThinLTO (as well as the export phase of ThinLTO for any | 
|  | 363 | /// call sites that appear in the merged module itself); in each of these | 
|  | 364 | /// cases we are directly operating on the call sites at the IR level. | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 365 | std::vector<VirtualCallSite> CallSites; | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 366 |  | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 367 | /// Whether all call sites represented by this CallSiteInfo, including those | 
|  | 368 | /// in summaries, have been devirtualized. This starts off as true because a | 
|  | 369 | /// default constructed CallSiteInfo represents no call sites. | 
|  | 370 | bool AllCallSitesDevirted = true; | 
|  | 371 |  | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 372 | // These fields are used during the export phase of ThinLTO and reflect | 
|  | 373 | // information collected from function summaries. | 
|  | 374 |  | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 375 | /// Whether any function summary contains an llvm.assume(llvm.type.test) for | 
|  | 376 | /// this slot. | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 377 | bool SummaryHasTypeTestAssumeUsers = false; | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 378 |  | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 379 | /// CFI-specific: a vector containing the list of function summaries that use | 
|  | 380 | /// the llvm.type.checked.load intrinsic and therefore will require | 
|  | 381 | /// resolutions for llvm.type.test in order to implement CFI checks if | 
|  | 382 | /// devirtualization was unsuccessful. If devirtualization was successful, the | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 383 | /// pass will clear this vector by calling markDevirt(). If at the end of the | 
|  | 384 | /// pass the vector is non-empty, we will need to add a use of llvm.type.test | 
|  | 385 | /// to each of the function summaries in the vector. | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 386 | std::vector<FunctionSummary *> SummaryTypeCheckedLoadUsers; | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 387 | std::vector<FunctionSummary *> SummaryTypeTestAssumeUsers; | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 388 |  | 
|  | 389 | bool isExported() const { | 
|  | 390 | return SummaryHasTypeTestAssumeUsers || | 
|  | 391 | !SummaryTypeCheckedLoadUsers.empty(); | 
|  | 392 | } | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 393 |  | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 394 | void addSummaryTypeCheckedLoadUser(FunctionSummary *FS) { | 
|  | 395 | SummaryTypeCheckedLoadUsers.push_back(FS); | 
|  | 396 | AllCallSitesDevirted = false; | 
|  | 397 | } | 
|  | 398 |  | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 399 | void addSummaryTypeTestAssumeUser(FunctionSummary *FS) { | 
|  | 400 | SummaryTypeTestAssumeUsers.push_back(FS); | 
| Eugene Leviant | 943afb5 | 2019-10-17 07:46:18 +0000 | [diff] [blame] | 401 | SummaryHasTypeTestAssumeUsers = true; | 
|  | 402 | AllCallSitesDevirted = false; | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 403 | } | 
|  | 404 |  | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 405 | void markDevirt() { | 
|  | 406 | AllCallSitesDevirted = true; | 
|  | 407 |  | 
|  | 408 | // As explained in the comment for SummaryTypeCheckedLoadUsers. | 
|  | 409 | SummaryTypeCheckedLoadUsers.clear(); | 
|  | 410 | } | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 411 | }; | 
|  | 412 |  | 
|  | 413 | // Call site information collected for a specific VTableSlot. | 
|  | 414 | struct VTableSlotInfo { | 
|  | 415 | // The set of call sites which do not have all constant integer arguments | 
|  | 416 | // (excluding "this"). | 
|  | 417 | CallSiteInfo CSInfo; | 
|  | 418 |  | 
|  | 419 | // The set of call sites with all constant integer arguments (excluding | 
|  | 420 | // "this"), grouped by argument list. | 
|  | 421 | std::map<std::vector<uint64_t>, CallSiteInfo> ConstCSInfo; | 
|  | 422 |  | 
|  | 423 | void addCallSite(Value *VTable, CallSite CS, unsigned *NumUnsafeUses); | 
|  | 424 |  | 
|  | 425 | private: | 
|  | 426 | CallSiteInfo &findCallSiteInfo(CallSite CS); | 
|  | 427 | }; | 
|  | 428 |  | 
|  | 429 | CallSiteInfo &VTableSlotInfo::findCallSiteInfo(CallSite CS) { | 
|  | 430 | std::vector<uint64_t> Args; | 
|  | 431 | auto *CI = dyn_cast<IntegerType>(CS.getType()); | 
|  | 432 | if (!CI || CI->getBitWidth() > 64 || CS.arg_empty()) | 
|  | 433 | return CSInfo; | 
|  | 434 | for (auto &&Arg : make_range(CS.arg_begin() + 1, CS.arg_end())) { | 
|  | 435 | auto *CI = dyn_cast<ConstantInt>(Arg); | 
|  | 436 | if (!CI || CI->getBitWidth() > 64) | 
|  | 437 | return CSInfo; | 
|  | 438 | Args.push_back(CI->getZExtValue()); | 
|  | 439 | } | 
|  | 440 | return ConstCSInfo[Args]; | 
|  | 441 | } | 
|  | 442 |  | 
|  | 443 | void VTableSlotInfo::addCallSite(Value *VTable, CallSite CS, | 
|  | 444 | unsigned *NumUnsafeUses) { | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 445 | auto &CSI = findCallSiteInfo(CS); | 
|  | 446 | CSI.AllCallSitesDevirted = false; | 
|  | 447 | CSI.CallSites.push_back({VTable, CS, NumUnsafeUses}); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 448 | } | 
|  | 449 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 450 | struct DevirtModule { | 
|  | 451 | Module &M; | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 452 | function_ref<AAResults &(Function &)> AARGetter; | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 453 | function_ref<DominatorTree &(Function &)> LookupDomTree; | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 454 |  | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 455 | ModuleSummaryIndex *ExportSummary; | 
|  | 456 | const ModuleSummaryIndex *ImportSummary; | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 457 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 458 | IntegerType *Int8Ty; | 
|  | 459 | PointerType *Int8PtrTy; | 
|  | 460 | IntegerType *Int32Ty; | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 461 | IntegerType *Int64Ty; | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 462 | IntegerType *IntPtrTy; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 463 |  | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 464 | bool RemarksEnabled; | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 465 | function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter; | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 466 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 467 | MapVector<VTableSlot, VTableSlotInfo> CallSlots; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 468 |  | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 469 | // This map keeps track of the number of "unsafe" uses of a loaded function | 
|  | 470 | // pointer. The key is the associated llvm.type.test intrinsic call generated | 
|  | 471 | // by this pass. An unsafe use is one that calls the loaded function pointer | 
|  | 472 | // directly. Every time we eliminate an unsafe use (for example, by | 
|  | 473 | // devirtualizing it or by applying virtual constant propagation), we | 
|  | 474 | // decrement the value stored in this map. If a value reaches zero, we can | 
|  | 475 | // eliminate the type check by RAUWing the associated llvm.type.test call with | 
|  | 476 | // true. | 
|  | 477 | std::map<CallInst *, unsigned> NumUnsafeUsesForTypeTest; | 
|  | 478 |  | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 479 | DevirtModule(Module &M, function_ref<AAResults &(Function &)> AARGetter, | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 480 | function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter, | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 481 | function_ref<DominatorTree &(Function &)> LookupDomTree, | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 482 | ModuleSummaryIndex *ExportSummary, | 
|  | 483 | const ModuleSummaryIndex *ImportSummary) | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 484 | : M(M), AARGetter(AARGetter), LookupDomTree(LookupDomTree), | 
|  | 485 | ExportSummary(ExportSummary), ImportSummary(ImportSummary), | 
|  | 486 | Int8Ty(Type::getInt8Ty(M.getContext())), | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 487 | Int8PtrTy(Type::getInt8PtrTy(M.getContext())), | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 488 | Int32Ty(Type::getInt32Ty(M.getContext())), | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 489 | Int64Ty(Type::getInt64Ty(M.getContext())), | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 490 | IntPtrTy(M.getDataLayout().getIntPtrType(M.getContext(), 0)), | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 491 | RemarksEnabled(areRemarksEnabled()), OREGetter(OREGetter) { | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 492 | assert(!(ExportSummary && ImportSummary)); | 
|  | 493 | } | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 494 |  | 
|  | 495 | bool areRemarksEnabled(); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 496 |  | 
| Teresa Johnson | c8e3686 | 2019-12-06 12:13:34 -0800 | [diff] [blame] | 497 | void scanTypeTestUsers(Function *TypeTestFunc); | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 498 | void scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc); | 
|  | 499 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 500 | void buildTypeIdentifierMap( | 
|  | 501 | std::vector<VTableBits> &Bits, | 
|  | 502 | DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap); | 
|  | 503 | bool | 
|  | 504 | tryFindVirtualCallTargets(std::vector<VirtualCallTarget> &TargetsForSlot, | 
|  | 505 | const std::set<TypeMemberInfo> &TypeMemberInfos, | 
|  | 506 | uint64_t ByteOffset); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 507 |  | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 508 | void applySingleImplDevirt(VTableSlotInfo &SlotInfo, Constant *TheFn, | 
|  | 509 | bool &IsExported); | 
| Eugene Leviant | 943afb5 | 2019-10-17 07:46:18 +0000 | [diff] [blame] | 510 | bool trySingleImplDevirt(ModuleSummaryIndex *ExportSummary, | 
|  | 511 | MutableArrayRef<VirtualCallTarget> TargetsForSlot, | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 512 | VTableSlotInfo &SlotInfo, | 
|  | 513 | WholeProgramDevirtResolution *Res); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 514 |  | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 515 | void applyICallBranchFunnel(VTableSlotInfo &SlotInfo, Constant *JT, | 
|  | 516 | bool &IsExported); | 
|  | 517 | void tryICallBranchFunnel(MutableArrayRef<VirtualCallTarget> TargetsForSlot, | 
|  | 518 | VTableSlotInfo &SlotInfo, | 
|  | 519 | WholeProgramDevirtResolution *Res, VTableSlot Slot); | 
|  | 520 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 521 | bool tryEvaluateFunctionsWithArgs( | 
|  | 522 | MutableArrayRef<VirtualCallTarget> TargetsForSlot, | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 523 | ArrayRef<uint64_t> Args); | 
|  | 524 |  | 
|  | 525 | void applyUniformRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, | 
|  | 526 | uint64_t TheRetVal); | 
|  | 527 | bool tryUniformRetValOpt(MutableArrayRef<VirtualCallTarget> TargetsForSlot, | 
| Peter Collingbourne | 77a8d56 | 2017-03-04 01:34:53 +0000 | [diff] [blame] | 528 | CallSiteInfo &CSInfo, | 
|  | 529 | WholeProgramDevirtResolution::ByArg *Res); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 530 |  | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 531 | // Returns the global symbol name that is used to export information about the | 
|  | 532 | // given vtable slot and list of arguments. | 
|  | 533 | std::string getGlobalName(VTableSlot Slot, ArrayRef<uint64_t> Args, | 
|  | 534 | StringRef Name); | 
|  | 535 |  | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 536 | bool shouldExportConstantsAsAbsoluteSymbols(); | 
|  | 537 |  | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 538 | // This function is called during the export phase to create a symbol | 
|  | 539 | // definition containing information about the given vtable slot and list of | 
|  | 540 | // arguments. | 
|  | 541 | void exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name, | 
|  | 542 | Constant *C); | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 543 | void exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args, StringRef Name, | 
|  | 544 | uint32_t Const, uint32_t &Storage); | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 545 |  | 
|  | 546 | // This function is called during the import phase to create a reference to | 
|  | 547 | // the symbol definition created during the export phase. | 
|  | 548 | Constant *importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args, | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 549 | StringRef Name); | 
|  | 550 | Constant *importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args, | 
|  | 551 | StringRef Name, IntegerType *IntTy, | 
|  | 552 | uint32_t Storage); | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 553 |  | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 554 | Constant *getMemberAddr(const TypeMemberInfo *M); | 
|  | 555 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 556 | void applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, bool IsOne, | 
|  | 557 | Constant *UniqueMemberAddr); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 558 | bool tryUniqueRetValOpt(unsigned BitWidth, | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 559 | MutableArrayRef<VirtualCallTarget> TargetsForSlot, | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 560 | CallSiteInfo &CSInfo, | 
|  | 561 | WholeProgramDevirtResolution::ByArg *Res, | 
|  | 562 | VTableSlot Slot, ArrayRef<uint64_t> Args); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 563 |  | 
|  | 564 | void applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName, | 
|  | 565 | Constant *Byte, Constant *Bit); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 566 | bool tryVirtualConstProp(MutableArrayRef<VirtualCallTarget> TargetsForSlot, | 
| Peter Collingbourne | 77a8d56 | 2017-03-04 01:34:53 +0000 | [diff] [blame] | 567 | VTableSlotInfo &SlotInfo, | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 568 | WholeProgramDevirtResolution *Res, VTableSlot Slot); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 569 |  | 
|  | 570 | void rebuildGlobal(VTableBits &B); | 
|  | 571 |  | 
| Peter Collingbourne | 6d284fa | 2017-03-09 00:21:25 +0000 | [diff] [blame] | 572 | // Apply the summary resolution for Slot to all virtual calls in SlotInfo. | 
|  | 573 | void importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo); | 
|  | 574 |  | 
|  | 575 | // If we were able to eliminate all unsafe uses for a type checked load, | 
|  | 576 | // eliminate the associated type tests by replacing them with true. | 
|  | 577 | void removeRedundantTypeTests(); | 
|  | 578 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 579 | bool run(); | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 580 |  | 
|  | 581 | // Lower the module using the action and summary passed as command line | 
|  | 582 | // arguments. For testing purposes only. | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 583 | static bool | 
|  | 584 | runForTesting(Module &M, function_ref<AAResults &(Function &)> AARGetter, | 
|  | 585 | function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter, | 
|  | 586 | function_ref<DominatorTree &(Function &)> LookupDomTree); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 587 | }; | 
|  | 588 |  | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 589 | struct DevirtIndex { | 
|  | 590 | ModuleSummaryIndex &ExportSummary; | 
|  | 591 | // The set in which to record GUIDs exported from their module by | 
|  | 592 | // devirtualization, used by client to ensure they are not internalized. | 
|  | 593 | std::set<GlobalValue::GUID> &ExportedGUIDs; | 
|  | 594 | // A map in which to record the information necessary to locate the WPD | 
|  | 595 | // resolution for local targets in case they are exported by cross module | 
|  | 596 | // importing. | 
|  | 597 | std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap; | 
|  | 598 |  | 
|  | 599 | MapVector<VTableSlotSummary, VTableSlotInfo> CallSlots; | 
|  | 600 |  | 
|  | 601 | DevirtIndex( | 
|  | 602 | ModuleSummaryIndex &ExportSummary, | 
|  | 603 | std::set<GlobalValue::GUID> &ExportedGUIDs, | 
|  | 604 | std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) | 
|  | 605 | : ExportSummary(ExportSummary), ExportedGUIDs(ExportedGUIDs), | 
|  | 606 | LocalWPDTargetsMap(LocalWPDTargetsMap) {} | 
|  | 607 |  | 
|  | 608 | bool tryFindVirtualCallTargets(std::vector<ValueInfo> &TargetsForSlot, | 
|  | 609 | const TypeIdCompatibleVtableInfo TIdInfo, | 
|  | 610 | uint64_t ByteOffset); | 
|  | 611 |  | 
|  | 612 | bool trySingleImplDevirt(MutableArrayRef<ValueInfo> TargetsForSlot, | 
|  | 613 | VTableSlotSummary &SlotSummary, | 
|  | 614 | VTableSlotInfo &SlotInfo, | 
|  | 615 | WholeProgramDevirtResolution *Res, | 
|  | 616 | std::set<ValueInfo> &DevirtTargets); | 
|  | 617 |  | 
|  | 618 | void run(); | 
|  | 619 | }; | 
|  | 620 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 621 | struct WholeProgramDevirt : public ModulePass { | 
|  | 622 | static char ID; | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 623 |  | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 624 | bool UseCommandLine = false; | 
|  | 625 |  | 
| Simon Pilgrim | 39c0829 | 2019-11-14 13:54:29 +0000 | [diff] [blame] | 626 | ModuleSummaryIndex *ExportSummary = nullptr; | 
|  | 627 | const ModuleSummaryIndex *ImportSummary = nullptr; | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 628 |  | 
|  | 629 | WholeProgramDevirt() : ModulePass(ID), UseCommandLine(true) { | 
|  | 630 | initializeWholeProgramDevirtPass(*PassRegistry::getPassRegistry()); | 
|  | 631 | } | 
|  | 632 |  | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 633 | WholeProgramDevirt(ModuleSummaryIndex *ExportSummary, | 
|  | 634 | const ModuleSummaryIndex *ImportSummary) | 
|  | 635 | : ModulePass(ID), ExportSummary(ExportSummary), | 
|  | 636 | ImportSummary(ImportSummary) { | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 637 | initializeWholeProgramDevirtPass(*PassRegistry::getPassRegistry()); | 
|  | 638 | } | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 639 |  | 
|  | 640 | bool runOnModule(Module &M) override { | 
| Andrew Kaylor | aa641a5 | 2016-04-22 22:06:11 +0000 | [diff] [blame] | 641 | if (skipModule(M)) | 
|  | 642 | return false; | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 643 |  | 
| Peter Collingbourne | 9110cb4 | 2018-01-05 00:27:51 +0000 | [diff] [blame] | 644 | // In the new pass manager, we can request the optimization | 
|  | 645 | // remark emitter pass on a per-function-basis, which the | 
|  | 646 | // OREGetter will do for us. | 
|  | 647 | // In the old pass manager, this is harder, so we just build | 
|  | 648 | // an optimization remark emitter on the fly, when we need it. | 
|  | 649 | std::unique_ptr<OptimizationRemarkEmitter> ORE; | 
|  | 650 | auto OREGetter = [&](Function *F) -> OptimizationRemarkEmitter & { | 
| Jonas Devlieghere | 0eaee54 | 2019-08-15 15:54:37 +0000 | [diff] [blame] | 651 | ORE = std::make_unique<OptimizationRemarkEmitter>(F); | 
| Peter Collingbourne | 9110cb4 | 2018-01-05 00:27:51 +0000 | [diff] [blame] | 652 | return *ORE; | 
|  | 653 | }; | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 654 |  | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 655 | auto LookupDomTree = [this](Function &F) -> DominatorTree & { | 
|  | 656 | return this->getAnalysis<DominatorTreeWrapperPass>(F).getDomTree(); | 
|  | 657 | }; | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 658 |  | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 659 | if (UseCommandLine) | 
|  | 660 | return DevirtModule::runForTesting(M, LegacyAARGetter(*this), OREGetter, | 
|  | 661 | LookupDomTree); | 
|  | 662 |  | 
|  | 663 | return DevirtModule(M, LegacyAARGetter(*this), OREGetter, LookupDomTree, | 
|  | 664 | ExportSummary, ImportSummary) | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 665 | .run(); | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 666 | } | 
|  | 667 |  | 
|  | 668 | void getAnalysisUsage(AnalysisUsage &AU) const override { | 
|  | 669 | AU.addRequired<AssumptionCacheTracker>(); | 
|  | 670 | AU.addRequired<TargetLibraryInfoWrapperPass>(); | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 671 | AU.addRequired<DominatorTreeWrapperPass>(); | 
| Andrew Kaylor | aa641a5 | 2016-04-22 22:06:11 +0000 | [diff] [blame] | 672 | } | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 673 | }; | 
|  | 674 |  | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 675 | } // end anonymous namespace | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 676 |  | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 677 | INITIALIZE_PASS_BEGIN(WholeProgramDevirt, "wholeprogramdevirt", | 
|  | 678 | "Whole program devirtualization", false, false) | 
|  | 679 | INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) | 
|  | 680 | INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass) | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 681 | INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 682 | INITIALIZE_PASS_END(WholeProgramDevirt, "wholeprogramdevirt", | 
|  | 683 | "Whole program devirtualization", false, false) | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 684 | char WholeProgramDevirt::ID = 0; | 
|  | 685 |  | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 686 | ModulePass * | 
|  | 687 | llvm::createWholeProgramDevirtPass(ModuleSummaryIndex *ExportSummary, | 
|  | 688 | const ModuleSummaryIndex *ImportSummary) { | 
|  | 689 | return new WholeProgramDevirt(ExportSummary, ImportSummary); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 690 | } | 
|  | 691 |  | 
| Chandler Carruth | 164a2aa6 | 2016-06-17 00:11:01 +0000 | [diff] [blame] | 692 | PreservedAnalyses WholeProgramDevirtPass::run(Module &M, | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 693 | ModuleAnalysisManager &AM) { | 
|  | 694 | auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager(); | 
|  | 695 | auto AARGetter = [&](Function &F) -> AAResults & { | 
|  | 696 | return FAM.getResult<AAManager>(F); | 
|  | 697 | }; | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 698 | auto OREGetter = [&](Function *F) -> OptimizationRemarkEmitter & { | 
|  | 699 | return FAM.getResult<OptimizationRemarkEmitterAnalysis>(*F); | 
|  | 700 | }; | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 701 | auto LookupDomTree = [&FAM](Function &F) -> DominatorTree & { | 
|  | 702 | return FAM.getResult<DominatorTreeAnalysis>(F); | 
|  | 703 | }; | 
|  | 704 | if (!DevirtModule(M, AARGetter, OREGetter, LookupDomTree, ExportSummary, | 
|  | 705 | ImportSummary) | 
| Teresa Johnson | 28023db | 2018-07-19 14:51:32 +0000 | [diff] [blame] | 706 | .run()) | 
| Davide Italiano | d737dd2 | 2016-06-14 21:44:19 +0000 | [diff] [blame] | 707 | return PreservedAnalyses::all(); | 
|  | 708 | return PreservedAnalyses::none(); | 
|  | 709 | } | 
|  | 710 |  | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 711 | namespace llvm { | 
|  | 712 | void runWholeProgramDevirtOnIndex( | 
|  | 713 | ModuleSummaryIndex &Summary, std::set<GlobalValue::GUID> &ExportedGUIDs, | 
|  | 714 | std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) { | 
|  | 715 | DevirtIndex(Summary, ExportedGUIDs, LocalWPDTargetsMap).run(); | 
|  | 716 | } | 
|  | 717 |  | 
|  | 718 | void updateIndexWPDForExports( | 
|  | 719 | ModuleSummaryIndex &Summary, | 
| evgeny | 3d708bf | 2019-11-15 16:13:19 +0300 | [diff] [blame] | 720 | function_ref<bool(StringRef, ValueInfo)> isExported, | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 721 | std::map<ValueInfo, std::vector<VTableSlotSummary>> &LocalWPDTargetsMap) { | 
|  | 722 | for (auto &T : LocalWPDTargetsMap) { | 
|  | 723 | auto &VI = T.first; | 
|  | 724 | // This was enforced earlier during trySingleImplDevirt. | 
|  | 725 | assert(VI.getSummaryList().size() == 1 && | 
|  | 726 | "Devirt of local target has more than one copy"); | 
|  | 727 | auto &S = VI.getSummaryList()[0]; | 
| evgeny | 3d708bf | 2019-11-15 16:13:19 +0300 | [diff] [blame] | 728 | if (!isExported(S->modulePath(), VI)) | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 729 | continue; | 
|  | 730 |  | 
|  | 731 | // It's been exported by a cross module import. | 
|  | 732 | for (auto &SlotSummary : T.second) { | 
|  | 733 | auto *TIdSum = Summary.getTypeIdSummary(SlotSummary.TypeID); | 
|  | 734 | assert(TIdSum); | 
|  | 735 | auto WPDRes = TIdSum->WPDRes.find(SlotSummary.ByteOffset); | 
|  | 736 | assert(WPDRes != TIdSum->WPDRes.end()); | 
|  | 737 | WPDRes->second.SingleImplName = ModuleSummaryIndex::getGlobalNameForLocal( | 
|  | 738 | WPDRes->second.SingleImplName, | 
|  | 739 | Summary.getModuleHash(S->modulePath())); | 
|  | 740 | } | 
|  | 741 | } | 
|  | 742 | } | 
|  | 743 |  | 
|  | 744 | } // end namespace llvm | 
|  | 745 |  | 
| Evgeny Leviant | 8973fae | 2020-01-24 00:31:39 -0800 | [diff] [blame^] | 746 | static Error checkCombinedSummaryForTesting(ModuleSummaryIndex *Summary) { | 
|  | 747 | // Check that summary index contains regular LTO module when performing | 
|  | 748 | // export to prevent occasional use of index from pure ThinLTO compilation | 
|  | 749 | // (-fno-split-lto-module). This kind of summary index is passed to | 
|  | 750 | // DevirtIndex::run, not to DevirtModule::run used by opt/runForTesting. | 
|  | 751 | const auto &ModPaths = Summary->modulePaths(); | 
|  | 752 | if (ClSummaryAction != PassSummaryAction::Import && | 
|  | 753 | ModPaths.find(ModuleSummaryIndex::getRegularLTOModuleName()) == | 
|  | 754 | ModPaths.end()) | 
|  | 755 | return createStringError( | 
|  | 756 | errc::invalid_argument, | 
|  | 757 | "combined summary should contain Regular LTO module"); | 
|  | 758 | return ErrorSuccess(); | 
|  | 759 | } | 
|  | 760 |  | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 761 | bool DevirtModule::runForTesting( | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 762 | Module &M, function_ref<AAResults &(Function &)> AARGetter, | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 763 | function_ref<OptimizationRemarkEmitter &(Function *)> OREGetter, | 
|  | 764 | function_ref<DominatorTree &(Function &)> LookupDomTree) { | 
| Evgeny Leviant | 8973fae | 2020-01-24 00:31:39 -0800 | [diff] [blame^] | 765 | std::unique_ptr<ModuleSummaryIndex> Summary = | 
|  | 766 | std::make_unique<ModuleSummaryIndex>(/*HaveGVs=*/false); | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 767 |  | 
|  | 768 | // Handle the command-line summary arguments. This code is for testing | 
|  | 769 | // purposes only, so we handle errors directly. | 
|  | 770 | if (!ClReadSummary.empty()) { | 
|  | 771 | ExitOnError ExitOnErr("-wholeprogramdevirt-read-summary: " + ClReadSummary + | 
|  | 772 | ": "); | 
|  | 773 | auto ReadSummaryFile = | 
|  | 774 | ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(ClReadSummary))); | 
| Evgeny Leviant | 8973fae | 2020-01-24 00:31:39 -0800 | [diff] [blame^] | 775 | if (Expected<std::unique_ptr<ModuleSummaryIndex>> SummaryOrErr = | 
|  | 776 | getModuleSummaryIndex(*ReadSummaryFile)) { | 
|  | 777 | Summary = std::move(*SummaryOrErr); | 
|  | 778 | ExitOnErr(checkCombinedSummaryForTesting(Summary.get())); | 
|  | 779 | } else { | 
|  | 780 | // Try YAML if we've failed with bitcode. | 
|  | 781 | consumeError(SummaryOrErr.takeError()); | 
|  | 782 | yaml::Input In(ReadSummaryFile->getBuffer()); | 
|  | 783 | In >> *Summary; | 
|  | 784 | ExitOnErr(errorCodeToError(In.error())); | 
|  | 785 | } | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 786 | } | 
|  | 787 |  | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 788 | bool Changed = | 
| Evgeny Leviant | 8973fae | 2020-01-24 00:31:39 -0800 | [diff] [blame^] | 789 | DevirtModule(M, AARGetter, OREGetter, LookupDomTree, | 
|  | 790 | ClSummaryAction == PassSummaryAction::Export ? Summary.get() | 
|  | 791 | : nullptr, | 
|  | 792 | ClSummaryAction == PassSummaryAction::Import ? Summary.get() | 
|  | 793 | : nullptr) | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 794 | .run(); | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 795 |  | 
|  | 796 | if (!ClWriteSummary.empty()) { | 
|  | 797 | ExitOnError ExitOnErr( | 
|  | 798 | "-wholeprogramdevirt-write-summary: " + ClWriteSummary + ": "); | 
|  | 799 | std::error_code EC; | 
| Evgeny Leviant | 8973fae | 2020-01-24 00:31:39 -0800 | [diff] [blame^] | 800 | if (StringRef(ClWriteSummary).endswith(".bc")) { | 
|  | 801 | raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::OF_None); | 
|  | 802 | ExitOnErr(errorCodeToError(EC)); | 
|  | 803 | WriteIndexToFile(*Summary, OS); | 
|  | 804 | } else { | 
|  | 805 | raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::OF_Text); | 
|  | 806 | ExitOnErr(errorCodeToError(EC)); | 
|  | 807 | yaml::Output Out(OS); | 
|  | 808 | Out << *Summary; | 
|  | 809 | } | 
| Peter Collingbourne | 2b33f65 | 2017-02-13 19:26:18 +0000 | [diff] [blame] | 810 | } | 
|  | 811 |  | 
|  | 812 | return Changed; | 
|  | 813 | } | 
|  | 814 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 815 | void DevirtModule::buildTypeIdentifierMap( | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 816 | std::vector<VTableBits> &Bits, | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 817 | DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap) { | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 818 | DenseMap<GlobalVariable *, VTableBits *> GVToBits; | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 819 | Bits.reserve(M.getGlobalList().size()); | 
|  | 820 | SmallVector<MDNode *, 2> Types; | 
|  | 821 | for (GlobalVariable &GV : M.globals()) { | 
|  | 822 | Types.clear(); | 
|  | 823 | GV.getMetadata(LLVMContext::MD_type, Types); | 
| Eugene Leviant | 2b70d61 | 2018-09-23 13:27:47 +0000 | [diff] [blame] | 824 | if (GV.isDeclaration() || Types.empty()) | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 825 | continue; | 
|  | 826 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 827 | VTableBits *&BitsPtr = GVToBits[&GV]; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 828 | if (!BitsPtr) { | 
|  | 829 | Bits.emplace_back(); | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 830 | Bits.back().GV = &GV; | 
|  | 831 | Bits.back().ObjectSize = | 
|  | 832 | M.getDataLayout().getTypeAllocSize(GV.getInitializer()->getType()); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 833 | BitsPtr = &Bits.back(); | 
|  | 834 | } | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 835 |  | 
|  | 836 | for (MDNode *Type : Types) { | 
|  | 837 | auto TypeID = Type->getOperand(1).get(); | 
|  | 838 |  | 
|  | 839 | uint64_t Offset = | 
|  | 840 | cast<ConstantInt>( | 
|  | 841 | cast<ConstantAsMetadata>(Type->getOperand(0))->getValue()) | 
|  | 842 | ->getZExtValue(); | 
|  | 843 |  | 
|  | 844 | TypeIdMap[TypeID].insert({BitsPtr, Offset}); | 
|  | 845 | } | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 846 | } | 
|  | 847 | } | 
|  | 848 |  | 
|  | 849 | bool DevirtModule::tryFindVirtualCallTargets( | 
|  | 850 | std::vector<VirtualCallTarget> &TargetsForSlot, | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 851 | const std::set<TypeMemberInfo> &TypeMemberInfos, uint64_t ByteOffset) { | 
|  | 852 | for (const TypeMemberInfo &TM : TypeMemberInfos) { | 
|  | 853 | if (!TM.Bits->GV->isConstant()) | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 854 | return false; | 
|  | 855 |  | 
| Peter Collingbourne | 8786754 | 2016-12-09 01:10:11 +0000 | [diff] [blame] | 856 | Constant *Ptr = getPointerAtOffset(TM.Bits->GV->getInitializer(), | 
| Oliver Stannard | 3b598b9 | 2019-10-17 09:58:57 +0000 | [diff] [blame] | 857 | TM.Offset + ByteOffset, M); | 
| Peter Collingbourne | 8786754 | 2016-12-09 01:10:11 +0000 | [diff] [blame] | 858 | if (!Ptr) | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 859 | return false; | 
|  | 860 |  | 
| Peter Collingbourne | 8786754 | 2016-12-09 01:10:11 +0000 | [diff] [blame] | 861 | auto Fn = dyn_cast<Function>(Ptr->stripPointerCasts()); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 862 | if (!Fn) | 
|  | 863 | return false; | 
|  | 864 |  | 
|  | 865 | // We can disregard __cxa_pure_virtual as a possible call target, as | 
|  | 866 | // calls to pure virtuals are UB. | 
|  | 867 | if (Fn->getName() == "__cxa_pure_virtual") | 
|  | 868 | continue; | 
|  | 869 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 870 | TargetsForSlot.push_back({Fn, &TM}); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 871 | } | 
|  | 872 |  | 
|  | 873 | // Give up if we couldn't find any targets. | 
|  | 874 | return !TargetsForSlot.empty(); | 
|  | 875 | } | 
|  | 876 |  | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 877 | bool DevirtIndex::tryFindVirtualCallTargets( | 
|  | 878 | std::vector<ValueInfo> &TargetsForSlot, const TypeIdCompatibleVtableInfo TIdInfo, | 
|  | 879 | uint64_t ByteOffset) { | 
| Mark de Wever | 098d334 | 2019-12-22 19:20:17 +0100 | [diff] [blame] | 880 | for (const TypeIdOffsetVtableInfo &P : TIdInfo) { | 
| Teresa Johnson | c844f88 | 2019-10-25 14:56:12 -0700 | [diff] [blame] | 881 | // Find the first non-available_externally linkage vtable initializer. | 
|  | 882 | // We can have multiple available_externally, linkonce_odr and weak_odr | 
|  | 883 | // vtable initializers, however we want to skip available_externally as they | 
|  | 884 | // do not have type metadata attached, and therefore the summary will not | 
| Teresa Johnson | 2cefb93 | 2020-01-13 13:50:41 -0800 | [diff] [blame] | 885 | // contain any vtable functions. We can also have multiple external | 
|  | 886 | // vtable initializers in the case of comdats, which we cannot check here. | 
|  | 887 | // The linker should give an error in this case. | 
| Teresa Johnson | c844f88 | 2019-10-25 14:56:12 -0700 | [diff] [blame] | 888 | // | 
|  | 889 | // Also, handle the case of same-named local Vtables with the same path | 
|  | 890 | // and therefore the same GUID. This can happen if there isn't enough | 
|  | 891 | // distinguishing path when compiling the source file. In that case we | 
|  | 892 | // conservatively return false early. | 
|  | 893 | const GlobalVarSummary *VS = nullptr; | 
|  | 894 | bool LocalFound = false; | 
|  | 895 | for (auto &S : P.VTableVI.getSummaryList()) { | 
|  | 896 | if (GlobalValue::isLocalLinkage(S->linkage())) { | 
|  | 897 | if (LocalFound) | 
|  | 898 | return false; | 
|  | 899 | LocalFound = true; | 
|  | 900 | } | 
| Teresa Johnson | 90e630a | 2020-01-23 17:26:51 -0800 | [diff] [blame] | 901 | if (!GlobalValue::isAvailableExternallyLinkage(S->linkage())) | 
| Teresa Johnson | 31441a3 | 2019-12-04 17:15:10 -0800 | [diff] [blame] | 902 | VS = cast<GlobalVarSummary>(S->getBaseObject()); | 
| Teresa Johnson | c844f88 | 2019-10-25 14:56:12 -0700 | [diff] [blame] | 903 | } | 
|  | 904 | if (!VS->isLive()) | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 905 | continue; | 
|  | 906 | for (auto VTP : VS->vTableFuncs()) { | 
|  | 907 | if (VTP.VTableOffset != P.AddressPointOffset + ByteOffset) | 
|  | 908 | continue; | 
|  | 909 |  | 
|  | 910 | TargetsForSlot.push_back(VTP.FuncVI); | 
|  | 911 | } | 
|  | 912 | } | 
|  | 913 |  | 
|  | 914 | // Give up if we couldn't find any targets. | 
|  | 915 | return !TargetsForSlot.empty(); | 
|  | 916 | } | 
|  | 917 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 918 | void DevirtModule::applySingleImplDevirt(VTableSlotInfo &SlotInfo, | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 919 | Constant *TheFn, bool &IsExported) { | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 920 | auto Apply = [&](CallSiteInfo &CSInfo) { | 
|  | 921 | for (auto &&VCallSite : CSInfo.CallSites) { | 
|  | 922 | if (RemarksEnabled) | 
| Teresa Johnson | b0a1d3b | 2018-08-14 03:00:16 +0000 | [diff] [blame] | 923 | VCallSite.emitRemark("single-impl", | 
|  | 924 | TheFn->stripPointerCasts()->getName(), OREGetter); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 925 | VCallSite.CS.setCalledFunction(ConstantExpr::getBitCast( | 
|  | 926 | TheFn, VCallSite.CS.getCalledValue()->getType())); | 
|  | 927 | // This use is no longer unsafe. | 
|  | 928 | if (VCallSite.NumUnsafeUses) | 
|  | 929 | --*VCallSite.NumUnsafeUses; | 
|  | 930 | } | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 931 | if (CSInfo.isExported()) | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 932 | IsExported = true; | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 933 | CSInfo.markDevirt(); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 934 | }; | 
|  | 935 | Apply(SlotInfo.CSInfo); | 
|  | 936 | for (auto &P : SlotInfo.ConstCSInfo) | 
|  | 937 | Apply(P.second); | 
|  | 938 | } | 
|  | 939 |  | 
| Eugene Leviant | 943afb5 | 2019-10-17 07:46:18 +0000 | [diff] [blame] | 940 | static bool AddCalls(VTableSlotInfo &SlotInfo, const ValueInfo &Callee) { | 
|  | 941 | // We can't add calls if we haven't seen a definition | 
|  | 942 | if (Callee.getSummaryList().empty()) | 
|  | 943 | return false; | 
|  | 944 |  | 
|  | 945 | // Insert calls into the summary index so that the devirtualized targets | 
|  | 946 | // are eligible for import. | 
|  | 947 | // FIXME: Annotate type tests with hotness. For now, mark these as hot | 
|  | 948 | // to better ensure we have the opportunity to inline them. | 
|  | 949 | bool IsExported = false; | 
|  | 950 | auto &S = Callee.getSummaryList()[0]; | 
|  | 951 | CalleeInfo CI(CalleeInfo::HotnessType::Hot, /* RelBF = */ 0); | 
|  | 952 | auto AddCalls = [&](CallSiteInfo &CSInfo) { | 
|  | 953 | for (auto *FS : CSInfo.SummaryTypeCheckedLoadUsers) { | 
|  | 954 | FS->addCall({Callee, CI}); | 
|  | 955 | IsExported |= S->modulePath() != FS->modulePath(); | 
|  | 956 | } | 
|  | 957 | for (auto *FS : CSInfo.SummaryTypeTestAssumeUsers) { | 
|  | 958 | FS->addCall({Callee, CI}); | 
|  | 959 | IsExported |= S->modulePath() != FS->modulePath(); | 
|  | 960 | } | 
|  | 961 | }; | 
|  | 962 | AddCalls(SlotInfo.CSInfo); | 
|  | 963 | for (auto &P : SlotInfo.ConstCSInfo) | 
|  | 964 | AddCalls(P.second); | 
|  | 965 | return IsExported; | 
|  | 966 | } | 
|  | 967 |  | 
| Peter Collingbourne | e236741 | 2017-02-15 02:13:08 +0000 | [diff] [blame] | 968 | bool DevirtModule::trySingleImplDevirt( | 
| Eugene Leviant | 943afb5 | 2019-10-17 07:46:18 +0000 | [diff] [blame] | 969 | ModuleSummaryIndex *ExportSummary, | 
|  | 970 | MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo, | 
|  | 971 | WholeProgramDevirtResolution *Res) { | 
| Peter Collingbourne | e236741 | 2017-02-15 02:13:08 +0000 | [diff] [blame] | 972 | // See if the program contains a single implementation of this virtual | 
|  | 973 | // function. | 
|  | 974 | Function *TheFn = TargetsForSlot[0].Fn; | 
|  | 975 | for (auto &&Target : TargetsForSlot) | 
|  | 976 | if (TheFn != Target.Fn) | 
|  | 977 | return false; | 
|  | 978 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 979 | // If so, update each call site to call that implementation directly. | 
| Peter Collingbourne | e236741 | 2017-02-15 02:13:08 +0000 | [diff] [blame] | 980 | if (RemarksEnabled) | 
|  | 981 | TargetsForSlot[0].WasDevirt = true; | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 982 |  | 
|  | 983 | bool IsExported = false; | 
|  | 984 | applySingleImplDevirt(SlotInfo, TheFn, IsExported); | 
|  | 985 | if (!IsExported) | 
|  | 986 | return false; | 
|  | 987 |  | 
|  | 988 | // If the only implementation has local linkage, we must promote to external | 
|  | 989 | // to make it visible to thin LTO objects. We can only get here during the | 
|  | 990 | // ThinLTO export phase. | 
|  | 991 | if (TheFn->hasLocalLinkage()) { | 
| Peter Collingbourne | 88a58cf | 2017-09-08 00:10:53 +0000 | [diff] [blame] | 992 | std::string NewName = (TheFn->getName() + "$merged").str(); | 
|  | 993 |  | 
|  | 994 | // Since we are renaming the function, any comdats with the same name must | 
|  | 995 | // also be renamed. This is required when targeting COFF, as the comdat name | 
|  | 996 | // must match one of the names of the symbols in the comdat. | 
|  | 997 | if (Comdat *C = TheFn->getComdat()) { | 
|  | 998 | if (C->getName() == TheFn->getName()) { | 
|  | 999 | Comdat *NewC = M.getOrInsertComdat(NewName); | 
|  | 1000 | NewC->setSelectionKind(C->getSelectionKind()); | 
|  | 1001 | for (GlobalObject &GO : M.global_objects()) | 
|  | 1002 | if (GO.getComdat() == C) | 
|  | 1003 | GO.setComdat(NewC); | 
|  | 1004 | } | 
|  | 1005 | } | 
|  | 1006 |  | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 1007 | TheFn->setLinkage(GlobalValue::ExternalLinkage); | 
|  | 1008 | TheFn->setVisibility(GlobalValue::HiddenVisibility); | 
| Peter Collingbourne | 88a58cf | 2017-09-08 00:10:53 +0000 | [diff] [blame] | 1009 | TheFn->setName(NewName); | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 1010 | } | 
| Eugene Leviant | 943afb5 | 2019-10-17 07:46:18 +0000 | [diff] [blame] | 1011 | if (ValueInfo TheFnVI = ExportSummary->getValueInfo(TheFn->getGUID())) | 
|  | 1012 | // Any needed promotion of 'TheFn' has already been done during | 
|  | 1013 | // LTO unit split, so we can ignore return value of AddCalls. | 
|  | 1014 | AddCalls(SlotInfo, TheFnVI); | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 1015 |  | 
|  | 1016 | Res->TheKind = WholeProgramDevirtResolution::SingleImpl; | 
|  | 1017 | Res->SingleImplName = TheFn->getName(); | 
|  | 1018 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1019 | return true; | 
|  | 1020 | } | 
|  | 1021 |  | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 1022 | bool DevirtIndex::trySingleImplDevirt(MutableArrayRef<ValueInfo> TargetsForSlot, | 
|  | 1023 | VTableSlotSummary &SlotSummary, | 
|  | 1024 | VTableSlotInfo &SlotInfo, | 
|  | 1025 | WholeProgramDevirtResolution *Res, | 
|  | 1026 | std::set<ValueInfo> &DevirtTargets) { | 
|  | 1027 | // See if the program contains a single implementation of this virtual | 
|  | 1028 | // function. | 
|  | 1029 | auto TheFn = TargetsForSlot[0]; | 
|  | 1030 | for (auto &&Target : TargetsForSlot) | 
|  | 1031 | if (TheFn != Target) | 
|  | 1032 | return false; | 
|  | 1033 |  | 
|  | 1034 | // Don't devirtualize if we don't have target definition. | 
|  | 1035 | auto Size = TheFn.getSummaryList().size(); | 
|  | 1036 | if (!Size) | 
|  | 1037 | return false; | 
|  | 1038 |  | 
|  | 1039 | // If the summary list contains multiple summaries where at least one is | 
|  | 1040 | // a local, give up, as we won't know which (possibly promoted) name to use. | 
|  | 1041 | for (auto &S : TheFn.getSummaryList()) | 
|  | 1042 | if (GlobalValue::isLocalLinkage(S->linkage()) && Size > 1) | 
|  | 1043 | return false; | 
|  | 1044 |  | 
|  | 1045 | // Collect functions devirtualized at least for one call site for stats. | 
|  | 1046 | if (PrintSummaryDevirt) | 
|  | 1047 | DevirtTargets.insert(TheFn); | 
|  | 1048 |  | 
|  | 1049 | auto &S = TheFn.getSummaryList()[0]; | 
| Eugene Leviant | 943afb5 | 2019-10-17 07:46:18 +0000 | [diff] [blame] | 1050 | bool IsExported = AddCalls(SlotInfo, TheFn); | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 1051 | if (IsExported) | 
|  | 1052 | ExportedGUIDs.insert(TheFn.getGUID()); | 
|  | 1053 |  | 
|  | 1054 | // Record in summary for use in devirtualization during the ThinLTO import | 
|  | 1055 | // step. | 
|  | 1056 | Res->TheKind = WholeProgramDevirtResolution::SingleImpl; | 
|  | 1057 | if (GlobalValue::isLocalLinkage(S->linkage())) { | 
|  | 1058 | if (IsExported) | 
|  | 1059 | // If target is a local function and we are exporting it by | 
|  | 1060 | // devirtualizing a call in another module, we need to record the | 
|  | 1061 | // promoted name. | 
|  | 1062 | Res->SingleImplName = ModuleSummaryIndex::getGlobalNameForLocal( | 
|  | 1063 | TheFn.name(), ExportSummary.getModuleHash(S->modulePath())); | 
|  | 1064 | else { | 
|  | 1065 | LocalWPDTargetsMap[TheFn].push_back(SlotSummary); | 
|  | 1066 | Res->SingleImplName = TheFn.name(); | 
|  | 1067 | } | 
|  | 1068 | } else | 
|  | 1069 | Res->SingleImplName = TheFn.name(); | 
|  | 1070 |  | 
|  | 1071 | // Name will be empty if this thin link driven off of serialized combined | 
|  | 1072 | // index (e.g. llvm-lto). However, WPD is not supported/invoked for the | 
|  | 1073 | // legacy LTO API anyway. | 
|  | 1074 | assert(!Res->SingleImplName.empty()); | 
|  | 1075 |  | 
|  | 1076 | return true; | 
|  | 1077 | } | 
|  | 1078 |  | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1079 | void DevirtModule::tryICallBranchFunnel( | 
|  | 1080 | MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo, | 
|  | 1081 | WholeProgramDevirtResolution *Res, VTableSlot Slot) { | 
|  | 1082 | Triple T(M.getTargetTriple()); | 
|  | 1083 | if (T.getArch() != Triple::x86_64) | 
|  | 1084 | return; | 
|  | 1085 |  | 
| Vitaly Buka | 66f53d7 | 2018-04-06 21:32:36 +0000 | [diff] [blame] | 1086 | if (TargetsForSlot.size() > ClThreshold) | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1087 | return; | 
|  | 1088 |  | 
|  | 1089 | bool HasNonDevirt = !SlotInfo.CSInfo.AllCallSitesDevirted; | 
|  | 1090 | if (!HasNonDevirt) | 
|  | 1091 | for (auto &P : SlotInfo.ConstCSInfo) | 
|  | 1092 | if (!P.second.AllCallSitesDevirted) { | 
|  | 1093 | HasNonDevirt = true; | 
|  | 1094 | break; | 
|  | 1095 | } | 
|  | 1096 |  | 
|  | 1097 | if (!HasNonDevirt) | 
|  | 1098 | return; | 
|  | 1099 |  | 
|  | 1100 | FunctionType *FT = | 
|  | 1101 | FunctionType::get(Type::getVoidTy(M.getContext()), {Int8PtrTy}, true); | 
|  | 1102 | Function *JT; | 
|  | 1103 | if (isa<MDString>(Slot.TypeID)) { | 
|  | 1104 | JT = Function::Create(FT, Function::ExternalLinkage, | 
| Dylan McKay | f920da0 | 2018-12-18 09:52:52 +0000 | [diff] [blame] | 1105 | M.getDataLayout().getProgramAddressSpace(), | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1106 | getGlobalName(Slot, {}, "branch_funnel"), &M); | 
|  | 1107 | JT->setVisibility(GlobalValue::HiddenVisibility); | 
|  | 1108 | } else { | 
| Dylan McKay | f920da0 | 2018-12-18 09:52:52 +0000 | [diff] [blame] | 1109 | JT = Function::Create(FT, Function::InternalLinkage, | 
|  | 1110 | M.getDataLayout().getProgramAddressSpace(), | 
|  | 1111 | "branch_funnel", &M); | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1112 | } | 
|  | 1113 | JT->addAttribute(1, Attribute::Nest); | 
|  | 1114 |  | 
|  | 1115 | std::vector<Value *> JTArgs; | 
|  | 1116 | JTArgs.push_back(JT->arg_begin()); | 
|  | 1117 | for (auto &T : TargetsForSlot) { | 
|  | 1118 | JTArgs.push_back(getMemberAddr(T.TM)); | 
|  | 1119 | JTArgs.push_back(T.Fn); | 
|  | 1120 | } | 
|  | 1121 |  | 
|  | 1122 | BasicBlock *BB = BasicBlock::Create(M.getContext(), "", JT, nullptr); | 
| James Y Knight | 7976eb5 | 2019-02-01 20:43:25 +0000 | [diff] [blame] | 1123 | Function *Intr = | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1124 | Intrinsic::getDeclaration(&M, llvm::Intrinsic::icall_branch_funnel, {}); | 
|  | 1125 |  | 
|  | 1126 | auto *CI = CallInst::Create(Intr, JTArgs, "", BB); | 
|  | 1127 | CI->setTailCallKind(CallInst::TCK_MustTail); | 
|  | 1128 | ReturnInst::Create(M.getContext(), nullptr, BB); | 
|  | 1129 |  | 
|  | 1130 | bool IsExported = false; | 
|  | 1131 | applyICallBranchFunnel(SlotInfo, JT, IsExported); | 
|  | 1132 | if (IsExported) | 
|  | 1133 | Res->TheKind = WholeProgramDevirtResolution::BranchFunnel; | 
|  | 1134 | } | 
|  | 1135 |  | 
|  | 1136 | void DevirtModule::applyICallBranchFunnel(VTableSlotInfo &SlotInfo, | 
|  | 1137 | Constant *JT, bool &IsExported) { | 
|  | 1138 | auto Apply = [&](CallSiteInfo &CSInfo) { | 
|  | 1139 | if (CSInfo.isExported()) | 
|  | 1140 | IsExported = true; | 
|  | 1141 | if (CSInfo.AllCallSitesDevirted) | 
|  | 1142 | return; | 
|  | 1143 | for (auto &&VCallSite : CSInfo.CallSites) { | 
|  | 1144 | CallSite CS = VCallSite.CS; | 
|  | 1145 |  | 
|  | 1146 | // Jump tables are only profitable if the retpoline mitigation is enabled. | 
|  | 1147 | Attribute FSAttr = CS.getCaller()->getFnAttribute("target-features"); | 
|  | 1148 | if (FSAttr.hasAttribute(Attribute::None) || | 
|  | 1149 | !FSAttr.getValueAsString().contains("+retpoline")) | 
|  | 1150 | continue; | 
|  | 1151 |  | 
|  | 1152 | if (RemarksEnabled) | 
| Teresa Johnson | b0a1d3b | 2018-08-14 03:00:16 +0000 | [diff] [blame] | 1153 | VCallSite.emitRemark("branch-funnel", | 
|  | 1154 | JT->stripPointerCasts()->getName(), OREGetter); | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1155 |  | 
|  | 1156 | // Pass the address of the vtable in the nest register, which is r10 on | 
|  | 1157 | // x86_64. | 
|  | 1158 | std::vector<Type *> NewArgs; | 
|  | 1159 | NewArgs.push_back(Int8PtrTy); | 
|  | 1160 | for (Type *T : CS.getFunctionType()->params()) | 
|  | 1161 | NewArgs.push_back(T); | 
| James Y Knight | 7976eb5 | 2019-02-01 20:43:25 +0000 | [diff] [blame] | 1162 | FunctionType *NewFT = | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1163 | FunctionType::get(CS.getFunctionType()->getReturnType(), NewArgs, | 
| James Y Knight | 7976eb5 | 2019-02-01 20:43:25 +0000 | [diff] [blame] | 1164 | CS.getFunctionType()->isVarArg()); | 
|  | 1165 | PointerType *NewFTPtr = PointerType::getUnqual(NewFT); | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1166 |  | 
|  | 1167 | IRBuilder<> IRB(CS.getInstruction()); | 
|  | 1168 | std::vector<Value *> Args; | 
|  | 1169 | Args.push_back(IRB.CreateBitCast(VCallSite.VTable, Int8PtrTy)); | 
|  | 1170 | for (unsigned I = 0; I != CS.getNumArgOperands(); ++I) | 
|  | 1171 | Args.push_back(CS.getArgOperand(I)); | 
|  | 1172 |  | 
|  | 1173 | CallSite NewCS; | 
|  | 1174 | if (CS.isCall()) | 
| James Y Knight | 7976eb5 | 2019-02-01 20:43:25 +0000 | [diff] [blame] | 1175 | NewCS = IRB.CreateCall(NewFT, IRB.CreateBitCast(JT, NewFTPtr), Args); | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1176 | else | 
|  | 1177 | NewCS = IRB.CreateInvoke( | 
| James Y Knight | d9e85a0 | 2019-02-01 20:43:34 +0000 | [diff] [blame] | 1178 | NewFT, IRB.CreateBitCast(JT, NewFTPtr), | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1179 | cast<InvokeInst>(CS.getInstruction())->getNormalDest(), | 
|  | 1180 | cast<InvokeInst>(CS.getInstruction())->getUnwindDest(), Args); | 
|  | 1181 | NewCS.setCallingConv(CS.getCallingConv()); | 
|  | 1182 |  | 
|  | 1183 | AttributeList Attrs = CS.getAttributes(); | 
|  | 1184 | std::vector<AttributeSet> NewArgAttrs; | 
|  | 1185 | NewArgAttrs.push_back(AttributeSet::get( | 
|  | 1186 | M.getContext(), ArrayRef<Attribute>{Attribute::get( | 
|  | 1187 | M.getContext(), Attribute::Nest)})); | 
|  | 1188 | for (unsigned I = 0; I + 2 <  Attrs.getNumAttrSets(); ++I) | 
|  | 1189 | NewArgAttrs.push_back(Attrs.getParamAttributes(I)); | 
|  | 1190 | NewCS.setAttributes( | 
|  | 1191 | AttributeList::get(M.getContext(), Attrs.getFnAttributes(), | 
|  | 1192 | Attrs.getRetAttributes(), NewArgAttrs)); | 
|  | 1193 |  | 
|  | 1194 | CS->replaceAllUsesWith(NewCS.getInstruction()); | 
|  | 1195 | CS->eraseFromParent(); | 
|  | 1196 |  | 
|  | 1197 | // This use is no longer unsafe. | 
|  | 1198 | if (VCallSite.NumUnsafeUses) | 
|  | 1199 | --*VCallSite.NumUnsafeUses; | 
|  | 1200 | } | 
|  | 1201 | // Don't mark as devirtualized because there may be callers compiled without | 
|  | 1202 | // retpoline mitigation, which would mean that they are lowered to | 
|  | 1203 | // llvm.type.test and therefore require an llvm.type.test resolution for the | 
|  | 1204 | // type identifier. | 
|  | 1205 | }; | 
|  | 1206 | Apply(SlotInfo.CSInfo); | 
|  | 1207 | for (auto &P : SlotInfo.ConstCSInfo) | 
|  | 1208 | Apply(P.second); | 
|  | 1209 | } | 
|  | 1210 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1211 | bool DevirtModule::tryEvaluateFunctionsWithArgs( | 
|  | 1212 | MutableArrayRef<VirtualCallTarget> TargetsForSlot, | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1213 | ArrayRef<uint64_t> Args) { | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1214 | // Evaluate each function and store the result in each target's RetVal | 
|  | 1215 | // field. | 
|  | 1216 | for (VirtualCallTarget &Target : TargetsForSlot) { | 
|  | 1217 | if (Target.Fn->arg_size() != Args.size() + 1) | 
|  | 1218 | return false; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1219 |  | 
|  | 1220 | Evaluator Eval(M.getDataLayout(), nullptr); | 
|  | 1221 | SmallVector<Constant *, 2> EvalArgs; | 
|  | 1222 | EvalArgs.push_back( | 
|  | 1223 | Constant::getNullValue(Target.Fn->getFunctionType()->getParamType(0))); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1224 | for (unsigned I = 0; I != Args.size(); ++I) { | 
|  | 1225 | auto *ArgTy = dyn_cast<IntegerType>( | 
|  | 1226 | Target.Fn->getFunctionType()->getParamType(I + 1)); | 
|  | 1227 | if (!ArgTy) | 
|  | 1228 | return false; | 
|  | 1229 | EvalArgs.push_back(ConstantInt::get(ArgTy, Args[I])); | 
|  | 1230 | } | 
|  | 1231 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1232 | Constant *RetVal; | 
|  | 1233 | if (!Eval.EvaluateFunction(Target.Fn, RetVal, EvalArgs) || | 
|  | 1234 | !isa<ConstantInt>(RetVal)) | 
|  | 1235 | return false; | 
|  | 1236 | Target.RetVal = cast<ConstantInt>(RetVal)->getZExtValue(); | 
|  | 1237 | } | 
|  | 1238 | return true; | 
|  | 1239 | } | 
|  | 1240 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1241 | void DevirtModule::applyUniformRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, | 
|  | 1242 | uint64_t TheRetVal) { | 
|  | 1243 | for (auto Call : CSInfo.CallSites) | 
|  | 1244 | Call.replaceAndErase( | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 1245 | "uniform-ret-val", FnName, RemarksEnabled, OREGetter, | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1246 | ConstantInt::get(cast<IntegerType>(Call.CS.getType()), TheRetVal)); | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1247 | CSInfo.markDevirt(); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1248 | } | 
|  | 1249 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1250 | bool DevirtModule::tryUniformRetValOpt( | 
| Peter Collingbourne | 77a8d56 | 2017-03-04 01:34:53 +0000 | [diff] [blame] | 1251 | MutableArrayRef<VirtualCallTarget> TargetsForSlot, CallSiteInfo &CSInfo, | 
|  | 1252 | WholeProgramDevirtResolution::ByArg *Res) { | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1253 | // Uniform return value optimization. If all functions return the same | 
|  | 1254 | // constant, replace all calls with that constant. | 
|  | 1255 | uint64_t TheRetVal = TargetsForSlot[0].RetVal; | 
|  | 1256 | for (const VirtualCallTarget &Target : TargetsForSlot) | 
|  | 1257 | if (Target.RetVal != TheRetVal) | 
|  | 1258 | return false; | 
|  | 1259 |  | 
| Peter Collingbourne | 77a8d56 | 2017-03-04 01:34:53 +0000 | [diff] [blame] | 1260 | if (CSInfo.isExported()) { | 
|  | 1261 | Res->TheKind = WholeProgramDevirtResolution::ByArg::UniformRetVal; | 
|  | 1262 | Res->Info = TheRetVal; | 
|  | 1263 | } | 
|  | 1264 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1265 | applyUniformRetValOpt(CSInfo, TargetsForSlot[0].Fn->getName(), TheRetVal); | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 1266 | if (RemarksEnabled) | 
|  | 1267 | for (auto &&Target : TargetsForSlot) | 
|  | 1268 | Target.WasDevirt = true; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1269 | return true; | 
|  | 1270 | } | 
|  | 1271 |  | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1272 | std::string DevirtModule::getGlobalName(VTableSlot Slot, | 
|  | 1273 | ArrayRef<uint64_t> Args, | 
|  | 1274 | StringRef Name) { | 
|  | 1275 | std::string FullName = "__typeid_"; | 
|  | 1276 | raw_string_ostream OS(FullName); | 
|  | 1277 | OS << cast<MDString>(Slot.TypeID)->getString() << '_' << Slot.ByteOffset; | 
|  | 1278 | for (uint64_t Arg : Args) | 
|  | 1279 | OS << '_' << Arg; | 
|  | 1280 | OS << '_' << Name; | 
|  | 1281 | return OS.str(); | 
|  | 1282 | } | 
|  | 1283 |  | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1284 | bool DevirtModule::shouldExportConstantsAsAbsoluteSymbols() { | 
|  | 1285 | Triple T(M.getTargetTriple()); | 
| Fangrui Song | 6904cd9 | 2020-01-06 10:16:28 -0800 | [diff] [blame] | 1286 | return T.isX86() && T.getObjectFormat() == Triple::ELF; | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1287 | } | 
|  | 1288 |  | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1289 | void DevirtModule::exportGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args, | 
|  | 1290 | StringRef Name, Constant *C) { | 
|  | 1291 | GlobalAlias *GA = GlobalAlias::create(Int8Ty, 0, GlobalValue::ExternalLinkage, | 
|  | 1292 | getGlobalName(Slot, Args, Name), C, &M); | 
|  | 1293 | GA->setVisibility(GlobalValue::HiddenVisibility); | 
|  | 1294 | } | 
|  | 1295 |  | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1296 | void DevirtModule::exportConstant(VTableSlot Slot, ArrayRef<uint64_t> Args, | 
|  | 1297 | StringRef Name, uint32_t Const, | 
|  | 1298 | uint32_t &Storage) { | 
|  | 1299 | if (shouldExportConstantsAsAbsoluteSymbols()) { | 
|  | 1300 | exportGlobal( | 
|  | 1301 | Slot, Args, Name, | 
|  | 1302 | ConstantExpr::getIntToPtr(ConstantInt::get(Int32Ty, Const), Int8PtrTy)); | 
|  | 1303 | return; | 
|  | 1304 | } | 
|  | 1305 |  | 
|  | 1306 | Storage = Const; | 
|  | 1307 | } | 
|  | 1308 |  | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1309 | Constant *DevirtModule::importGlobal(VTableSlot Slot, ArrayRef<uint64_t> Args, | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1310 | StringRef Name) { | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1311 | Constant *C = M.getOrInsertGlobal(getGlobalName(Slot, Args, Name), Int8Ty); | 
|  | 1312 | auto *GV = dyn_cast<GlobalVariable>(C); | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1313 | if (GV) | 
|  | 1314 | GV->setVisibility(GlobalValue::HiddenVisibility); | 
|  | 1315 | return C; | 
|  | 1316 | } | 
|  | 1317 |  | 
|  | 1318 | Constant *DevirtModule::importConstant(VTableSlot Slot, ArrayRef<uint64_t> Args, | 
|  | 1319 | StringRef Name, IntegerType *IntTy, | 
|  | 1320 | uint32_t Storage) { | 
|  | 1321 | if (!shouldExportConstantsAsAbsoluteSymbols()) | 
|  | 1322 | return ConstantInt::get(IntTy, Storage); | 
|  | 1323 |  | 
|  | 1324 | Constant *C = importGlobal(Slot, Args, Name); | 
|  | 1325 | auto *GV = cast<GlobalVariable>(C->stripPointerCasts()); | 
|  | 1326 | C = ConstantExpr::getPtrToInt(C, IntTy); | 
|  | 1327 |  | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1328 | // We only need to set metadata if the global is newly created, in which | 
|  | 1329 | // case it would not have hidden visibility. | 
| Benjamin Kramer | 0deb9a9 | 2018-05-31 13:29:58 +0000 | [diff] [blame] | 1330 | if (GV->hasMetadata(LLVMContext::MD_absolute_symbol)) | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1331 | return C; | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1332 |  | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1333 | auto SetAbsRange = [&](uint64_t Min, uint64_t Max) { | 
|  | 1334 | auto *MinC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Min)); | 
|  | 1335 | auto *MaxC = ConstantAsMetadata::get(ConstantInt::get(IntPtrTy, Max)); | 
|  | 1336 | GV->setMetadata(LLVMContext::MD_absolute_symbol, | 
|  | 1337 | MDNode::get(M.getContext(), {MinC, MaxC})); | 
|  | 1338 | }; | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1339 | unsigned AbsWidth = IntTy->getBitWidth(); | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1340 | if (AbsWidth == IntPtrTy->getBitWidth()) | 
|  | 1341 | SetAbsRange(~0ull, ~0ull); // Full set. | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1342 | else | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1343 | SetAbsRange(0, 1ull << AbsWidth); | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1344 | return C; | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1345 | } | 
|  | 1346 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1347 | void DevirtModule::applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, | 
|  | 1348 | bool IsOne, | 
|  | 1349 | Constant *UniqueMemberAddr) { | 
|  | 1350 | for (auto &&Call : CSInfo.CallSites) { | 
|  | 1351 | IRBuilder<> B(Call.CS.getInstruction()); | 
| Peter Collingbourne | 001052a | 2017-08-22 21:41:19 +0000 | [diff] [blame] | 1352 | Value *Cmp = | 
|  | 1353 | B.CreateICmp(IsOne ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE, | 
|  | 1354 | B.CreateBitCast(Call.VTable, Int8PtrTy), UniqueMemberAddr); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1355 | Cmp = B.CreateZExt(Cmp, Call.CS->getType()); | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 1356 | Call.replaceAndErase("unique-ret-val", FnName, RemarksEnabled, OREGetter, | 
|  | 1357 | Cmp); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1358 | } | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1359 | CSInfo.markDevirt(); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1360 | } | 
|  | 1361 |  | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1362 | Constant *DevirtModule::getMemberAddr(const TypeMemberInfo *M) { | 
|  | 1363 | Constant *C = ConstantExpr::getBitCast(M->Bits->GV, Int8PtrTy); | 
|  | 1364 | return ConstantExpr::getGetElementPtr(Int8Ty, C, | 
|  | 1365 | ConstantInt::get(Int64Ty, M->Offset)); | 
|  | 1366 | } | 
|  | 1367 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1368 | bool DevirtModule::tryUniqueRetValOpt( | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 1369 | unsigned BitWidth, MutableArrayRef<VirtualCallTarget> TargetsForSlot, | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1370 | CallSiteInfo &CSInfo, WholeProgramDevirtResolution::ByArg *Res, | 
|  | 1371 | VTableSlot Slot, ArrayRef<uint64_t> Args) { | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1372 | // IsOne controls whether we look for a 0 or a 1. | 
|  | 1373 | auto tryUniqueRetValOptFor = [&](bool IsOne) { | 
| Eugene Zelenko | cdc7161 | 2016-08-11 17:20:18 +0000 | [diff] [blame] | 1374 | const TypeMemberInfo *UniqueMember = nullptr; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1375 | for (const VirtualCallTarget &Target : TargetsForSlot) { | 
| Peter Collingbourne | 3866cc5 | 2016-03-08 03:50:36 +0000 | [diff] [blame] | 1376 | if (Target.RetVal == (IsOne ? 1 : 0)) { | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1377 | if (UniqueMember) | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1378 | return false; | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1379 | UniqueMember = Target.TM; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1380 | } | 
|  | 1381 | } | 
|  | 1382 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1383 | // We should have found a unique member or bailed out by now. We already | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1384 | // checked for a uniform return value in tryUniformRetValOpt. | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1385 | assert(UniqueMember); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1386 |  | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1387 | Constant *UniqueMemberAddr = getMemberAddr(UniqueMember); | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1388 | if (CSInfo.isExported()) { | 
|  | 1389 | Res->TheKind = WholeProgramDevirtResolution::ByArg::UniqueRetVal; | 
|  | 1390 | Res->Info = IsOne; | 
|  | 1391 |  | 
|  | 1392 | exportGlobal(Slot, Args, "unique_member", UniqueMemberAddr); | 
|  | 1393 | } | 
|  | 1394 |  | 
|  | 1395 | // Replace each call with the comparison. | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1396 | applyUniqueRetValOpt(CSInfo, TargetsForSlot[0].Fn->getName(), IsOne, | 
|  | 1397 | UniqueMemberAddr); | 
|  | 1398 |  | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 1399 | // Update devirtualization statistics for targets. | 
|  | 1400 | if (RemarksEnabled) | 
|  | 1401 | for (auto &&Target : TargetsForSlot) | 
|  | 1402 | Target.WasDevirt = true; | 
|  | 1403 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1404 | return true; | 
|  | 1405 | }; | 
|  | 1406 |  | 
|  | 1407 | if (BitWidth == 1) { | 
|  | 1408 | if (tryUniqueRetValOptFor(true)) | 
|  | 1409 | return true; | 
|  | 1410 | if (tryUniqueRetValOptFor(false)) | 
|  | 1411 | return true; | 
|  | 1412 | } | 
|  | 1413 | return false; | 
|  | 1414 | } | 
|  | 1415 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1416 | void DevirtModule::applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName, | 
|  | 1417 | Constant *Byte, Constant *Bit) { | 
|  | 1418 | for (auto Call : CSInfo.CallSites) { | 
|  | 1419 | auto *RetType = cast<IntegerType>(Call.CS.getType()); | 
|  | 1420 | IRBuilder<> B(Call.CS.getInstruction()); | 
| Peter Collingbourne | 001052a | 2017-08-22 21:41:19 +0000 | [diff] [blame] | 1421 | Value *Addr = | 
|  | 1422 | B.CreateGEP(Int8Ty, B.CreateBitCast(Call.VTable, Int8PtrTy), Byte); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1423 | if (RetType->getBitWidth() == 1) { | 
| James Y Knight | 14359ef | 2019-02-01 20:44:24 +0000 | [diff] [blame] | 1424 | Value *Bits = B.CreateLoad(Int8Ty, Addr); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1425 | Value *BitsAndBit = B.CreateAnd(Bits, Bit); | 
|  | 1426 | auto IsBitSet = B.CreateICmpNE(BitsAndBit, ConstantInt::get(Int8Ty, 0)); | 
|  | 1427 | Call.replaceAndErase("virtual-const-prop-1-bit", FnName, RemarksEnabled, | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 1428 | OREGetter, IsBitSet); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1429 | } else { | 
|  | 1430 | Value *ValAddr = B.CreateBitCast(Addr, RetType->getPointerTo()); | 
|  | 1431 | Value *Val = B.CreateLoad(RetType, ValAddr); | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 1432 | Call.replaceAndErase("virtual-const-prop", FnName, RemarksEnabled, | 
|  | 1433 | OREGetter, Val); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1434 | } | 
|  | 1435 | } | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1436 | CSInfo.markDevirt(); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1437 | } | 
|  | 1438 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1439 | bool DevirtModule::tryVirtualConstProp( | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1440 | MutableArrayRef<VirtualCallTarget> TargetsForSlot, VTableSlotInfo &SlotInfo, | 
|  | 1441 | WholeProgramDevirtResolution *Res, VTableSlot Slot) { | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1442 | // This only works if the function returns an integer. | 
|  | 1443 | auto RetType = dyn_cast<IntegerType>(TargetsForSlot[0].Fn->getReturnType()); | 
|  | 1444 | if (!RetType) | 
|  | 1445 | return false; | 
|  | 1446 | unsigned BitWidth = RetType->getBitWidth(); | 
|  | 1447 | if (BitWidth > 64) | 
|  | 1448 | return false; | 
|  | 1449 |  | 
| Peter Collingbourne | 17febdb | 2017-02-09 23:46:26 +0000 | [diff] [blame] | 1450 | // Make sure that each function is defined, does not access memory, takes at | 
|  | 1451 | // least one argument, does not use its first argument (which we assume is | 
|  | 1452 | // 'this'), and has the same return type. | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 1453 | // | 
|  | 1454 | // Note that we test whether this copy of the function is readnone, rather | 
|  | 1455 | // than testing function attributes, which must hold for any copy of the | 
|  | 1456 | // function, even a less optimized version substituted at link time. This is | 
|  | 1457 | // sound because the virtual constant propagation optimizations effectively | 
|  | 1458 | // inline all implementations of the virtual function into each call site, | 
|  | 1459 | // rather than using function attributes to perform local optimization. | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1460 | for (VirtualCallTarget &Target : TargetsForSlot) { | 
| Peter Collingbourne | 37317f1 | 2017-02-17 18:17:04 +0000 | [diff] [blame] | 1461 | if (Target.Fn->isDeclaration() || | 
|  | 1462 | computeFunctionBodyMemoryAccess(*Target.Fn, AARGetter(*Target.Fn)) != | 
|  | 1463 | MAK_ReadNone || | 
| Peter Collingbourne | 17febdb | 2017-02-09 23:46:26 +0000 | [diff] [blame] | 1464 | Target.Fn->arg_empty() || !Target.Fn->arg_begin()->use_empty() || | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1465 | Target.Fn->getReturnType() != RetType) | 
|  | 1466 | return false; | 
|  | 1467 | } | 
|  | 1468 |  | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1469 | for (auto &&CSByConstantArg : SlotInfo.ConstCSInfo) { | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1470 | if (!tryEvaluateFunctionsWithArgs(TargetsForSlot, CSByConstantArg.first)) | 
|  | 1471 | continue; | 
|  | 1472 |  | 
| Peter Collingbourne | 77a8d56 | 2017-03-04 01:34:53 +0000 | [diff] [blame] | 1473 | WholeProgramDevirtResolution::ByArg *ResByArg = nullptr; | 
|  | 1474 | if (Res) | 
|  | 1475 | ResByArg = &Res->ResByArg[CSByConstantArg.first]; | 
|  | 1476 |  | 
|  | 1477 | if (tryUniformRetValOpt(TargetsForSlot, CSByConstantArg.second, ResByArg)) | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1478 | continue; | 
|  | 1479 |  | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1480 | if (tryUniqueRetValOpt(BitWidth, TargetsForSlot, CSByConstantArg.second, | 
|  | 1481 | ResByArg, Slot, CSByConstantArg.first)) | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1482 | continue; | 
|  | 1483 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1484 | // Find an allocation offset in bits in all vtables associated with the | 
|  | 1485 | // type. | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1486 | uint64_t AllocBefore = | 
|  | 1487 | findLowestOffset(TargetsForSlot, /*IsAfter=*/false, BitWidth); | 
|  | 1488 | uint64_t AllocAfter = | 
|  | 1489 | findLowestOffset(TargetsForSlot, /*IsAfter=*/true, BitWidth); | 
|  | 1490 |  | 
|  | 1491 | // Calculate the total amount of padding needed to store a value at both | 
|  | 1492 | // ends of the object. | 
|  | 1493 | uint64_t TotalPaddingBefore = 0, TotalPaddingAfter = 0; | 
|  | 1494 | for (auto &&Target : TargetsForSlot) { | 
|  | 1495 | TotalPaddingBefore += std::max<int64_t>( | 
|  | 1496 | (AllocBefore + 7) / 8 - Target.allocatedBeforeBytes() - 1, 0); | 
|  | 1497 | TotalPaddingAfter += std::max<int64_t>( | 
|  | 1498 | (AllocAfter + 7) / 8 - Target.allocatedAfterBytes() - 1, 0); | 
|  | 1499 | } | 
|  | 1500 |  | 
|  | 1501 | // If the amount of padding is too large, give up. | 
|  | 1502 | // FIXME: do something smarter here. | 
|  | 1503 | if (std::min(TotalPaddingBefore, TotalPaddingAfter) > 128) | 
|  | 1504 | continue; | 
|  | 1505 |  | 
|  | 1506 | // Calculate the offset to the value as a (possibly negative) byte offset | 
|  | 1507 | // and (if applicable) a bit offset, and store the values in the targets. | 
|  | 1508 | int64_t OffsetByte; | 
|  | 1509 | uint64_t OffsetBit; | 
|  | 1510 | if (TotalPaddingBefore <= TotalPaddingAfter) | 
|  | 1511 | setBeforeReturnValues(TargetsForSlot, AllocBefore, BitWidth, OffsetByte, | 
|  | 1512 | OffsetBit); | 
|  | 1513 | else | 
|  | 1514 | setAfterReturnValues(TargetsForSlot, AllocAfter, BitWidth, OffsetByte, | 
|  | 1515 | OffsetBit); | 
|  | 1516 |  | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 1517 | if (RemarksEnabled) | 
|  | 1518 | for (auto &&Target : TargetsForSlot) | 
|  | 1519 | Target.WasDevirt = true; | 
|  | 1520 |  | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1521 |  | 
|  | 1522 | if (CSByConstantArg.second.isExported()) { | 
|  | 1523 | ResByArg->TheKind = WholeProgramDevirtResolution::ByArg::VirtualConstProp; | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1524 | exportConstant(Slot, CSByConstantArg.first, "byte", OffsetByte, | 
|  | 1525 | ResByArg->Byte); | 
|  | 1526 | exportConstant(Slot, CSByConstantArg.first, "bit", 1ULL << OffsetBit, | 
|  | 1527 | ResByArg->Bit); | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1528 | } | 
|  | 1529 |  | 
|  | 1530 | // Rewrite each call to a load from OffsetByte/OffsetBit. | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1531 | Constant *ByteConst = ConstantInt::get(Int32Ty, OffsetByte); | 
|  | 1532 | Constant *BitConst = ConstantInt::get(Int8Ty, 1ULL << OffsetBit); | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1533 | applyVirtualConstProp(CSByConstantArg.second, | 
|  | 1534 | TargetsForSlot[0].Fn->getName(), ByteConst, BitConst); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1535 | } | 
|  | 1536 | return true; | 
|  | 1537 | } | 
|  | 1538 |  | 
|  | 1539 | void DevirtModule::rebuildGlobal(VTableBits &B) { | 
|  | 1540 | if (B.Before.Bytes.empty() && B.After.Bytes.empty()) | 
|  | 1541 | return; | 
|  | 1542 |  | 
| Peter Collingbourne | ef5cfc2 | 2019-07-22 18:50:45 +0000 | [diff] [blame] | 1543 | // Align the before byte array to the global's minimum alignment so that we | 
|  | 1544 | // don't break any alignment requirements on the global. | 
| Guillaume Chatelet | 0e62011 | 2019-10-15 11:24:36 +0000 | [diff] [blame] | 1545 | MaybeAlign Alignment(B.GV->getAlignment()); | 
|  | 1546 | if (!Alignment) | 
|  | 1547 | Alignment = | 
|  | 1548 | Align(M.getDataLayout().getABITypeAlignment(B.GV->getValueType())); | 
|  | 1549 | B.Before.Bytes.resize(alignTo(B.Before.Bytes.size(), Alignment)); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1550 |  | 
|  | 1551 | // Before was stored in reverse order; flip it now. | 
|  | 1552 | for (size_t I = 0, Size = B.Before.Bytes.size(); I != Size / 2; ++I) | 
|  | 1553 | std::swap(B.Before.Bytes[I], B.Before.Bytes[Size - 1 - I]); | 
|  | 1554 |  | 
|  | 1555 | // Build an anonymous global containing the before bytes, followed by the | 
|  | 1556 | // original initializer, followed by the after bytes. | 
|  | 1557 | auto NewInit = ConstantStruct::getAnon( | 
|  | 1558 | {ConstantDataArray::get(M.getContext(), B.Before.Bytes), | 
|  | 1559 | B.GV->getInitializer(), | 
|  | 1560 | ConstantDataArray::get(M.getContext(), B.After.Bytes)}); | 
|  | 1561 | auto NewGV = | 
|  | 1562 | new GlobalVariable(M, NewInit->getType(), B.GV->isConstant(), | 
|  | 1563 | GlobalVariable::PrivateLinkage, NewInit, "", B.GV); | 
|  | 1564 | NewGV->setSection(B.GV->getSection()); | 
|  | 1565 | NewGV->setComdat(B.GV->getComdat()); | 
| Guillaume Chatelet | 0e62011 | 2019-10-15 11:24:36 +0000 | [diff] [blame] | 1566 | NewGV->setAlignment(MaybeAlign(B.GV->getAlignment())); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1567 |  | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1568 | // Copy the original vtable's metadata to the anonymous global, adjusting | 
|  | 1569 | // offsets as required. | 
|  | 1570 | NewGV->copyMetadata(B.GV, B.Before.Bytes.size()); | 
|  | 1571 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1572 | // Build an alias named after the original global, pointing at the second | 
|  | 1573 | // element (the original initializer). | 
|  | 1574 | auto Alias = GlobalAlias::create( | 
|  | 1575 | B.GV->getInitializer()->getType(), 0, B.GV->getLinkage(), "", | 
|  | 1576 | ConstantExpr::getGetElementPtr( | 
|  | 1577 | NewInit->getType(), NewGV, | 
|  | 1578 | ArrayRef<Constant *>{ConstantInt::get(Int32Ty, 0), | 
|  | 1579 | ConstantInt::get(Int32Ty, 1)}), | 
|  | 1580 | &M); | 
|  | 1581 | Alias->setVisibility(B.GV->getVisibility()); | 
|  | 1582 | Alias->takeName(B.GV); | 
|  | 1583 |  | 
|  | 1584 | B.GV->replaceAllUsesWith(Alias); | 
|  | 1585 | B.GV->eraseFromParent(); | 
|  | 1586 | } | 
|  | 1587 |  | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 1588 | bool DevirtModule::areRemarksEnabled() { | 
|  | 1589 | const auto &FL = M.getFunctionList(); | 
| Teresa Johnson | 5e1c0e7 | 2018-09-18 13:42:24 +0000 | [diff] [blame] | 1590 | for (const Function &Fn : FL) { | 
|  | 1591 | const auto &BBL = Fn.getBasicBlockList(); | 
|  | 1592 | if (BBL.empty()) | 
|  | 1593 | continue; | 
|  | 1594 | auto DI = OptimizationRemark(DEBUG_TYPE, "", DebugLoc(), &BBL.front()); | 
|  | 1595 | return DI.isEnabled(); | 
|  | 1596 | } | 
|  | 1597 | return false; | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 1598 | } | 
|  | 1599 |  | 
| Teresa Johnson | c8e3686 | 2019-12-06 12:13:34 -0800 | [diff] [blame] | 1600 | void DevirtModule::scanTypeTestUsers(Function *TypeTestFunc) { | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1601 | // Find all virtual calls via a virtual table pointer %p under an assumption | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1602 | // of the form llvm.assume(llvm.type.test(%p, %md)). This indicates that %p | 
|  | 1603 | // points to a member of the type identifier %md. Group calls by (type ID, | 
|  | 1604 | // offset) pair (effectively the identity of the virtual function) and store | 
|  | 1605 | // to CallSlots. | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 1606 | DenseSet<CallSite> SeenCallSites; | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1607 | for (auto I = TypeTestFunc->use_begin(), E = TypeTestFunc->use_end(); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1608 | I != E;) { | 
|  | 1609 | auto CI = dyn_cast<CallInst>(I->getUser()); | 
|  | 1610 | ++I; | 
|  | 1611 | if (!CI) | 
|  | 1612 | continue; | 
|  | 1613 |  | 
| Peter Collingbourne | ccdc225 | 2016-05-10 18:07:21 +0000 | [diff] [blame] | 1614 | // Search for virtual calls based on %p and add them to DevirtCalls. | 
|  | 1615 | SmallVector<DevirtCallSite, 1> DevirtCalls; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1616 | SmallVector<CallInst *, 1> Assumes; | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 1617 | auto &DT = LookupDomTree(*CI->getFunction()); | 
|  | 1618 | findDevirtualizableCallsForTypeTest(DevirtCalls, Assumes, CI, DT); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1619 |  | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 1620 | // If we found any, add them to CallSlots. | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1621 | if (!Assumes.empty()) { | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1622 | Metadata *TypeId = | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1623 | cast<MetadataAsValue>(CI->getArgOperand(1))->getMetadata(); | 
|  | 1624 | Value *Ptr = CI->getArgOperand(0)->stripPointerCasts(); | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 1625 | for (DevirtCallSite Call : DevirtCalls) { | 
|  | 1626 | // Only add this CallSite if we haven't seen it before. The vtable | 
|  | 1627 | // pointer may have been CSE'd with pointers from other call sites, | 
|  | 1628 | // and we don't want to process call sites multiple times. We can't | 
|  | 1629 | // just skip the vtable Ptr if it has been seen before, however, since | 
|  | 1630 | // it may be shared by type tests that dominate different calls. | 
|  | 1631 | if (SeenCallSites.insert(Call.CS).second) | 
| Peter Collingbourne | 001052a | 2017-08-22 21:41:19 +0000 | [diff] [blame] | 1632 | CallSlots[{TypeId, Call.Offset}].addCallSite(Ptr, Call.CS, nullptr); | 
| Peter Collingbourne | ccdc225 | 2016-05-10 18:07:21 +0000 | [diff] [blame] | 1633 | } | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1634 | } | 
|  | 1635 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1636 | // We no longer need the assumes or the type test. | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1637 | for (auto Assume : Assumes) | 
|  | 1638 | Assume->eraseFromParent(); | 
|  | 1639 | // We can't use RecursivelyDeleteTriviallyDeadInstructions here because we | 
|  | 1640 | // may use the vtable argument later. | 
|  | 1641 | if (CI->use_empty()) | 
|  | 1642 | CI->eraseFromParent(); | 
|  | 1643 | } | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1644 | } | 
|  | 1645 |  | 
|  | 1646 | void DevirtModule::scanTypeCheckedLoadUsers(Function *TypeCheckedLoadFunc) { | 
|  | 1647 | Function *TypeTestFunc = Intrinsic::getDeclaration(&M, Intrinsic::type_test); | 
|  | 1648 |  | 
|  | 1649 | for (auto I = TypeCheckedLoadFunc->use_begin(), | 
|  | 1650 | E = TypeCheckedLoadFunc->use_end(); | 
|  | 1651 | I != E;) { | 
|  | 1652 | auto CI = dyn_cast<CallInst>(I->getUser()); | 
|  | 1653 | ++I; | 
|  | 1654 | if (!CI) | 
|  | 1655 | continue; | 
|  | 1656 |  | 
|  | 1657 | Value *Ptr = CI->getArgOperand(0); | 
|  | 1658 | Value *Offset = CI->getArgOperand(1); | 
|  | 1659 | Value *TypeIdValue = CI->getArgOperand(2); | 
|  | 1660 | Metadata *TypeId = cast<MetadataAsValue>(TypeIdValue)->getMetadata(); | 
|  | 1661 |  | 
|  | 1662 | SmallVector<DevirtCallSite, 1> DevirtCalls; | 
|  | 1663 | SmallVector<Instruction *, 1> LoadedPtrs; | 
|  | 1664 | SmallVector<Instruction *, 1> Preds; | 
|  | 1665 | bool HasNonCallUses = false; | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 1666 | auto &DT = LookupDomTree(*CI->getFunction()); | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1667 | findDevirtualizableCallsForTypeCheckedLoad(DevirtCalls, LoadedPtrs, Preds, | 
| Teresa Johnson | f24136f | 2018-09-27 14:55:32 +0000 | [diff] [blame] | 1668 | HasNonCallUses, CI, DT); | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1669 |  | 
|  | 1670 | // Start by generating "pessimistic" code that explicitly loads the function | 
|  | 1671 | // pointer from the vtable and performs the type check. If possible, we will | 
|  | 1672 | // eliminate the load and the type check later. | 
|  | 1673 |  | 
|  | 1674 | // If possible, only generate the load at the point where it is used. | 
|  | 1675 | // This helps avoid unnecessary spills. | 
|  | 1676 | IRBuilder<> LoadB( | 
|  | 1677 | (LoadedPtrs.size() == 1 && !HasNonCallUses) ? LoadedPtrs[0] : CI); | 
|  | 1678 | Value *GEP = LoadB.CreateGEP(Int8Ty, Ptr, Offset); | 
|  | 1679 | Value *GEPPtr = LoadB.CreateBitCast(GEP, PointerType::getUnqual(Int8PtrTy)); | 
|  | 1680 | Value *LoadedValue = LoadB.CreateLoad(Int8PtrTy, GEPPtr); | 
|  | 1681 |  | 
|  | 1682 | for (Instruction *LoadedPtr : LoadedPtrs) { | 
|  | 1683 | LoadedPtr->replaceAllUsesWith(LoadedValue); | 
|  | 1684 | LoadedPtr->eraseFromParent(); | 
|  | 1685 | } | 
|  | 1686 |  | 
|  | 1687 | // Likewise for the type test. | 
|  | 1688 | IRBuilder<> CallB((Preds.size() == 1 && !HasNonCallUses) ? Preds[0] : CI); | 
|  | 1689 | CallInst *TypeTestCall = CallB.CreateCall(TypeTestFunc, {Ptr, TypeIdValue}); | 
|  | 1690 |  | 
|  | 1691 | for (Instruction *Pred : Preds) { | 
|  | 1692 | Pred->replaceAllUsesWith(TypeTestCall); | 
|  | 1693 | Pred->eraseFromParent(); | 
|  | 1694 | } | 
|  | 1695 |  | 
|  | 1696 | // We have already erased any extractvalue instructions that refer to the | 
|  | 1697 | // intrinsic call, but the intrinsic may have other non-extractvalue uses | 
|  | 1698 | // (although this is unlikely). In that case, explicitly build a pair and | 
|  | 1699 | // RAUW it. | 
|  | 1700 | if (!CI->use_empty()) { | 
|  | 1701 | Value *Pair = UndefValue::get(CI->getType()); | 
|  | 1702 | IRBuilder<> B(CI); | 
|  | 1703 | Pair = B.CreateInsertValue(Pair, LoadedValue, {0}); | 
|  | 1704 | Pair = B.CreateInsertValue(Pair, TypeTestCall, {1}); | 
|  | 1705 | CI->replaceAllUsesWith(Pair); | 
|  | 1706 | } | 
|  | 1707 |  | 
|  | 1708 | // The number of unsafe uses is initially the number of uses. | 
|  | 1709 | auto &NumUnsafeUses = NumUnsafeUsesForTypeTest[TypeTestCall]; | 
|  | 1710 | NumUnsafeUses = DevirtCalls.size(); | 
|  | 1711 |  | 
|  | 1712 | // If the function pointer has a non-call user, we cannot eliminate the type | 
|  | 1713 | // check, as one of those users may eventually call the pointer. Increment | 
|  | 1714 | // the unsafe use count to make sure it cannot reach zero. | 
|  | 1715 | if (HasNonCallUses) | 
|  | 1716 | ++NumUnsafeUses; | 
|  | 1717 | for (DevirtCallSite Call : DevirtCalls) { | 
| Peter Collingbourne | 50cbd7c | 2017-02-15 21:56:51 +0000 | [diff] [blame] | 1718 | CallSlots[{TypeId, Call.Offset}].addCallSite(Ptr, Call.CS, | 
|  | 1719 | &NumUnsafeUses); | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1720 | } | 
|  | 1721 |  | 
|  | 1722 | CI->eraseFromParent(); | 
|  | 1723 | } | 
|  | 1724 | } | 
|  | 1725 |  | 
| Peter Collingbourne | 6d284fa | 2017-03-09 00:21:25 +0000 | [diff] [blame] | 1726 | void DevirtModule::importResolution(VTableSlot Slot, VTableSlotInfo &SlotInfo) { | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 1727 | auto *TypeId = dyn_cast<MDString>(Slot.TypeID); | 
|  | 1728 | if (!TypeId) | 
|  | 1729 | return; | 
| Peter Collingbourne | 9a3f979 | 2017-03-22 18:04:39 +0000 | [diff] [blame] | 1730 | const TypeIdSummary *TidSummary = | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 1731 | ImportSummary->getTypeIdSummary(TypeId->getString()); | 
| Peter Collingbourne | 9a3f979 | 2017-03-22 18:04:39 +0000 | [diff] [blame] | 1732 | if (!TidSummary) | 
|  | 1733 | return; | 
|  | 1734 | auto ResI = TidSummary->WPDRes.find(Slot.ByteOffset); | 
|  | 1735 | if (ResI == TidSummary->WPDRes.end()) | 
|  | 1736 | return; | 
|  | 1737 | const WholeProgramDevirtResolution &Res = ResI->second; | 
| Peter Collingbourne | 6d284fa | 2017-03-09 00:21:25 +0000 | [diff] [blame] | 1738 |  | 
|  | 1739 | if (Res.TheKind == WholeProgramDevirtResolution::SingleImpl) { | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 1740 | assert(!Res.SingleImplName.empty()); | 
| Peter Collingbourne | 6d284fa | 2017-03-09 00:21:25 +0000 | [diff] [blame] | 1741 | // The type of the function in the declaration is irrelevant because every | 
|  | 1742 | // call site will cast it to the correct type. | 
| James Y Knight | 1368022 | 2019-02-01 02:28:03 +0000 | [diff] [blame] | 1743 | Constant *SingleImpl = | 
|  | 1744 | cast<Constant>(M.getOrInsertFunction(Res.SingleImplName, | 
|  | 1745 | Type::getVoidTy(M.getContext())) | 
|  | 1746 | .getCallee()); | 
| Peter Collingbourne | 6d284fa | 2017-03-09 00:21:25 +0000 | [diff] [blame] | 1747 |  | 
|  | 1748 | // This is the import phase so we should not be exporting anything. | 
|  | 1749 | bool IsExported = false; | 
|  | 1750 | applySingleImplDevirt(SlotInfo, SingleImpl, IsExported); | 
|  | 1751 | assert(!IsExported); | 
|  | 1752 | } | 
| Peter Collingbourne | 0152c81 | 2017-03-09 01:11:15 +0000 | [diff] [blame] | 1753 |  | 
|  | 1754 | for (auto &CSByConstantArg : SlotInfo.ConstCSInfo) { | 
|  | 1755 | auto I = Res.ResByArg.find(CSByConstantArg.first); | 
|  | 1756 | if (I == Res.ResByArg.end()) | 
|  | 1757 | continue; | 
|  | 1758 | auto &ResByArg = I->second; | 
|  | 1759 | // FIXME: We should figure out what to do about the "function name" argument | 
|  | 1760 | // to the apply* functions, as the function names are unavailable during the | 
|  | 1761 | // importing phase. For now we just pass the empty string. This does not | 
|  | 1762 | // impact correctness because the function names are just used for remarks. | 
|  | 1763 | switch (ResByArg.TheKind) { | 
|  | 1764 | case WholeProgramDevirtResolution::ByArg::UniformRetVal: | 
|  | 1765 | applyUniformRetValOpt(CSByConstantArg.second, "", ResByArg.Info); | 
|  | 1766 | break; | 
| Peter Collingbourne | 59675ba | 2017-03-10 20:09:11 +0000 | [diff] [blame] | 1767 | case WholeProgramDevirtResolution::ByArg::UniqueRetVal: { | 
|  | 1768 | Constant *UniqueMemberAddr = | 
|  | 1769 | importGlobal(Slot, CSByConstantArg.first, "unique_member"); | 
|  | 1770 | applyUniqueRetValOpt(CSByConstantArg.second, "", ResByArg.Info, | 
|  | 1771 | UniqueMemberAddr); | 
|  | 1772 | break; | 
|  | 1773 | } | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1774 | case WholeProgramDevirtResolution::ByArg::VirtualConstProp: { | 
| Peter Collingbourne | b15a35e | 2017-09-11 22:34:42 +0000 | [diff] [blame] | 1775 | Constant *Byte = importConstant(Slot, CSByConstantArg.first, "byte", | 
|  | 1776 | Int32Ty, ResByArg.Byte); | 
|  | 1777 | Constant *Bit = importConstant(Slot, CSByConstantArg.first, "bit", Int8Ty, | 
|  | 1778 | ResByArg.Bit); | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1779 | applyVirtualConstProp(CSByConstantArg.second, "", Byte, Bit); | 
| Adrian Prantl | 0e6694d | 2017-12-19 22:05:25 +0000 | [diff] [blame] | 1780 | break; | 
| Peter Collingbourne | 14dcf02 | 2017-03-10 20:13:58 +0000 | [diff] [blame] | 1781 | } | 
| Peter Collingbourne | 0152c81 | 2017-03-09 01:11:15 +0000 | [diff] [blame] | 1782 | default: | 
|  | 1783 | break; | 
|  | 1784 | } | 
|  | 1785 | } | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1786 |  | 
|  | 1787 | if (Res.TheKind == WholeProgramDevirtResolution::BranchFunnel) { | 
| James Y Knight | 1368022 | 2019-02-01 02:28:03 +0000 | [diff] [blame] | 1788 | // The type of the function is irrelevant, because it's bitcast at calls | 
|  | 1789 | // anyhow. | 
|  | 1790 | Constant *JT = cast<Constant>( | 
|  | 1791 | M.getOrInsertFunction(getGlobalName(Slot, {}, "branch_funnel"), | 
|  | 1792 | Type::getVoidTy(M.getContext())) | 
|  | 1793 | .getCallee()); | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1794 | bool IsExported = false; | 
|  | 1795 | applyICallBranchFunnel(SlotInfo, JT, IsExported); | 
|  | 1796 | assert(!IsExported); | 
|  | 1797 | } | 
| Peter Collingbourne | 6d284fa | 2017-03-09 00:21:25 +0000 | [diff] [blame] | 1798 | } | 
|  | 1799 |  | 
|  | 1800 | void DevirtModule::removeRedundantTypeTests() { | 
|  | 1801 | auto True = ConstantInt::getTrue(M.getContext()); | 
|  | 1802 | for (auto &&U : NumUnsafeUsesForTypeTest) { | 
|  | 1803 | if (U.second == 0) { | 
|  | 1804 | U.first->replaceAllUsesWith(True); | 
|  | 1805 | U.first->eraseFromParent(); | 
|  | 1806 | } | 
|  | 1807 | } | 
|  | 1808 | } | 
|  | 1809 |  | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1810 | bool DevirtModule::run() { | 
| Teresa Johnson | d0b1f30 | 2019-02-14 21:22:50 +0000 | [diff] [blame] | 1811 | // If only some of the modules were split, we cannot correctly perform | 
|  | 1812 | // this transformation. We already checked for the presense of type tests | 
|  | 1813 | // with partially split modules during the thin link, and would have emitted | 
|  | 1814 | // an error if any were found, so here we can simply return. | 
|  | 1815 | if ((ExportSummary && ExportSummary->partiallySplitLTOUnits()) || | 
|  | 1816 | (ImportSummary && ImportSummary->partiallySplitLTOUnits())) | 
|  | 1817 | return false; | 
|  | 1818 |  | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1819 | Function *TypeTestFunc = | 
|  | 1820 | M.getFunction(Intrinsic::getName(Intrinsic::type_test)); | 
|  | 1821 | Function *TypeCheckedLoadFunc = | 
|  | 1822 | M.getFunction(Intrinsic::getName(Intrinsic::type_checked_load)); | 
|  | 1823 | Function *AssumeFunc = M.getFunction(Intrinsic::getName(Intrinsic::assume)); | 
|  | 1824 |  | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1825 | // Normally if there are no users of the devirtualization intrinsics in the | 
|  | 1826 | // module, this pass has nothing to do. But if we are exporting, we also need | 
|  | 1827 | // to handle any users that appear only in the function summaries. | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 1828 | if (!ExportSummary && | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1829 | (!TypeTestFunc || TypeTestFunc->use_empty() || !AssumeFunc || | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1830 | AssumeFunc->use_empty()) && | 
|  | 1831 | (!TypeCheckedLoadFunc || TypeCheckedLoadFunc->use_empty())) | 
|  | 1832 | return false; | 
|  | 1833 |  | 
|  | 1834 | if (TypeTestFunc && AssumeFunc) | 
| Teresa Johnson | c8e3686 | 2019-12-06 12:13:34 -0800 | [diff] [blame] | 1835 | scanTypeTestUsers(TypeTestFunc); | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1836 |  | 
|  | 1837 | if (TypeCheckedLoadFunc) | 
|  | 1838 | scanTypeCheckedLoadUsers(TypeCheckedLoadFunc); | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1839 |  | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 1840 | if (ImportSummary) { | 
| Peter Collingbourne | 6d284fa | 2017-03-09 00:21:25 +0000 | [diff] [blame] | 1841 | for (auto &S : CallSlots) | 
|  | 1842 | importResolution(S.first, S.second); | 
|  | 1843 |  | 
|  | 1844 | removeRedundantTypeTests(); | 
|  | 1845 |  | 
|  | 1846 | // The rest of the code is only necessary when exporting or during regular | 
|  | 1847 | // LTO, so we are done. | 
|  | 1848 | return true; | 
|  | 1849 | } | 
|  | 1850 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1851 | // Rebuild type metadata into a map for easy lookup. | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1852 | std::vector<VTableBits> Bits; | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1853 | DenseMap<Metadata *, std::set<TypeMemberInfo>> TypeIdMap; | 
|  | 1854 | buildTypeIdentifierMap(Bits, TypeIdMap); | 
|  | 1855 | if (TypeIdMap.empty()) | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1856 | return true; | 
|  | 1857 |  | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1858 | // Collect information from summary about which calls to try to devirtualize. | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 1859 | if (ExportSummary) { | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1860 | DenseMap<GlobalValue::GUID, TinyPtrVector<Metadata *>> MetadataByGUID; | 
|  | 1861 | for (auto &P : TypeIdMap) { | 
|  | 1862 | if (auto *TypeId = dyn_cast<MDString>(P.first)) | 
|  | 1863 | MetadataByGUID[GlobalValue::getGUID(TypeId->getString())].push_back( | 
|  | 1864 | TypeId); | 
|  | 1865 | } | 
|  | 1866 |  | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 1867 | for (auto &P : *ExportSummary) { | 
| Peter Collingbourne | 9667b91 | 2017-05-04 18:03:25 +0000 | [diff] [blame] | 1868 | for (auto &S : P.second.SummaryList) { | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1869 | auto *FS = dyn_cast<FunctionSummary>(S.get()); | 
|  | 1870 | if (!FS) | 
|  | 1871 | continue; | 
|  | 1872 | // FIXME: Only add live functions. | 
| George Rimar | 5d8aea1 | 2017-03-10 10:31:56 +0000 | [diff] [blame] | 1873 | for (FunctionSummary::VFuncId VF : FS->type_test_assume_vcalls()) { | 
|  | 1874 | for (Metadata *MD : MetadataByGUID[VF.GUID]) { | 
| Eugene Leviant | 943afb5 | 2019-10-17 07:46:18 +0000 | [diff] [blame] | 1875 | CallSlots[{MD, VF.Offset}].CSInfo.addSummaryTypeTestAssumeUser(FS); | 
| George Rimar | 5d8aea1 | 2017-03-10 10:31:56 +0000 | [diff] [blame] | 1876 | } | 
|  | 1877 | } | 
|  | 1878 | for (FunctionSummary::VFuncId VF : FS->type_checked_load_vcalls()) { | 
|  | 1879 | for (Metadata *MD : MetadataByGUID[VF.GUID]) { | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1880 | CallSlots[{MD, VF.Offset}].CSInfo.addSummaryTypeCheckedLoadUser(FS); | 
| George Rimar | 5d8aea1 | 2017-03-10 10:31:56 +0000 | [diff] [blame] | 1881 | } | 
|  | 1882 | } | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1883 | for (const FunctionSummary::ConstVCall &VC : | 
| George Rimar | 5d8aea1 | 2017-03-10 10:31:56 +0000 | [diff] [blame] | 1884 | FS->type_test_assume_const_vcalls()) { | 
|  | 1885 | for (Metadata *MD : MetadataByGUID[VC.VFunc.GUID]) { | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 1886 | CallSlots[{MD, VC.VFunc.Offset}] | 
| George Rimar | 5d8aea1 | 2017-03-10 10:31:56 +0000 | [diff] [blame] | 1887 | .ConstCSInfo[VC.Args] | 
| Eugene Leviant | 943afb5 | 2019-10-17 07:46:18 +0000 | [diff] [blame] | 1888 | .addSummaryTypeTestAssumeUser(FS); | 
| George Rimar | 5d8aea1 | 2017-03-10 10:31:56 +0000 | [diff] [blame] | 1889 | } | 
|  | 1890 | } | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 1891 | for (const FunctionSummary::ConstVCall &VC : | 
| George Rimar | 5d8aea1 | 2017-03-10 10:31:56 +0000 | [diff] [blame] | 1892 | FS->type_checked_load_const_vcalls()) { | 
|  | 1893 | for (Metadata *MD : MetadataByGUID[VC.VFunc.GUID]) { | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1894 | CallSlots[{MD, VC.VFunc.Offset}] | 
|  | 1895 | .ConstCSInfo[VC.Args] | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1896 | .addSummaryTypeCheckedLoadUser(FS); | 
| George Rimar | 5d8aea1 | 2017-03-10 10:31:56 +0000 | [diff] [blame] | 1897 | } | 
|  | 1898 | } | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1899 | } | 
|  | 1900 | } | 
|  | 1901 | } | 
|  | 1902 |  | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1903 | // For each (type, offset) pair: | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1904 | bool DidVirtualConstProp = false; | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 1905 | std::map<std::string, Function*> DevirtTargets; | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1906 | for (auto &S : CallSlots) { | 
| Peter Collingbourne | 7efd750 | 2016-06-24 21:21:32 +0000 | [diff] [blame] | 1907 | // Search each of the members of the type identifier for the virtual | 
|  | 1908 | // function implementation at offset S.first.ByteOffset, and add to | 
|  | 1909 | // TargetsForSlot. | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1910 | std::vector<VirtualCallTarget> TargetsForSlot; | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1911 | if (tryFindVirtualCallTargets(TargetsForSlot, TypeIdMap[S.first.TypeID], | 
|  | 1912 | S.first.ByteOffset)) { | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 1913 | WholeProgramDevirtResolution *Res = nullptr; | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 1914 | if (ExportSummary && isa<MDString>(S.first.TypeID)) | 
|  | 1915 | Res = &ExportSummary | 
| Peter Collingbourne | 9a3f979 | 2017-03-22 18:04:39 +0000 | [diff] [blame] | 1916 | ->getOrInsertTypeIdSummary( | 
|  | 1917 | cast<MDString>(S.first.TypeID)->getString()) | 
|  | 1918 | .WPDRes[S.first.ByteOffset]; | 
| Peter Collingbourne | 2325bb3 | 2017-03-04 01:31:01 +0000 | [diff] [blame] | 1919 |  | 
| Eugene Leviant | 943afb5 | 2019-10-17 07:46:18 +0000 | [diff] [blame] | 1920 | if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res)) { | 
| Peter Collingbourne | 2974856 | 2018-03-09 19:11:44 +0000 | [diff] [blame] | 1921 | DidVirtualConstProp |= | 
|  | 1922 | tryVirtualConstProp(TargetsForSlot, S.second, Res, S.first); | 
|  | 1923 |  | 
|  | 1924 | tryICallBranchFunnel(TargetsForSlot, S.second, Res, S.first); | 
|  | 1925 | } | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1926 |  | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1927 | // Collect functions devirtualized at least for one call site for stats. | 
|  | 1928 | if (RemarksEnabled) | 
|  | 1929 | for (const auto &T : TargetsForSlot) | 
|  | 1930 | if (T.WasDevirt) | 
|  | 1931 | DevirtTargets[T.Fn->getName()] = T.Fn; | 
|  | 1932 | } | 
|  | 1933 |  | 
|  | 1934 | // CFI-specific: if we are exporting and any llvm.type.checked.load | 
|  | 1935 | // intrinsics were *not* devirtualized, we need to add the resulting | 
|  | 1936 | // llvm.type.test intrinsics to the function summaries so that the | 
|  | 1937 | // LowerTypeTests pass will export them. | 
| Peter Collingbourne | f7691d8 | 2017-03-22 18:22:59 +0000 | [diff] [blame] | 1938 | if (ExportSummary && isa<MDString>(S.first.TypeID)) { | 
| Peter Collingbourne | b406baa | 2017-03-04 01:23:30 +0000 | [diff] [blame] | 1939 | auto GUID = | 
|  | 1940 | GlobalValue::getGUID(cast<MDString>(S.first.TypeID)->getString()); | 
|  | 1941 | for (auto FS : S.second.CSInfo.SummaryTypeCheckedLoadUsers) | 
|  | 1942 | FS->addTypeTest(GUID); | 
|  | 1943 | for (auto &CCS : S.second.ConstCSInfo) | 
|  | 1944 | for (auto FS : CCS.second.SummaryTypeCheckedLoadUsers) | 
|  | 1945 | FS->addTypeTest(GUID); | 
|  | 1946 | } | 
| Ivan Krasin | f3403fd | 2016-08-11 19:09:02 +0000 | [diff] [blame] | 1947 | } | 
|  | 1948 |  | 
|  | 1949 | if (RemarksEnabled) { | 
|  | 1950 | // Generate remarks for each devirtualized function. | 
|  | 1951 | for (const auto &DT : DevirtTargets) { | 
|  | 1952 | Function *F = DT.second; | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 1953 |  | 
| Sam Elliott | e963c89 | 2017-08-21 16:57:21 +0000 | [diff] [blame] | 1954 | using namespace ore; | 
| Peter Collingbourne | 9110cb4 | 2018-01-05 00:27:51 +0000 | [diff] [blame] | 1955 | OREGetter(F).emit(OptimizationRemark(DEBUG_TYPE, "Devirtualized", F) | 
|  | 1956 | << "devirtualized " | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 1957 | << NV("FunctionName", DT.first)); | 
| Ivan Krasin | b05e06e | 2016-08-05 19:45:16 +0000 | [diff] [blame] | 1958 | } | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1959 | } | 
|  | 1960 |  | 
| Peter Collingbourne | 6d284fa | 2017-03-09 00:21:25 +0000 | [diff] [blame] | 1961 | removeRedundantTypeTests(); | 
| Peter Collingbourne | 0312f61 | 2016-06-25 00:23:04 +0000 | [diff] [blame] | 1962 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1963 | // Rebuild each global we touched as part of virtual constant propagation to | 
|  | 1964 | // include the before and after bytes. | 
|  | 1965 | if (DidVirtualConstProp) | 
|  | 1966 | for (VTableBits &B : Bits) | 
|  | 1967 | rebuildGlobal(B); | 
|  | 1968 |  | 
| Teresa Johnson | 90e630a | 2020-01-23 17:26:51 -0800 | [diff] [blame] | 1969 | // We have lowered or deleted the type checked load intrinsics, so we no | 
| Oliver Stannard | 3b598b9 | 2019-10-17 09:58:57 +0000 | [diff] [blame] | 1970 | // longer have enough information to reason about the liveness of virtual | 
|  | 1971 | // function pointers in GlobalDCE. | 
|  | 1972 | for (GlobalVariable &GV : M.globals()) | 
|  | 1973 | GV.eraseMetadata(LLVMContext::MD_vcall_visibility); | 
|  | 1974 |  | 
| Peter Collingbourne | df49d1b | 2016-02-09 22:50:34 +0000 | [diff] [blame] | 1975 | return true; | 
|  | 1976 | } | 
| Teresa Johnson | d2df54e | 2019-08-02 13:10:52 +0000 | [diff] [blame] | 1977 |  | 
|  | 1978 | void DevirtIndex::run() { | 
|  | 1979 | if (ExportSummary.typeIdCompatibleVtableMap().empty()) | 
|  | 1980 | return; | 
|  | 1981 |  | 
|  | 1982 | DenseMap<GlobalValue::GUID, std::vector<StringRef>> NameByGUID; | 
|  | 1983 | for (auto &P : ExportSummary.typeIdCompatibleVtableMap()) { | 
|  | 1984 | NameByGUID[GlobalValue::getGUID(P.first)].push_back(P.first); | 
|  | 1985 | } | 
|  | 1986 |  | 
|  | 1987 | // Collect information from summary about which calls to try to devirtualize. | 
|  | 1988 | for (auto &P : ExportSummary) { | 
|  | 1989 | for (auto &S : P.second.SummaryList) { | 
|  | 1990 | auto *FS = dyn_cast<FunctionSummary>(S.get()); | 
|  | 1991 | if (!FS) | 
|  | 1992 | continue; | 
|  | 1993 | // FIXME: Only add live functions. | 
|  | 1994 | for (FunctionSummary::VFuncId VF : FS->type_test_assume_vcalls()) { | 
|  | 1995 | for (StringRef Name : NameByGUID[VF.GUID]) { | 
|  | 1996 | CallSlots[{Name, VF.Offset}].CSInfo.addSummaryTypeTestAssumeUser(FS); | 
|  | 1997 | } | 
|  | 1998 | } | 
|  | 1999 | for (FunctionSummary::VFuncId VF : FS->type_checked_load_vcalls()) { | 
|  | 2000 | for (StringRef Name : NameByGUID[VF.GUID]) { | 
|  | 2001 | CallSlots[{Name, VF.Offset}].CSInfo.addSummaryTypeCheckedLoadUser(FS); | 
|  | 2002 | } | 
|  | 2003 | } | 
|  | 2004 | for (const FunctionSummary::ConstVCall &VC : | 
|  | 2005 | FS->type_test_assume_const_vcalls()) { | 
|  | 2006 | for (StringRef Name : NameByGUID[VC.VFunc.GUID]) { | 
|  | 2007 | CallSlots[{Name, VC.VFunc.Offset}] | 
|  | 2008 | .ConstCSInfo[VC.Args] | 
|  | 2009 | .addSummaryTypeTestAssumeUser(FS); | 
|  | 2010 | } | 
|  | 2011 | } | 
|  | 2012 | for (const FunctionSummary::ConstVCall &VC : | 
|  | 2013 | FS->type_checked_load_const_vcalls()) { | 
|  | 2014 | for (StringRef Name : NameByGUID[VC.VFunc.GUID]) { | 
|  | 2015 | CallSlots[{Name, VC.VFunc.Offset}] | 
|  | 2016 | .ConstCSInfo[VC.Args] | 
|  | 2017 | .addSummaryTypeCheckedLoadUser(FS); | 
|  | 2018 | } | 
|  | 2019 | } | 
|  | 2020 | } | 
|  | 2021 | } | 
|  | 2022 |  | 
|  | 2023 | std::set<ValueInfo> DevirtTargets; | 
|  | 2024 | // For each (type, offset) pair: | 
|  | 2025 | for (auto &S : CallSlots) { | 
|  | 2026 | // Search each of the members of the type identifier for the virtual | 
|  | 2027 | // function implementation at offset S.first.ByteOffset, and add to | 
|  | 2028 | // TargetsForSlot. | 
|  | 2029 | std::vector<ValueInfo> TargetsForSlot; | 
|  | 2030 | auto TidSummary = ExportSummary.getTypeIdCompatibleVtableSummary(S.first.TypeID); | 
|  | 2031 | assert(TidSummary); | 
|  | 2032 | if (tryFindVirtualCallTargets(TargetsForSlot, *TidSummary, | 
|  | 2033 | S.first.ByteOffset)) { | 
|  | 2034 | WholeProgramDevirtResolution *Res = | 
|  | 2035 | &ExportSummary.getOrInsertTypeIdSummary(S.first.TypeID) | 
|  | 2036 | .WPDRes[S.first.ByteOffset]; | 
|  | 2037 |  | 
|  | 2038 | if (!trySingleImplDevirt(TargetsForSlot, S.first, S.second, Res, | 
|  | 2039 | DevirtTargets)) | 
|  | 2040 | continue; | 
|  | 2041 | } | 
|  | 2042 | } | 
|  | 2043 |  | 
|  | 2044 | // Optionally have the thin link print message for each devirtualized | 
|  | 2045 | // function. | 
|  | 2046 | if (PrintSummaryDevirt) | 
|  | 2047 | for (const auto &DT : DevirtTargets) | 
|  | 2048 | errs() << "Devirtualized call to " << DT << "\n"; | 
|  | 2049 |  | 
|  | 2050 | return; | 
|  | 2051 | } |