diff --git a/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h b/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h --- a/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h +++ b/llvm/include/llvm/Transforms/IPO/FunctionAttrs.h @@ -19,6 +19,7 @@ #include "llvm/Analysis/LazyCallGraph.h" #include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/PassManager.h" +#include namespace llvm { @@ -27,13 +28,71 @@ class Module; class Pass; -/// The three kinds of memory access relevant to 'readonly' and -/// 'readnone' attributes. -enum MemoryAccessKind { - MAK_ReadNone = 0, - MAK_ReadOnly = 1, - MAK_MayWrite = 2, - MAK_WriteOnly = 3 +/// The kinds of memory access relevant to 'readnone', 'readonly' & 'writeonly' +/// attributes. +class MemoryAccessKind { + enum MemoryAccessKindTy : uint8_t { + MAK_ReadNone = 1, + MAK_ReadOnly = 1 << 1, + MAK_MayWrite = 1 << 2, + MAK_WriteOnly = 1 << 3 + }; + + uint8_t Val = MAK_ReadNone; + +public: + static MemoryAccessKind readNone() { + MemoryAccessKind Res; + return Res; + } + + static MemoryAccessKind readOnly() { + MemoryAccessKind Res; + return Res.setReadOnly(); + } + + static MemoryAccessKind mayWrite() { + MemoryAccessKind Res; + return Res.setMayWrite(); + } + + static MemoryAccessKind writeOnly() { + MemoryAccessKind Res; + return Res.setWriteOnly(); + } + + MemoryAccessKind setReadOnly() { + assert(!isMayWrite() && !isWriteOnly() && "already marked as writing!"); + Val |= MAK_ReadOnly; + return *this; + } + + MemoryAccessKind setMayWrite() { + assert(!isReadOnly() && "already marked as readonly!"); + Val |= MAK_MayWrite; + return *this; + } + + MemoryAccessKind setWriteOnly() { + assert(!isReadOnly() && "already marked as readonly!"); + Val |= MAK_WriteOnly; + return *this; + } + + bool isReadNone() { + if ((Val & MAK_ReadNone) != 0) { + assert((Val & ~MAK_ReadNone) == 0 && + "no other fields should be set for readnone"); + return true; + } + return false; + } + + bool isReadOnly() { return (Val & MAK_ReadOnly) != 0; } + + bool isMayWrite() { return (Val & MAK_MayWrite) != 0; } + + bool isWriteOnly() { return (Val & MAK_WriteOnly) != 0; } }; /// Returns the memory access properties of this copy of the function. diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -127,22 +127,27 @@ FunctionModRefBehavior MRB = AAR.getModRefBehavior(&F); if (MRB == FMRB_DoesNotAccessMemory) // Already perfect! - return MAK_ReadNone; + return MemoryAccessKind::readNone(); if (!ThisBody) { if (AliasAnalysis::onlyReadsMemory(MRB)) - return MAK_ReadOnly; + return MemoryAccessKind::readOnly(); if (AliasAnalysis::onlyWritesMemory(MRB)) - return MAK_WriteOnly; + return MemoryAccessKind::writeOnly(); // Conservatively assume it reads and writes to memory. - return MAK_MayWrite; + return MemoryAccessKind::mayWrite(); } // Scan the function body for instructions that may read or write memory. bool ReadsMemory = false; bool WritesMemory = false; + bool AccessesNonArgs = false; + auto IsNonArgumentObject = [](const Value *Ptr) { + const Value *UO = getUnderlyingObject(Ptr); + return !UO || !isa(UO); + }; for (Instruction &I : instructions(F)) { // Some instructions can be ignored even if they read or write memory. // Detect these now, skipping to the next instruction if one is found. @@ -188,6 +193,8 @@ MemoryLocation Loc = MemoryLocation::getBeforeOrAfter(Arg, I.getAAMetadata()); + AccessesNonArgs |= IsNonArgumentObject(Loc.Ptr); + // Skip accesses to local or constant memory as they don't impact the // externally visible mod/ref behavior. if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true)) @@ -234,12 +241,13 @@ if (WritesMemory) { if (!ReadsMemory) - return MAK_WriteOnly; + return MemoryAccessKind::writeOnly(); else - return MAK_MayWrite; + return MemoryAccessKind::mayWrite(); } - return ReadsMemory ? MAK_ReadOnly : MAK_ReadNone; + return ReadsMemory ? MemoryAccessKind::readOnly() + : MemoryAccessKind::readNone(); } MemoryAccessKind llvm::computeFunctionBodyMemoryAccess(Function &F, @@ -262,20 +270,12 @@ // Non-exact function definitions may not be selected at link time, and an // alternative version that writes to memory may be selected. See the // comment on GlobalValue::isDefinitionExact for more details. - switch (checkFunctionMemoryAccess(*F, F->hasExactDefinition(), - AAR, SCCNodes)) { - case MAK_MayWrite: + MemoryAccessKind AccessKind = + checkFunctionMemoryAccess(*F, F->hasExactDefinition(), AAR, SCCNodes); + if (AccessKind.isMayWrite()) return; - case MAK_ReadOnly: - ReadsMemory = true; - break; - case MAK_WriteOnly: - WritesMemory = true; - break; - case MAK_ReadNone: - // Nothing to do! - break; - } + ReadsMemory |= AccessKind.isReadOnly(); + WritesMemory |= AccessKind.isWriteOnly(); } // If the SCC contains both functions that read and functions that write, then diff --git a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp --- a/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp +++ b/llvm/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp @@ -311,7 +311,7 @@ return; } if (!F->isDeclaration() && - computeFunctionBodyMemoryAccess(*F, AARGetter(*F)) == MAK_ReadNone) + computeFunctionBodyMemoryAccess(*F, AARGetter(*F)).isReadNone()) EligibleVirtualFns.insert(F); }); } diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp --- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -1701,8 +1701,8 @@ // rather than using function attributes to perform local optimization. for (VirtualCallTarget &Target : TargetsForSlot) { if (Target.Fn->isDeclaration() || - computeFunctionBodyMemoryAccess(*Target.Fn, AARGetter(*Target.Fn)) != - MAK_ReadNone || + !computeFunctionBodyMemoryAccess(*Target.Fn, AARGetter(*Target.Fn)) + .isReadNone() || Target.Fn->arg_empty() || !Target.Fn->arg_begin()->use_empty() || Target.Fn->getReturnType() != RetType) return false;