Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the 'initializes' attribute langref and support #84803

Merged
merged 39 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
2554c82
Add 'initialized' attribute langref
haopliu Mar 11, 2024
a9256bf
Update the LangRef
haopliu Mar 12, 2024
9150ece
Refactor the argument uses collecting to a function
haopliu Mar 12, 2024
206b707
Add 'initialized' attribute support and undo the FunctionAttrs refact…
haopliu Mar 14, 2024
eaf5ca0
Update the LangRef (round 2)
haopliu Mar 14, 2024
52cc649
Update FoldingSet.h
haopliu Mar 14, 2024
f7fddb0
Change the attribute type to ConstantRangeList
haopliu Apr 5, 2024
3b5955a
Rename attr to initializes
haopliu Apr 5, 2024
6a1df7a
Update CRL about accessing the range of an EmptySet/FullSet
haopliu Apr 9, 2024
fe2e0a5
Change CRL.insert to be ordered and no overlapping
haopliu Apr 12, 2024
f73df04
Optimize CRL::insert for common cases
haopliu Apr 16, 2024
63c45f6
Update ConstantRangeList
haopliu Apr 16, 2024
dcc2b38
Add unit tests for BitcodeReader/Verifier/Assembler
haopliu Apr 17, 2024
9723322
Add a space after commas
haopliu Apr 17, 2024
e70988d
Update CRL and unit tests
haopliu Apr 18, 2024
9782c31
Update CRL to merge consecutive ranges
haopliu Apr 23, 2024
a924dd3
Handle a common case in CRL::insert and add CRL::dump
haopliu Apr 23, 2024
45cdc34
Nit: change dbgs() to llvm::dbgs()
haopliu Apr 24, 2024
4eeba08
Change the attr impl to TrailingObjects<ConstantRange>
haopliu Apr 25, 2024
502e062
Update LLVMContextImpl.h
haopliu Apr 26, 2024
fac03b8
Merge branch 'llvm:main' into dse-commit
haopliu May 8, 2024
ea3e7c5
Rebase to the latest main
haopliu May 8, 2024
7630510
Change getInitializes() to return ArrayRef<ConstantRange>
haopliu May 8, 2024
a604b4f
Remove llvm::
haopliu May 8, 2024
f941b24
Update LangRef and ConstantRangeList
haopliu May 9, 2024
e52fd9d
Update BitCodeReader/Writer
haopliu May 9, 2024
8e64fa9
Encode bitwidth once for ConstantRangeList kind attr
haopliu May 9, 2024
9ae0fca
Use SpecificBumpPtrAllocator to allocate mem and make sure to call de…
haopliu May 16, 2024
d4809ab
Refactor readConstantRange and update BitcodeReader
haopliu May 17, 2024
99d36cc
Merge branch 'main' into dse-commit
haopliu May 28, 2024
96191cd
Change the attr imple back to ConstantRangeList
haopliu May 30, 2024
645f577
Add getConstantRangeList unittest
haopliu May 30, 2024
2a8ea9a
Add Bitwidth in FoldingSetNodeId
haopliu Jun 11, 2024
37edecd
Merge branch 'main' into dse-commit
haopliu Jun 11, 2024
80306d7
Clean up a redundant check in BitcodeReader
haopliu Jun 11, 2024
67445fe
Change attr impl to normal Alloc w/ a vector and call dtor for each a…
haopliu Jun 18, 2024
68643a5
nit: use uninitialized_copy in the ConstantRangeListAttributeImpl ctor
haopliu Jun 18, 2024
46e8d87
Remove redundant BitWidth in the attr Profile as APInt::Profile alrea…
haopliu Jun 20, 2024
714d02f
Use ConstantRange::contains() in ConstantRangeList::insert()
haopliu Jun 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,31 @@ Currently, only the following parameter attributes are defined:
``readonly`` or a ``memory`` attribute that does not contain
``argmem: write``.

``initialized((Lo1,Hi1),...)``
This attribute indicates that the function initializes the ranges of the
aeubanks marked this conversation as resolved.
Show resolved Hide resolved
pointer parameter's memory, [%p+LoN, %p+HiN): there are no reads, and no
special accesses (such as volatile access or untrackable capture) before
the initialization write in the function.

This attribute implies that the function initializes and does not read
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't use implies.

`This attribute only holds for the memory accessed via this pointer parameter. Other arbitrary accesses to the same memory via other pointers are allowed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

before initialization through this pointer argument, even though it may
read the memory before initialization that the pointer points to, such
as through other arguments.

Note that this attribute does not apply to the unwind edge: the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is now redundant with the unwind comment above

initializing function does not read the pointer before writing to it
regardless of unwinding or not, but the memory may not actually be
written to when unwinding happens.

The ``writable`` or ``dereferenceable`` attribute does not imply the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The ``writable`` or ``dereferenceable`` attribute does not imply the
The ``writable`` or ``dereferenceable`` attribute do not imply the

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

``initialized`` attribute, and ``initialized`` does not imply ``writeonly``
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "does not imply [the] initialized attribute, and ... writeonly, since initialized allows reading from the pointer after writing." ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd separate this into two sentences.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

since ``initialized`` allows reading from the pointer after writing.

This attribute is a list of const ranges in ascending order with no
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/const/constant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, done!

overlapping or adjoining list elements. LoN/HiN are 64-bit ints, and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't heard "adjoining" used in this context, I think "consecutive" is more commonly used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

put LoN/HiN in double backticks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

negative values are allowed in case the argument points partway into
an allocation.

``dead_on_unwind``
At a high level, this attribute indicates that the pointer argument is dead
if the call unwinds, in the sense that the caller will not depend on the
Expand Down
146 changes: 83 additions & 63 deletions llvm/lib/Transforms/IPO/FunctionAttrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,56 +580,21 @@ struct ArgumentUsesTracker : public CaptureTracker {
const SCCNodeSet &SCCNodes;
};

} // end anonymous namespace

namespace llvm {

template <> struct GraphTraits<ArgumentGraphNode *> {
using NodeRef = ArgumentGraphNode *;
using ChildIteratorType = SmallVectorImpl<ArgumentGraphNode *>::iterator;

static NodeRef getEntryNode(NodeRef A) { return A; }
static ChildIteratorType child_begin(NodeRef N) { return N->Uses.begin(); }
static ChildIteratorType child_end(NodeRef N) { return N->Uses.end(); }
};

template <>
struct GraphTraits<ArgumentGraph *> : public GraphTraits<ArgumentGraphNode *> {
static NodeRef getEntryNode(ArgumentGraph *AG) { return AG->getEntryNode(); }

static ChildIteratorType nodes_begin(ArgumentGraph *AG) {
return AG->begin();
}

static ChildIteratorType nodes_end(ArgumentGraph *AG) { return AG->end(); }
};

} // end namespace llvm

/// Returns Attribute::None, Attribute::ReadOnly or Attribute::ReadNone.
static Attribute::AttrKind
determinePointerAccessAttrs(Argument *A,
const SmallPtrSet<Argument *, 8> &SCCNodes) {
/// Get all uses of an argument in the function and store them to different
/// lists: Reads, Writes, and SpecialUses.
std::tuple<SmallVector<Instruction *, 16>, SmallVector<Instruction *, 16>,
SmallVector<Instruction *, 16>>
getArgumentUses(Argument *A, const SmallPtrSet<Argument *, 8> &SCCNodes) {
SmallVector<Use *, 32> Worklist;
SmallPtrSet<Use *, 32> Visited;

// inalloca arguments are always clobbered by the call.
if (A->hasInAllocaAttr() || A->hasPreallocatedAttr())
return Attribute::None;

bool IsRead = false;
bool IsWrite = false;
SmallVector<Instruction *, 16> Reads, Writes, SpecialUses;

for (Use &U : A->uses()) {
Visited.insert(&U);
Worklist.push_back(&U);
}

while (!Worklist.empty()) {
if (IsWrite && IsRead)
// No point in searching further..
return Attribute::None;

Use *U = Worklist.pop_back_val();
Instruction *I = cast<Instruction>(U->getUser());

Expand All @@ -649,7 +614,7 @@ determinePointerAccessAttrs(Argument *A,
case Instruction::Invoke: {
CallBase &CB = cast<CallBase>(*I);
if (CB.isCallee(U)) {
IsRead = true;
Reads.push_back(I);
// Note that indirect calls do not capture, see comment in
// CaptureTracking for context
continue;
Expand All @@ -668,12 +633,15 @@ determinePointerAccessAttrs(Argument *A,
if (Visited.insert(&UU).second)
Worklist.push_back(&UU);
} else if (!CB.doesNotCapture(UseIndex)) {
if (!CB.onlyReadsMemory())
if (!CB.onlyReadsMemory()) {
// If the callee can save a copy into other memory, then simply
// scanning uses of the call is insufficient. We have no way
// of tracking copies of the pointer through memory to see
// if a reloaded copy is written to, thus we must give up.
return Attribute::None;
// if a reloaded copy is written to, thus we treat it as special
// uses.
SpecialUses.push_back(I);
continue;
}
// Push users for processing once we finish this one
if (!I->getType()->isVoidTy())
for (Use &UU : I->uses())
Expand All @@ -698,52 +666,98 @@ determinePointerAccessAttrs(Argument *A,
if (CB.doesNotAccessMemory(UseIndex)) {
/* nop */
} else if (!isModSet(ArgMR) || CB.onlyReadsMemory(UseIndex)) {
IsRead = true;
Reads.push_back(I);
} else if (!isRefSet(ArgMR) ||
CB.dataOperandHasImpliedAttr(UseIndex, Attribute::WriteOnly)) {
IsWrite = true;
Writes.push_back(I);
} else {
return Attribute::None;
SpecialUses.push_back(I);
}
break;
}

case Instruction::Load:
// A volatile load has side effects beyond what readonly can be relied
// upon.
if (cast<LoadInst>(I)->isVolatile())
return Attribute::None;
if (cast<LoadInst>(I)->isVolatile()) {
SpecialUses.push_back(I);
continue;
}

IsRead = true;
Reads.push_back(I);
break;

case Instruction::Store:
if (cast<StoreInst>(I)->getValueOperand() == *U)
if (cast<StoreInst>(I)->getValueOperand() == *U) {
// untrackable capture
return Attribute::None;
SpecialUses.push_back(I);
continue;
}

// A volatile store has side effects beyond what writeonly can be relied
// upon.
if (cast<StoreInst>(I)->isVolatile())
return Attribute::None;
if (cast<StoreInst>(I)->isVolatile()) {
SpecialUses.push_back(I);
continue;
}

IsWrite = true;
Writes.push_back(I);
break;

case Instruction::ICmp:
case Instruction::Ret:
break;

default:
return Attribute::None;
SpecialUses.push_back(I);
}
}
return {Reads, Writes, SpecialUses};
}

} // end anonymous namespace

namespace llvm {

template <> struct GraphTraits<ArgumentGraphNode *> {
using NodeRef = ArgumentGraphNode *;
using ChildIteratorType = SmallVectorImpl<ArgumentGraphNode *>::iterator;

static NodeRef getEntryNode(NodeRef A) { return A; }
static ChildIteratorType child_begin(NodeRef N) { return N->Uses.begin(); }
static ChildIteratorType child_end(NodeRef N) { return N->Uses.end(); }
};

template <>
struct GraphTraits<ArgumentGraph *> : public GraphTraits<ArgumentGraphNode *> {
static NodeRef getEntryNode(ArgumentGraph *AG) { return AG->getEntryNode(); }

static ChildIteratorType nodes_begin(ArgumentGraph *AG) {
return AG->begin();
}

static ChildIteratorType nodes_end(ArgumentGraph *AG) { return AG->end(); }
};

} // end namespace llvm

/// Returns Attribute::None, Attribute::ReadOnly or Attribute::ReadNone.
static Attribute::AttrKind
determinePointerAccessAttrs(Argument *A, SmallVector<Instruction *, 16> &Reads,
SmallVector<Instruction *, 16> &Writes,
SmallVector<Instruction *, 16> &SpecialUses) {
// inalloca arguments are always clobbered by the call.
if (A->hasInAllocaAttr() || A->hasPreallocatedAttr())
return Attribute::None;

if (!SpecialUses.empty())
return Attribute::None;

if (IsWrite && IsRead)
if (!Writes.empty() && !Reads.empty())
return Attribute::None;
else if (IsRead)
else if (!Reads.empty())
return Attribute::ReadOnly;
else if (IsWrite)
else if (!Writes.empty())
return Attribute::WriteOnly;
else
return Attribute::ReadNone;
Expand Down Expand Up @@ -931,7 +945,9 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
// functions in the SCC.
SmallPtrSet<Argument *, 8> Self;
Self.insert(&A);
Attribute::AttrKind R = determinePointerAccessAttrs(&A, Self);
auto [Reads, Writes, SpecialUses] = getArgumentUses(&A, Self);
Attribute::AttrKind R =
determinePointerAccessAttrs(&A, Reads, Writes, SpecialUses);
if (R != Attribute::None)
if (addAccessAttr(&A, R))
Changed.insert(F);
Expand Down Expand Up @@ -963,7 +979,9 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
// Infer the access attributes given the new nocapture one
SmallPtrSet<Argument *, 8> Self;
Self.insert(&*A);
Attribute::AttrKind R = determinePointerAccessAttrs(&*A, Self);
auto [Reads, Writes, SpecialUses] = getArgumentUses(&*A, Self);
Attribute::AttrKind R =
determinePointerAccessAttrs(&*A, Reads, Writes, SpecialUses);
if (R != Attribute::None)
addAccessAttr(A, R);
}
Expand Down Expand Up @@ -1032,7 +1050,9 @@ static void addArgumentAttrs(const SCCNodeSet &SCCNodes,
Attribute::AttrKind AccessAttr = Attribute::ReadNone;
for (ArgumentGraphNode *N : ArgumentSCC) {
Argument *A = N->Definition;
Attribute::AttrKind K = determinePointerAccessAttrs(A, ArgumentSCCNodes);
auto [Reads, Writes, SpecialUses] = getArgumentUses(A, ArgumentSCCNodes);
Attribute::AttrKind K =
determinePointerAccessAttrs(A, Reads, Writes, SpecialUses);
AccessAttr = meetAccessAttr(AccessAttr, K);
if (AccessAttr == Attribute::None)
break;
Expand Down
Loading