This is an archive of the discontinued LLVM Phabricator instance.

[Attributor] Derive memory location attributes (argmemonly, ...)
ClosedPublic

Authored by jdoerfert on Jan 26 2020, 1:13 AM.

Details

Summary

In addition to memory behavior attributes (readonly/writeonly) we now
derive memory location attributes (argmemonly/inaccessiblememonly/...).
The former is part of AAMemoryBehavior and the latter part of
AAMemoryLocation. While they are similar in nature it got messy when
they were put in a single AA. Location attributes for arguments and
floating values will follow later.

Note that both memory attributes kinds can derive readnone. If there are
no accesses AAMemoryBehavior will derive readnone. If there are accesses
but only to stack (=local) locations AAMemoryLocation will derive
readnone.

Diff Detail

Event Timeline

jdoerfert created this revision.Jan 26 2020, 1:13 AM
Herald added a project: Restricted Project. · View Herald TranscriptJan 26 2020, 1:13 AM

Unit tests: unknown.

clang-tidy: fail. clang-tidy found 1 errors and 2 warnings. 0 of them are added as review comments below (why?).

clang-format: fail. Please format your changes with clang-format by running git-clang-format HEAD^ or applying this patch.

Build artifacts: diff.json, clang-tidy.txt, clang-format.patch, CMakeCache.txt, console-log.txt

Pre-merge checks is in beta. Report issue. Please join beta or enable it for your project.

jdoerfert updated this revision to Diff 240768.Jan 27 2020, 9:06 PM

Improvements based on later use cases

Unit tests: unknown.

clang-tidy: fail. clang-tidy found 1 errors and 2 warnings. 0 of them are added as review comments below (why?).

clang-format: pass.

Build artifacts: diff.json, clang-tidy.txt, clang-format.patch, CMakeCache.txt, console-log.txt

Pre-merge checks is in beta. Report issue. Please join beta or enable it for your project.

uenoku added a comment.Feb 2 2020, 2:55 AM

Are there tests for inaccessiblememonly or inaccessiblemem_or_argmemonly?

llvm/test/Transforms/Attributor/ArgumentPromotion/X86/min-legal-vector-width.ll
320

Why is there new writeonly? Is it simply by on-demand creation?

jdoerfert marked an inline comment as done.Feb 2 2020, 10:09 AM

Are there tests for inaccessiblememonly or inaccessiblemem_or_argmemonly?

Good catch. I think I forgot to merge in tests from an old version. Will add tests for both.

llvm/test/Transforms/Attributor/ArgumentPromotion/X86/min-legal-vector-width.ll
320

I think so. memset is known argmemonly and writeonly anyway.

jdoerfert edited the summary of this revision. (Show Details)Feb 2 2020, 10:10 AM
jdoerfert updated this revision to Diff 243431.Feb 8 2020, 11:39 PM

Add inaccessible(_or_argmem) tests

jdoerfert updated this revision to Diff 243432.Feb 8 2020, 11:40 PM

Remove accidental change

jdoerfert updated this revision to Diff 243433.Feb 8 2020, 11:43 PM

Merge minor fixes

jdoerfert updated this revision to Diff 243434.Feb 8 2020, 11:46 PM

Remove fix for follow up patch accidentally merged in here

jdoerfert updated this revision to Diff 243438.Feb 9 2020, 1:00 AM

Minor update

uenoku added a comment.EditedFeb 13 2020, 10:33 PM

Sorry for the delay. I have just completed my bachelor's course so I can take enough time for development.

I think a state without NO_UNKOWN_MEM is semantically identical to a pessimistic fixpoint, right?

llvm/include/llvm/Transforms/IPO/Attributor.h
2600

nit: functions → function

llvm/lib/Transforms/IPO/Attributor.cpp
6199–6200

Why is byval argument regarded as local memory?

llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll
17–18

Is this change caused by this patch?

jdoerfert marked 2 inline comments as done.Feb 13 2020, 10:47 PM

Sorry for the delay. I have just completed my bachelor's course so I can take enough time for development.

No worries. I hope that went well!

I think a state without NO_UNKOWN_MEM is semantically identical to a pessimistic fixpoint, right?

In this patch probably true. With D73527 users can iterate over the "unknown" accesses and determine their original on their own. Once we give up, we do not guarantee all accesses are tracked.

llvm/lib/Transforms/IPO/Attributor.cpp
6199–6200

This is how I understand it:

A `byval` makes a copy "on the call edge". The copy lives in the scope of the callee. So the byval argument is actually a locak (stack) copy in the callee which is initialized with the incoming value at the beginning of the function. That's also why we should mark byval call site arguments as read only regardless of their argument counterpart.
llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll
17–18

Yes. With this patch @CaptureAStruct is known to be readnone which makes all operations we delete here side-effect free, thus AAIsDead removes them for us. It was not readnone before because there are loads and stores but only to local memory.

uenoku accepted this revision.Feb 13 2020, 11:13 PM

I think a state without NO_UNKOWN_MEM is semantically identical to a pessimistic fixpoint, right?

In this patch probably true. With D73527 users can iterate over the "unknown" accesses and determine their original on their own. Once we give up, we do not guarantee all accesses are tracked.

I understand.

LGTM.

llvm/test/Transforms/Attributor/ArgumentPromotion/fp80.ll
17–18

Oh, I understand. It's interesting.

This revision is now accepted and ready to land.Feb 13 2020, 11:13 PM
This revision was automatically updated to reflect the committed changes.
rcox2 added a subscriber: rcox2.Aug 13 2021, 6:56 PM
rcox2 added inline comments.
llvm/include/llvm/Transforms/IPO/Attributor.h
2603

Hi Johannes,

We have a question about the intended behavior of the attributor on functions with byval arguments.
Consider the following input (t1.ll)

{code}
%struct._vec = type { float, float, float }

define void @caller() {

%x = alloca %struct._vec, align 4
%y = getelementptr inbounds %struct._vec, %struct._vec* %x, i32 0, i32 1
store float 42.0, float* %y
%v = call float @callee(%struct._vec* %x)
ret float %v

}

define float @callee(%struct._vec* byval(%struct._vec) %x) {

%y = getelementptr inbounds %struct._vec, %struct._vec* %x, i32 0, i32 1
%v = load float, float* %y
ret float %v

}
{code}

If I run this through the attributor with:
opt -passes='module(attributor)' -S t1.ll
I get:

{code}
%struct._vec = type { float, float, float }

; Function Attrs: nofree nosync nounwind readnone willreturn
define float @caller() #0 {

%x = alloca %struct._vec, align 4
%y = getelementptr inbounds %struct._vec, %struct._vec* %x, i32 0, i32 1
store float 4.200000e+01, float* %y, align 4
%v = call float @callee(%struct._vec* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(12) %x) #0
ret float %v

}

; Function Attrs: nofree nosync nounwind readnone willreturn
define float @callee(%struct._vec* noalias nocapture nofree nonnull readonly byval(%struct._vec) align 4 dereferenceable(12) %x) #0 {

%y = getelementptr inbounds %struct._vec, %struct._vec* %x, i32 0, i32 1
%v = load float, float* %y, align 4
ret float %v

}

attributes #0 = { nofree nosync nounwind readnone willreturn }
{code}

The key point is that the @callee is marked 'readnone' rather than 'readonly'.

Adding the dse pass:
opt -passes='module(attributor),dse' -S t1.ll

%struct._vec = type { float, float, float }
{code}

; Function Attrs: nofree nosync nounwind readnone willreturn
define float @caller() #0 {

%x = alloca %struct._vec, align 4
%v = call float @callee(%struct._vec* noalias nocapture nofree noundef nonnull readonly align 4 dereferenceable(12) %x) #0
ret float %v

}

; Function Attrs: nofree nosync nounwind readnone willreturn
define float @callee(%struct._vec* noalias nocapture nofree nonnull readonly byval(%struct._vec) align 4 dereferenceable(12) %x) #0 {

%y = getelementptr inbounds %struct._vec, %struct._vec* %x, i32 0, i32 1
%v = load float, float* %y, align 4
ret float %v

}

attributes #0 = { nofree nosync nounwind readnone willreturn }
{code}

With a loss of the store in the caller. So, is this a bug in the attributor, because it is marking the callee "readnone"? Or is it a bug in dse, because it thinks that 'readnone' with a byval arg menas it can eleiminate the store in the caller?

With a loss of the store in the caller. So, is this a bug in the attributor, because it is marking the callee "readnone"? Or is it a bug in dse, because it thinks that 'readnone' with a byval arg menas it can eleiminate the store in the caller?

This is a good point. Initially, I was hoping byval arguments would be owned by the callee, however, as of right now they are not. Even if we conceptually let them be owned by the callee, we would need some changes in various places to account for the explicit copy while retaining the readnone the attributor adds. Long story short, this is (right now) a bug and should be fixed. I made D108140 which also contains a TODO.

Thanks, Johannes, for providing a fix to get around this problem.

  • Robert