-
Notifications
You must be signed in to change notification settings - Fork 38
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
Validate types for select instruction #413
Conversation
Codecov Report
@@ Coverage Diff @@
## master #413 +/- ##
==========================================
+ Coverage 99.37% 99.55% +0.18%
==========================================
Files 49 49
Lines 14345 14529 +184
==========================================
+ Hits 14255 14464 +209
+ Misses 90 65 -25 |
cb886fb
to
58d9a60
Compare
7be40fc
to
abcc344
Compare
0e63773
to
e719374
Compare
012d40a
to
f0962f4
Compare
2f4487d
to
8fd62a3
Compare
325254b
to
2e7fe50
Compare
return static_cast<OperandStackType>(valType); | ||
} | ||
|
||
inline bool type_matches(OperandStackType actual_type, ValType expected_type) noexcept |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inline bool type_matches(OperandStackType actual_type, ValType expected_type) noexcept | |
inline bool operator==(OperandStackType actual_type, ValType expected_type) noexcept |
?
return static_cast<ValType>(actual_type) == expected_type; | ||
} | ||
|
||
inline bool type_matches(OperandStackType actual_type, OperandStackType expected_type) noexcept |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inline bool type_matches(OperandStackType actual_type, OperandStackType expected_type) noexcept | |
inline bool operator==(OperandStackType actual_type, OperandStackType expected_type) noexcept |
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't go with operators, because then we should probably also have operator!=
for completeness, and for all pairs OperansStackType
-OperandStackType
, OperandStackType
-ValType
, ValType
-OperandStackType
, and unit tests for all of them...
Also it doesn't seem exacly like equivalence-kind of relation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have similar doubts.
{ | ||
frame.unreachable = true; | ||
operand_stack.shrink(static_cast<size_t>(frame.parent_stack_height)); | ||
} | ||
|
||
inline void push_operand(Stack<ValType>& operand_stack, ValType type) | ||
inline void push_operand(Stack<OperandStackType>& operand_stack, ValType type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of this overload, it may be better to just allow implicit conversion ValType
-> OperandStackType
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would you do that exactly? I can imagine only making OperandStackType
a class, containing another enum type, and implicit conversion constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it looks like. Not sure this is worth it then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it looks that it would be quite verbose then.
2e7fe50
to
e1e91fd
Compare
e1e91fd
to
3bca082
Compare
3bca082
to
b14c4e0
Compare
b14c4e0
to
3ccacae
Compare
a16aa56
to
33eede1
Compare
enum class OperandStackType : uint8_t | ||
{ | ||
Unknown = 0, | ||
i32 = static_cast<uint8_t>(ValType::i32), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enum class ValType : uint8_t
so why is there a need for typecasting here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't this just extend ValType
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because ValType
is still a different type from uint8_t
, enum classes are strongly typed unlike plain enums.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't this just extend
ValType
?
No, not in C++
inline void drop_operand( | ||
const ControlFrame& frame, Stack<ValType>& operand_stack, std::optional<ValType> expected_type) | ||
inline void drop_operand(const ControlFrame& frame, Stack<OperandStackType>& operand_stack, | ||
OperandStackType expected_type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be actually easier codewise to keep using optional<ValType>
, not introducing OperandStackType
, and comparing that way?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did it, too, at some point. These two variants are quite close, but enum one is perhaps a bit more concise and closer to Validation Algorithm.
Also enum way guarantees stack operands to be 1 byte each, without "has_value" flag overhead in optional
.
(Also "Unknown" maybe better expresses the notion of "value with unknown/opaque type" than empty optional
, which is rather supposed to mean no value/no type
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still use a mixture of optional<ValType>
and OperandStackType
. Perhaps we should aim to only use OperandStackType
where possible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so, the remaining uses of optional<ValType>
are all for block types, where empty one really means "no result value" (and so for example at jumps this means nothing is kept on stack; keeping Unknown
would be incorrect)
3ccacae
to
f648368
Compare
|
||
const auto operand_type = frame_stack_height > frame.parent_stack_height ? | ||
operand_stack[0] : | ||
OperandStackType::Unknown; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we get unknown in case of unreachable?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, if unreachable and nothing is pushed before, then select
doesn't know types and will push Unknown
in the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comes from "polymorphic stack" concept in wasm. Even if unreachable, some information about instructions is validated.
Depends on #408
Some of related spec tests were already passing by accident, because the tests are not great:
https://github.com/WebAssembly/spec/blob/636b862b9c8a25ad65fb240fefd673e7f23bcdd0/test/core/select.wast#L296-L307
^ These were invalid but for the wrong reason - functions are void but have result of
select
on stack in the end.https://github.com/WebAssembly/spec/blob/636b862b9c8a25ad65fb240fefd673e7f23bcdd0/test/core/select.wast#L326-L333
^ This was also invalid for the wrong reason -
select
popped two values and pushed 0, so it was type mismatch fordrop
instruction.(added to "To Upstream")