[RFC][Attributor] change argument privatization to size-based type selection#181716
[RFC][Attributor] change argument privatization to size-based type selection#181716
Conversation
…ection WARNING: for feedback only, not yet human-edited to meet quality standards. The current Attributor design assumes that getAllocatedType has semantic meaning. This is fairly safe currently, based on its use of isDenselyPacked to conservatively reject cases where the semantics of the type would be obviously unsound, but it is suboptimal and also contraditory to the design of Attributor's fixed-point use analysis to be taking this information instead from the definition site. There is probably a few options here, depending on what matters to the ABI: the simplest option is what I've asked Claude to do here, just pick a reasonable size type that maps adequately to the size of the object on the current platform. Alternatively, we could use PtrUseAnalysis (with my bugfixes in llvm#179726) to collect all uses of the pointer and then compute and estimated fixed-point partitioning of that information globally (essentially estimating what SROA, where PtrUseAnalysis originally came from, will be able to do once the argment values are split, plus IPO handling). Summary of plan: replace type-based privatization with size-based approach that selects optimal types based on data size, alignment, and target capabilities. Uses integers for small data, vectors for medium sizes, and aligned arrays for larger data. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
You can test this locally with the following command:git diff -U0 --pickaxe-regex -S '([^a-zA-Z0-9#_-]undef([^a-zA-Z0-9_-]|$)|UndefValue::get)' 'HEAD~1' HEAD llvm/include/llvm/Transforms/IPO/Attributor.h llvm/lib/Transforms/IPO/AttributorAttributes.cpp llvm/test/Transforms/Attributor/ArgumentPromotion/X86/attributes.ll llvm/test/Transforms/Attributor/ArgumentPromotion/X86/min-legal-vector-width.ll llvm/test/Transforms/Attributor/ArgumentPromotion/array.ll llvm/test/Transforms/Attributor/ArgumentPromotion/attrs.ll llvm/test/Transforms/Attributor/ArgumentPromotion/byval-2.ll llvm/test/Transforms/Attributor/ArgumentPromotion/byval.ll llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll llvm/test/Transforms/Attributor/ArgumentPromotion/tail.ll llvm/test/Transforms/Attributor/IPConstantProp/2009-09-24-byval-ptr.ll llvm/test/Transforms/Attributor/nofpclass.ll llvm/test/Transforms/Attributor/value-simplify-pointer-info.ll llvm/test/Transforms/Attributor/value-simplify.llThe following files introduce new uses of undef:
Undef is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields In tests, avoid using For example, this is considered a bad practice: define void @fn() {
...
br i1 undef, ...
}Please use the following instead: define void @fn(i1 %cond) {
...
br i1 %cond, ...
}Please refer to the Undefined Behavior Manual for more information. |
You can test this locally with the following command:git-clang-format --diff origin/main HEAD --extensions cpp,h -- llvm/include/llvm/Transforms/IPO/Attributor.h llvm/lib/Transforms/IPO/AttributorAttributes.cpp --diff_from_common_commit
View the diff from clang-format here.diff --git a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
index 17b9b9308..26fd1afc7 100644
--- a/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
+++ b/llvm/lib/Transforms/IPO/AttributorAttributes.cpp
@@ -7382,9 +7382,8 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
}
// Get alignment information for choosing optimal type
- const auto *AlignAA =
- A.getAAFor<AAAlign>(*this, IRPosition::value(getAssociatedValue()),
- DepClassTy::OPTIONAL);
+ const auto *AlignAA = A.getAAFor<AAAlign>(
+ *this, IRPosition::value(getAssociatedValue()), DepClassTy::OPTIONAL);
Align Alignment = AlignAA ? AlignAA->getAssumedAlign() : Align(1);
// Get TTI for target-specific type selection
@@ -7532,9 +7531,9 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
/// 2. Up to vector register size: use vector of integers
/// 3. Otherwise: use array of integers sized to alignment
static Type *getPrivatizableReplacementType(uint64_t Size, Align Alignment,
- const DataLayout &DL,
- LLVMContext &Ctx,
- const TargetTransformInfo *TTI) {
+ const DataLayout &DL,
+ LLVMContext &Ctx,
+ const TargetTransformInfo *TTI) {
// Strategy 1: If size fits in pointer, use integer type
uint64_t PointerSizeInBits = DL.getPointerSizeInBits();
if (Size * 8 <= PointerSizeInBits)
@@ -7574,10 +7573,11 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
return ArrayType::get(ElementTy, NumElements);
}
- /// Initialize \p Base with the replacement value argument at position \p ArgNo.
- /// Stores the value argument into the alloca.
- static void createInitialization(Type *ReplacementTy, Value &Base, Function &F,
- unsigned ArgNo, BasicBlock::iterator IP) {
+ /// Initialize \p Base with the replacement value argument at position \p
+ /// ArgNo. Stores the value argument into the alloca.
+ static void createInitialization(Type *ReplacementTy, Value &Base,
+ Function &F, unsigned ArgNo,
+ BasicBlock::iterator IP) {
assert(ReplacementTy && "Expected valid replacement type!");
// The argument is a value - store it into the alloca
@@ -7603,7 +7603,8 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
ChangeStatus manifest(Attributor &A) override {
if (!PrivatizableSize)
return ChangeStatus::UNCHANGED;
- assert(!PrivatizableSize->isZero() && "Expected non-zero privatizable size!");
+ assert(!PrivatizableSize->isZero() &&
+ "Expected non-zero privatizable size!");
// Collect all tail calls in the function as we cannot allow new allocas to
// escape into tail recursion.
@@ -7627,7 +7628,8 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
A.getAAFor<AAAlign>(*this, IRPosition::value(*Arg), DepClassTy::NONE);
// Callback to repair the associated function. A new alloca is placed at the
- // beginning and initialized with the replacement value passed as an argument.
+ // beginning and initialized with the replacement value passed as an
+ // argument.
Type *RepTy = PrivatizableType;
Attributor::ArgumentReplacementInfo::CalleeRepairCBTy FnRepairCB =
[=](const Attributor::ArgumentReplacementInfo &ARI,
@@ -7636,10 +7638,10 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
BasicBlock::iterator IP = EntryBB.getFirstInsertionPt();
const DataLayout &DL = IP->getDataLayout();
unsigned AS = DL.getAllocaAddrSpace();
- Instruction *AI = new AllocaInst(RepTy, AS,
- Arg->getName() + ".priv", IP);
- createInitialization(RepTy, *AI, ReplacementFn,
- ArgIt->getArgNo(), IP);
+ Instruction *AI =
+ new AllocaInst(RepTy, AS, Arg->getName() + ".priv", IP);
+ createInitialization(RepTy, *AI, ReplacementFn, ArgIt->getArgNo(),
+ IP);
if (AI->getType() != Arg->getType())
AI = BitCastInst::CreatePointerBitCastOrAddrSpaceCast(
@@ -7650,16 +7652,16 @@ struct AAPrivatizablePtrArgument final : public AAPrivatizablePtrImpl {
CI->setTailCall(false);
};
- // Callback to repair a call site of the associated function. The replacement
- // value is loaded prior to the call and passed to the new function version.
+ // Callback to repair a call site of the associated function. The
+ // replacement value is loaded prior to the call and passed to the new
+ // function version.
Attributor::ArgumentReplacementInfo::ACSRepairCBTy ACSRepairCB =
[=](const Attributor::ArgumentReplacementInfo &ARI,
AbstractCallSite ACS, SmallVectorImpl<Value *> &NewArgOperands) {
// When no alignment is specified for the load instruction,
// natural alignment is assumed.
createReplacementValues(
- AlignAA ? AlignAA->getAssumedAlign() : Align(0),
- RepTy, ACS,
+ AlignAA ? AlignAA->getAssumedAlign() : Align(0), RepTy, ACS,
ACS.getCallArgOperand(ARI.getReplacedArg().getArgNo()),
NewArgOperands);
};
|
🐧 Linux x64 Test Results
All executed tests passed, but another part of the build failed. Click on a failure below to see the details. lib/Transforms/IPO/CMakeFiles/LLVMipo.dir/AttributorAttributes.cpp.oIf these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the |
WARNING: for feedback only on approach and design goals, not yet human-edited to meet quality standards.
The current Attributor design assumes that AllocaInst getAllocatedType has semantic meaning. This is fairly safe currently, based on its use of isDenselyPacked to conservatively reject cases where the semantics of the type would be obviously unsound, but it is suboptimal and also contradictory to the design of Attributor's fixed-point use analysis to be taking this information instead from the definition site. It is also one of very few remaining uses of getAllocatedType in the optimization passes. Once all are gone, we can delete this non-semantic accessor, just like typed-pointers and typed-GEP are being deleted for that same reason, and simplify the IR.
There is probably a few options here, depending on what matters to the ABI and the intent of the pass from the original authors: the simplest option is what I've asked Claude to do here, just pick some reasonable size type that maps adequately to the size and alignment of the object on the current platform. Alternatively, we could use PtrUseAnalysis (with my bugfixes in #179726) to collect all uses of the pointer and then compute an estimated fixed-point partitioning of that information globally (essentially estimating what SROA, where PtrUseAnalysis originally came from, will be able to do once the argument values are split, plus IPO handling)–the benefit of this approach being that several other passes (GlobalOpt and GPU targets) already do, or need to do, that same memory access analysis.
tl;dr: Replace type-based privatization with size-based approach that selects optimal types based on data size, alignment, and target capabilities. Uses integers for small data, vectors for medium sizes, and aligned arrays for larger data.