Skip to content

Remove Type ordering#3793

Merged
tlively merged 15 commits into
WebAssembly:mainfrom
tlively:remove-type-order
May 18, 2021
Merged

Remove Type ordering#3793
tlively merged 15 commits into
WebAssembly:mainfrom
tlively:remove-type-order

Conversation

@tlively

@tlively tlively commented Apr 10, 2021

Copy link
Copy Markdown
Member

As found in #3682, the current implementation of type ordering is not correct,
and although the immediate issue would be easy to fix, I don't think the current
intended comparison algorithm is correct in the first place. Rather than try to
switch to using a correct algorithm (which I am not sure I know how to
implement, although I have an idea) this PR removes Type ordering entirely. In
places that used Type ordering with std::set or std::map because they require
deterministic iteration order, this PR uses new set and map wrappers to provide
deterministic insertion-order iteration.

As found in WebAssembly#3682, the current implementation of type ordering is not correct,
and although the immediate issue would be easy to fix, I don't think the current
intended comparison algorithm is correct in the first place. Rather than try to
switch to using a correct algorithm (which I am not sure I know how to
implement, although I have an idea) this PR removes Type ordering entirely. In
places that used Type ordering with std::set or std::map because they require
deterministic iteration order, this PR uses new set and map wrappers to provide
deterministic insertion-order iteration.
@tlively tlively requested review from aheejin and kripken April 10, 2021 00:45

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice!

For fuzzing this, I'd up the frequency of the CheckDeterminism tool from 0.1 to 1.

Comment thread src/support/insertion_order.h Outdated
iterator begin() { return vec.cbegin(); }
iterator end() { return vec.cend(); }

bool empty() const { return vec.size() == 0; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
bool empty() const { return vec.size() == 0; }
bool empty() const { return vec.empty(); }

Comment thread src/support/insertion_order.h Outdated
iterator begin() { return vec.begin(); }
iterator end() { return vec.end(); }

bool empty() const { return vec.size() == 0; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
bool empty() const { return vec.size() == 0; }
bool empty() const { return vec.empty(); }

Comment thread src/wasm-stack.h Outdated
// Keeps track of the binary index of the scratch locals used to lower
// tuple.extract.
std::map<Type, Index> scratchLocals;
std::unordered_map<Type, Index> scratchLocals;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this must be sorted, we iterate on it and it affects the order of localTypes which is reflected in the binary IIANM

@tlively

tlively commented Apr 12, 2021

Copy link
Copy Markdown
Member Author

Good idea with the fuzzing. I quickly found that there was indeed a determinism issue. The parallel function analysis used a map that was deterministically ordered by non-deterministic function addresses, which ultimately caused the insertion order in collectHeapTypes to be non-deterministic. I fixed this by switching to an ordered_map in the parallel function analysis, which required using an ordered_map in a few other places as well to get the types to match.

Comment thread src/passes/Asyncify.cpp
std::map<Type, Name> map;
std::map<Name, Type> rev;
std::unordered_map<Type, Name> map;
std::unordered_map<Name, Type> rev;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How about for changes like this, we create a new uniterable_map (or better name), which is unordered_map without the ability to iterate? That would avoid the possibility of nondeterminism, which otherwise can be very annoying to find later if we miss anything in this PR.

In other words, the PR could change a map into either uniterable_map (if we never iterate on it) or ordered_map (if we do). Then we should have 0 risk.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If we had an uniterable_map available in a header, I have a hard time imagining that I would use it beyond this single PR. The tough part about getting the determinism correct here is that now getting deterministic iteration depends on having deterministic insertions, whereas previously the insertions could be non-deterministic and the iteration would still be deterministic due to sorting. An uniterable_map would not help solve that problem, unfortunately.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see, very good point...

Yes, maybe not that generally useful then. Still I think it's worthwhile, but can discuss separately if it doesn't help you here.

Comment thread src/ir/module-utils.h Outdated
};

typedef std::map<Function*, T> Map;
typedef ordered_map<Function*, T> Map;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CallGraphPropertyAnalysis is used widely so the extra overhead here worries me a little. Worth measuring the memory and speed impact of this PR.

@tlively

tlively commented Apr 24, 2021

Copy link
Copy Markdown
Member Author

I plan to update this so that not all parallel analyses are affected; only the ones that matter.

@kripken

kripken commented May 9, 2021

Copy link
Copy Markdown
Member

Btw, I noticed that we already have code for insert-ordered data structures, InsertOrderedSet and InsertOrderedMap in src/cfg/Relooper.h. If you want I can look into refactoring the relooper code for that out (although I think it might be trivial).

@tlively

tlively commented May 9, 2021

Copy link
Copy Markdown
Member Author

Oh, good to know. I'll take a look at that refactoring.

@tlively tlively requested a review from kripken May 18, 2021 19:28

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice!

lgtm, but I'd recommend fuzzing heavily before landing (and with CheckDeterminism as before)

Comment thread src/support/insert_ordered.h Outdated
return *this;
}
InsertOrderedMap(InsertOrderedMap&& other)
: Map(std::move(other.Map)), List(std::move(other.List)) {}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are move semantics guaranteed to get this right? I feel like I've read that they have, but I've never depended on that as much as this line seems to...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good question. I couldn't find any specific spec guarantees, so I removed this new constructor and assignment operator. They were left over from shots in the dark trying to fix a bug that turned out to be unrelated.

(block
(local.set $0
(i32.const -1048577)
(i32.const 8192)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This does not look right, but reading more carefully, it actually is...

@tlively tlively merged commit 92b0cbd into WebAssembly:main May 18, 2021
@tlively tlively deleted the remove-type-order branch May 18, 2021 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants