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

Validate types for select instruction #413

Merged
merged 5 commits into from
Jul 28, 2020
Merged

Validate types for select instruction #413

merged 5 commits into from
Jul 28, 2020

Conversation

gumb0
Copy link
Collaborator

@gumb0 gumb0 commented Jul 7, 2020

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

(assert_invalid
  (module (func $type-num-vs-num (select (i32.const 1) (i64.const 1) (i32.const 1))))
  "type mismatch"
)
(assert_invalid
  (module (func $type-num-vs-num (select (i32.const 1) (f32.const 1.0) (i32.const 1))))
  "type mismatch"
)
(assert_invalid
  (module (func $type-num-vs-num (select (i32.const 1) (f64.const 1.0) (i32.const 1))))
  "type mismatch"
)

^ 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

(assert_invalid
  (module
    (func $type-3rd-operand-empty
      (i32.const 0) (i32.const 0) (select) (drop)
    )
  )
  "type mismatch"
)

^ This was also invalid for the wrong reason - select popped two values and pushed 0, so it was type mismatch for drop instruction.

(added to "To Upstream")

@codecov
Copy link

codecov bot commented Jul 7, 2020

Codecov Report

Merging #413 into master will increase coverage by 0.18%.
The diff coverage is 99.48%.

@@            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     

@gumb0 gumb0 force-pushed the validate-types-select branch from cb886fb to 58d9a60 Compare July 7, 2020 16:40
@gumb0 gumb0 force-pushed the validate-types-select branch 2 times, most recently from 7be40fc to abcc344 Compare July 14, 2020 16:26
@gumb0 gumb0 changed the base branch from master to validate-types-br July 14, 2020 16:26
@gumb0 gumb0 force-pushed the validate-types-select branch 6 times, most recently from 0e63773 to e719374 Compare July 15, 2020 13:41
@gumb0 gumb0 force-pushed the validate-types-br branch 6 times, most recently from 012d40a to f0962f4 Compare July 20, 2020 18:34
@gumb0 gumb0 force-pushed the validate-types-select branch 3 times, most recently from 2f4487d to 8fd62a3 Compare July 21, 2020 10:10
@gumb0 gumb0 marked this pull request as ready for review July 21, 2020 10:37
@gumb0 gumb0 requested review from chfast and axic July 21, 2020 14:02
@gumb0 gumb0 force-pushed the validate-types-br branch from f0962f4 to b977028 Compare July 27, 2020 12:40
@gumb0 gumb0 force-pushed the validate-types-select branch from 325254b to 2e7fe50 Compare July 27, 2020 12:44
lib/fizzy/parser_expr.cpp Outdated Show resolved Hide resolved
return static_cast<OperandStackType>(valType);
}

inline bool type_matches(OperandStackType actual_type, ValType expected_type) noexcept
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
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
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
inline bool type_matches(OperandStackType actual_type, OperandStackType expected_type) noexcept
inline bool operator==(OperandStackType actual_type, OperandStackType expected_type) noexcept

?

Copy link
Collaborator Author

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.

Copy link
Collaborator

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)
Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator

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.

Copy link
Collaborator Author

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.

@gumb0 gumb0 force-pushed the validate-types-select branch from 2e7fe50 to e1e91fd Compare July 28, 2020 09:39
@gumb0 gumb0 force-pushed the validate-types-br branch from b977028 to 053b6fe Compare July 28, 2020 09:48
@gumb0 gumb0 force-pushed the validate-types-select branch from e1e91fd to 3bca082 Compare July 28, 2020 09:59
@gumb0 gumb0 force-pushed the validate-types-select branch from 3bca082 to b14c4e0 Compare July 28, 2020 10:15
@gumb0 gumb0 force-pushed the validate-types-br branch from 053b6fe to 935c588 Compare July 28, 2020 10:16
@gumb0 gumb0 force-pushed the validate-types-select branch from b14c4e0 to 3ccacae Compare July 28, 2020 10:18
@gumb0 gumb0 force-pushed the validate-types-br branch 2 times, most recently from a16aa56 to 33eede1 Compare July 28, 2020 11:35
enum class OperandStackType : uint8_t
{
Unknown = 0,
i32 = static_cast<uint8_t>(ValType::i32),
Copy link
Member

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?

Copy link
Member

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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)
Copy link
Member

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?

Copy link
Collaborator Author

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)

Copy link
Member

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?

Copy link
Collaborator Author

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)

Base automatically changed from validate-types-br to master July 28, 2020 12:07
@gumb0 gumb0 force-pushed the validate-types-select branch from 3ccacae to f648368 Compare July 28, 2020 12:15

const auto operand_type = frame_stack_height > frame.parent_stack_height ?
operand_stack[0] :
OperandStackType::Unknown;
Copy link
Member

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator

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.

@gumb0 gumb0 merged commit 9111419 into master Jul 28, 2020
@gumb0 gumb0 deleted the validate-types-select branch July 28, 2020 14:00
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.

3 participants