This is an archive of the discontinued LLVM Phabricator instance.

NewGVN
ClosedPublic

Authored by davide on Nov 1 2016, 5:03 PM.

Diff Detail

Event Timeline

davide updated this revision to Diff 76658.Nov 1 2016, 5:03 PM
davide retitled this revision from to [WIP] Initial NewGVN.
davide updated this object.
davide removed subscribers: mgorny, modocache.
filcab added a subscriber: filcab.Nov 2 2016, 2:56 PM
davide retitled this revision from [WIP] Initial NewGVN to NewGVN.Nov 15 2016, 10:50 PM
davide added reviewers: dberlin, majnemer.
davide added a subscriber: llvm-commits.
majnemer added inline comments.Nov 15 2016, 11:04 PM
include/llvm/Transforms/Scalar/GVNExpression.h
15–16

I'd insert a newline here.

17–18

I'd insert a newline here.

77

Comments should end with a period.

86

other should follow the naming convention.

119–120

unsigned int -> unsigned, there are other places where this not followed, please correct them.

129–162

Inline keyword is superfluous, we are in the class definition. Please correct this elsewhere as well.

557

Please use const auto * when assigning w/ a dyn_cast, the type should be clear.

Great to see this finally come upstream! Thanks for driving this :)

(Just two quick comments inline while I'm here)

include/llvm/Transforms/Scalar/GVNExpression.h
222

final does not seem applied equally everywhere, for instance why isn't LoadExpression marked final as well?

242

= default; (check the others)

dberlin added inline comments.Nov 15 2016, 11:44 PM
lib/Transforms/Scalar/NewGVN.cpp
1002

There's a bug (well, incompleteness) here that i just noticed
For memory, we also need to mark the uses of the MemoryDef/MemoryUse for the instruction as touched.
(and handle MemoryPhi's).
While most of the time, they are already touched, otherwise, will not iterate when we have discovered something about memory for, say, store over store.

MemoryPhi's will need value numbering when I fix the store over store problem, but for now, i'd just skip them.

So i'd add something like

if (MemoryAccess *MA = MSSA->getMemoryAccess(V)) 
  markMemoryUsersTouched(MA);

where markMemoryUsersTouched just walks the users of MA and mark MA->getInst() touched iff it's a MemoryUseOrDef.

This will ensure memory instructions change when we discover new things about them.

Sorry, in GCC, they are part of the IR, so there aren't two use lists :)

Prazek added a subscriber: Prazek.Nov 17 2016, 2:25 PM
Prazek added inline comments.
include/llvm/Transforms/Scalar/GVNExpression.h
38

I am not really sure what does it mean, where other enums seems to be self descriptive.
Does it mean "inside basic block, but not BB start nor end?"

66

You could call another ctor here like:
Expression(unsigned int o = ..) : Expression(ExpressionTypeBase), o) {}

davide updated this revision to Diff 78441.Nov 17 2016, 5:25 PM
davide marked 7 inline comments as done.

Addressed the first round of comments + random cleanups I found by inspection.

davide updated this revision to Diff 78442.Nov 17 2016, 5:25 PM
davide edited reviewers, added: Bigcheese; removed: deadalnix.
davide added inline comments.Nov 17 2016, 6:54 PM
include/llvm/Transforms/Scalar/GVNExpression.h
222

Just an oversight. Fixed (also in other places).

242

I converted all of them, please let me know if I missed something

Can you also post on Phabricator a mockup of a patch to enable the newgvn pass by default in the trunk clang compiler in order to simplify end-user testing? I noticed that the current clang compiler doesn't seem to enable the current gvn pass by default.

davide added a comment.EditedNov 19 2016, 12:40 PM

Can you also post on Phabricator a mockup of a patch to enable the newgvn pass by default in the trunk clang compiler in order to simplify end-user testing? I noticed that the current clang compiler doesn't seem to enable the current gvn pass by default.

Sure, I'll make one. I'm not sure what you mean when you say that the current clang compiler doesn't call the current GVN. In EmitAssemblyHelper::CreatePasses, we call inside LLVM, PMBuilder.populateModulePassManager() which creates a pass pipeline containing GVN (see Transforms/IPO/PassManagerBuilder.cpp).

Hi Davide,

A high level comment for an issue I've just run into when rebasing GVNSink on top of NewGVN - you've defined LoadExpression::equals and StoreExpression::equals in the header, which gives multiple definition errors (for ::equals and the vtables) if any other file tries to #include <GVNExpression.h>.

Cheers,

James

davide updated this revision to Diff 78751.Nov 21 2016, 12:12 PM
davide edited edge metadata.

Move LoadExpression::equals/StoreExpression::equals to NewGVN.cpp

Bigcheese requested changes to this revision.Nov 27 2016, 5:59 PM
Bigcheese edited edge metadata.
Bigcheese added inline comments.
include/llvm/Transforms/Scalar/GVNExpression.h
50

These don't need to be private.

55

This can be private.

56

Since you've got getOptcode() and setOpcode() this doesn't need to be protected. Either make this public and get rid of the accessors, or make it private.

65

I think this can be removed.

70
72–84

The behavior of this operator is quite nonintuitive. It's actually isTrivallyCongruent(), not is equal. However if this is the natural equality definition for this type, then it would be fine with a comment explaining that.

88–90

This can return a different hash_code for Expressions which compare equal.

112–114

Don't need to be private.

166

Not needed.

190

Anchor.

include/llvm/Transforms/Scalar/NewGVN.h
11–13

This looks like the comment from the old GVN pass.

lib/Transforms/Scalar/NewGVN.cpp
2

NewGVN.cpp

11–15

Old comment. Missing \file

128

Prefer ~0u

133

Prefer ~1u

146

This seems like the wrong equality operator for the above hash. It will return true for things that don't hash to the same value.

211

This doesn't need to be explicit.

1227

Do we have a proper type anywhere to use for a range instead of pair? first and last.

This revision now requires changes to proceed.Nov 27 2016, 5:59 PM
davide updated this revision to Diff 79876.Nov 30 2016, 11:53 PM
davide edited edge metadata.
davide marked 10 inline comments as done and an inline comment as not done.Nov 30 2016, 11:53 PM

Addressed Michael's comments.

Sorry for the delay. I'm on the road this week with limited access to internet.
I encourage other people to comment so that we can get the first revision in tree and iterate from there.

include/llvm/Transforms/Scalar/GVNExpression.h
88–90

See Danny's reply (in the next mail).

lib/Transforms/Scalar/NewGVN.cpp
1227

No, I personally don't mind std::pair, but I can change it if you feel strong.

davide added a comment.Dec 5 2016, 6:57 AM

Monday morning ping.
I would like to get the first cut in-tree soon (let's say, this week or the next) so we can iterate in-tree.

RKSimon edited edge metadata.Dec 6 2016, 10:59 AM

Mainly a code style review and a few random things I managed to spot. I don't know enough about GVN to go in depth.

What I did notice was that this really needs commenting more thoroughly, describing each Expression class type etc. and there needs to be more consistency with the ordering of class variables / method types.

include/llvm/Transforms/Scalar/GVNExpression.h
35

Use a short prefix not the whole enum type name: ET_Base, ..... ET_Store, ET_BasicEnd

48

Should this be called ExpressionBase or something?

63

Keep the constructors / destructors at the top of the public block? At least not mixed in with the other methods.

68

Magic numbers - replace with enum?

84

Split the print/debug methods into their own section headed with (similar to MachineInstr.h):

//
// Debugging support
//
84

Debug print out code can be quite bulky - worth moving these to a cpp file instead of inline?

117

Consistently put these next to the ops_begin() methods?

156

Put these consistently in the same place in each class.

206

Add override? Probably in a few other places too.

445

Put class variables consistently at the top or the bottom.

lib/Transforms/Scalar/NewGVN.cpp
137

static_cast<uintptr_t>(-1) ?

174

Is it a good idea to leave an initializer here?

246

Don't leave this amongst the creators

376

newline

437

Did you mean to compare pointer values?

443

Tidyup?

E->ops_push_back(lookupOperandLeader(Arg1, nullptr, B));
E->ops_push_back(lookupOperandLeader(Arg2, nullptr, B));
460
return nullptr;
553

wasted newline

591

Remove braces:

if (Value *V = ConstantFoldInstOperands(I, C, *DL, TLI))
677

E->ops_push_back(lookupOperandLeader(PointerOp, LI, B))

810
if (II && EI->getNumIndices() == 1 && *EI->idx_begin() == 0) {
979
if (E == nullptr) {
1271

Same named variables is asking for trouble

1434

newline

1573

Why is this increment not done in the for block?

1681

Use for-range loop?

for (CongruenceClass *CC : CongruenceClasses])
1828

Why isn't this increment done in the for block?

silvas added a subscriber: silvas.Dec 8 2016, 11:30 PM

Simon has done a pretty good job with the style review. Once his comments are cleaned up it will be easier to provide further comments.

A couple other stylistic comments and a doc request. I still need to do a walk-through of the code and stuff to provide more in-depth comments.

include/llvm/Transforms/Scalar/GVNExpression.h
242

PrintEType here and elsewhere.

lib/Transforms/Scalar/NewGVN.cpp
11

This file comment needs to be signficantly expanded. Remember, lots of people looking at this class might be e.g. people taking a compiler class that want to look at a "real" GVN implementation. Let's make sure come away impressed so that they will want to join LLVM!

At the very least, some citations for the relevant paper and stuff, along with summary of which exact variant of the algorithm are implemented would be good. Also, I think the high-level idea of GVN is simple enough that a high-level from-scratch description would be appropriate. I can help with writing this if you want.

In theory, we can expand this later, but when have you seen a commit improving a file-level comment? The only one I remember was in response to post-commit review asking for an improved file-level comment. So getting it right the first time is actually pretty important.

475

Are the ifdef's necessary when all that is inside is a DEBUG?

Also, uhm, this needs tests. Maybe you can get away with just adding some more RUN lines to some LegacyGVN tests?

Also, uhm, this needs tests. Maybe you can get away with just adding some more RUN lines to some LegacyGVN tests?

Yes, this is my intention. In the non-upstream branch all the tests have another run line which looks like
RUN: opt -newgvn %s | FileCheck %s. They're not included here just to make the diff smaller.

kariddi added a subscriber: kariddi.Dec 9 2016, 6:54 PM
kariddi added inline comments.
lib/Transforms/Scalar/NewGVN.cpp
1220

This gets cleared, but the CongruenceClasses seem to be created through "new" and stored in the vector.

Where do they get destroyed?

kariddi added inline comments.Dec 9 2016, 7:17 PM
lib/Transforms/Scalar/NewGVN.cpp
1331

You are hiding the already defined variable of the same name above. This is slightly confusing while reading the code.

Consider changing this to CurrInstRange or the one above in TotalInstRange or something like that.

Also, uhm, this needs tests. Maybe you can get away with just adding some more RUN lines to some LegacyGVN tests?

Yes, this is my intention. In the non-upstream branch all the tests have another run line which looks like
RUN: opt -newgvn %s | FileCheck %s. They're not included here just to make the diff smaller.

Might it be possible to create a new phab patch now - dependent on this one that includes the NewGVN activation and test changes? Doesn't affect this initial patch but helps everyone see the upcoming effects on tests.

davide updated this revision to Diff 81137.Dec 12 2016, 2:10 PM
davide edited edge metadata.
davide marked 23 inline comments as done.

Thank you all for the comments! New revision attached.

davide added a subscriber: hfinkel.Dec 12 2016, 2:17 PM

Thank you all for the comments! New revision attached.

@hfinkel : Hal, do you plan to review this?

include/llvm/Transforms/Scalar/GVNExpression.h
35

Done.

48

To be honest, I prefer Expression. I can change it if you feel strong about it.

63

Fixed.

84

Not sure, this doesn't seem to have a terrible compile-time impact. We can move later, if needed.

84

yeah.

156

Done.

206

Done, I think.

445

Consistently put variables at the top of the class.

lib/Transforms/Scalar/NewGVN.cpp
11

I agree. Tried to expand it a bit.

137

Michael preferred ~0U, but from what I see -1 is used everywhere else, so I'm switching back.

460

Changed, here and everywhere else in the file.

475

Probably not, removing.

1271

well, very spread in the new PM port, but I agree with you. I put an underscore at the beginning so that we can distinguish.

1573

Many other passes do the same, seems like common style.

1681

Nice catch!

1828

See comment above.

dberlin added inline comments.Dec 12 2016, 5:52 PM
lib/Transforms/Scalar/NewGVN.cpp
11

I'm happy to describe the sparse predicated algorithm a bit if you want to add it. I'll touch on the predication/etc bits when we add them.

Traditional GVN algorithms fall into two categories: Congruence partitioning and Hash based.

Hash based GVN's hash the operation performed by an instruction in some fashion, and look it up in a hash table. Anything that hashes the same and is otherwise "congruent" is considered equal. A hash based value numbering is optimistic if it is assumes that everything not in the table is congruent to everything else, and pessimistic if it is assumes everything not in the table is not congruent to everything else.

Congruence partitioning based GVN's start with every value in a single partition, and split the partition as they discover values that are not equal.

Optimistic hash based GVN and congruence partitioning GVN will discover the same set of congruences.

Most compilers nowadays use optimistic hash based approaches. The downside to optimistic hash based value numbering is that it requires reprocessing the entire routine again and again until the hashtables stops changing. This is because value dependences are not tracked well enough to know what must be reprocessed, and values can be involved in cycles (meaning there is no perfect order in which you can process the function to get a correct result). This makes these algorithms non-sparse. There are refinements to these algorithms, such as SCC based value numbering, which only requires iterating SCC's of the SSA graph, but most compilers use the hash table approach.

By contrast, the algorithm is more like the sparse conditional constant propagation algorithm, and uses a worklist of instructions to process. Dependencies between values and instructions are tracked finely enough (through the CongruenceClass structure) that when the value an operation has changes, we add the possibly dependent instructions to the worklist and keep going.

Memory locations s also value numbered by this algorithm. For memory, the goal of the algorithm is to discover the values stored at various memory locations (instead of just what loads are equivalent). Because of this loads and stores are value numbered together (while they are different expression classes, the hash ensures this occurs). MemorySSA is used to value number memory state.

To give a concrete example, given:
1 = MemoryDef(0)
store %a, %ptr

and

MemoryUse (1)
load %ptr

These will be value numbered into the same congruence class, as the memory is the same location with the same value.

This also enables the algorithm to discover equivalences that alias analysis cannot easily do.

A trivial example:

1= MemoryDef(0)
store %a, %ptr
MemoryUse(1)
load %ptr
2 = MemoryDef(1)
store %a, %ptr
MemoryUse(2)
load %ptr

These loads are equivalent, but a simple value numbering will not discover this.

The algorithm we use will discover that the stores store the same value, and thus will say that 1 and 2 are equivalent memory states.

It will then value number
MemoryUse(2)
load %ptr

as if it was
MemoryUse(1)
load %ptr

This enables the algorithm to discover fairly advanced (and even cyclic) equivalences between memory locations, much as it will do for scalars.

The algorithm used also performs unreachable code elimination/etc, similar to how sparse conditional constant propagation works. It optimistically assumes edges are unreachable until proven otherwise, and ignores unreachable values when value numbering phi nodes to create a maximal answer to value equivalence.

In addition to the above this algorithm supports forward propagation, global reassociation, and predication.

A couple more small comments as take another pass looking at the patch.

lib/Transforms/Scalar/NewGVN.cpp
843

"Don’t duplicate function or class name at the beginning of the comment."

http://llvm.org/docs/CodingStandards.html#doxygen-use-in-documentation-comments

1456

This is just implementing a lexicographical comparison, right?

If so, I would do:

return std::tie(DFSIn, DFSOut, ...) < std::tie(other.DFSIn, other.DFSOut, ...);
1465

This seems super sketchy, comparing < on pointers. What's up with that? Won't that make the result nondeterministic?

hfinkel added inline comments.Dec 13 2016, 4:00 PM
include/llvm/Transforms/Scalar/GVNExpression.h
66

Please explain in this comment why you're not comparing the expression types for loads and stores. This is also somewhat confusing because we've already compared the opcodes.

184

This looks a bit odd. Shouldn't you check the opcode equality first and then cast to BasicExpression?

lib/Transforms/Scalar/NewGVN.cpp
216

Do we want to guard this with #ifdef NDEBUG?

461

Add period after expression.

648

We also need to add operand bundles too for calls.

davide updated this revision to Diff 81338.Dec 13 2016, 6:12 PM

Another round of comments.

davide added inline comments.Dec 13 2016, 6:14 PM
lib/Transforms/Scalar/NewGVN.cpp
648

Addressed all your other comments, Hal. I put a FIXME here and I'll review it later (sorry I'm not super familiar with operator bundles and I want to add a test as well).

843

Done, did a pass over NewGVN.cpp

1456

Done, it's now much easier to understand, thanks!

hfinkel edited edge metadata.Dec 13 2016, 7:27 PM

A few more comments on the comments, otherwise, I'm fine with committing this and working on it in-tree. Thanks for your work on this!

include/llvm/Transforms/Scalar/GVNExpression.h
67

What does load coercion mean in this context? Also, for loads and stores we set the opcode to 0 and that should be noted somewhere here.

lib/Transforms/Scalar/NewGVN.cpp
649

bundle operators -> operand bundles

vsk added a subscriber: vsk.Dec 13 2016, 7:33 PM
vsk added inline comments.
include/llvm/Transforms/Scalar/GVNExpression.h
64

Could you add a brief comment explaining the significance of the ~0U, ~1U, and ~2U opcodes before using them? In particular, it would be nice to explain why we don't look at 'Other' when Opcode is in {~0,~1}.

Prazek added inline comments.Dec 14 2016, 3:06 AM
include/llvm/Transforms/Scalar/GVNExpression.h
112–115

It would be better to provide defautl values here

126–127

and remove it from here.
Also, it seems suspicious that NumOperands argument is not NumOperands, it is actually MaxOperands.

188

const auto &OE

242

const auto &OE

This revision was automatically updated to reflect the committed changes.