Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Comments/Questions regarding classification opcodes #183

Closed
manoskouk opened this issue Jan 20, 2021 · 6 comments
Closed

Comments/Questions regarding classification opcodes #183

manoskouk opened this issue Jan 20, 2021 · 6 comments
Assignees

Comments

@manoskouk
Copy link
Contributor

manoskouk commented Jan 20, 2021

Hello,
I have been implementing the latest version of the gc proposal in V8 and have come up with the following comments/questions:

  • While ref.cast succeeds on null, it is not clear what should happen with the ref.as_* instructions. As stated, it seems they should all fail for null, but then ref.as_func should return (ref func) over funcref == (ref null func). What is the intended behavior?
  • Similarly, br_on_func should branch with a (ref func) type.
  • The typing of br_on_* : [anyref]->[anyref] instructions needlessly loosens the type of its argument. E.g., if we pass an eqref to br_on_data and the branch is not taken, we will end up with an anyref value instead of eqref. In my opinion, those instructions should return the type of their argument.
  • The br_on_* instructions only work if the targeted label expects as result exactly the value produced by the branch instruction. This could be loosened to allow the branch type to have more values, or even no values (in which case the matched value is discarded). If both are allowed, the behavior of (br_on_t $l arg) where arg: ta. would be equivalent to (if (result ta) (ref.is_t arg) (then (ref_as_t arg) (br $l)) (else arg)).
@rossberg
Copy link
Member

Excellent suggestions! PTAL at #184 for a fix.

  • While ref.cast succeeds on null, it is not clear what should happen with the ref.as_* instructions. As stated, it seems they should all fail for null, but then ref.as_func should return (ref func) over funcref == (ref null func). What is the intended behavior?
  • Similarly, br_on_func should branch with a (ref func) type.

Absolutely! Also refined the other ref_as_* and br_on_* typings accordingly.

  • The typing of br_on_* : [anyref]->[anyref] instructions needlessly loosens the type of its argument. E.g., if we pass an eqref to br_on_data and the branch is not taken, we will end up with an anyref value instead of eqref. In my opinion, those instructions should return the type of their argument.

Agreed, changed as well.

  • The br_on_* instructions only work if the targeted label expects as result exactly the value produced by the branch instruction. This could be loosened to allow the branch type to have more values, or even no values (in which case the matched value is discarded). If both are allowed, the behavior of (br_on_t $l arg) where arg: ta. would be equivalent to (if (result ta) (ref.is_t arg) (then (ref_as_t arg) (br $l)) (else arg)).

I relaxed the typing rules to allow the label to have a supertype of the type tested for. With that, I think the equivalence you are looking for holds. I'm not sure I understand what you mean by allowing "more values" or "no values" or why that is needed, can you elaborate?

@rossberg rossberg self-assigned this Jan 21, 2021
@manoskouk
Copy link
Contributor Author

Sorry for not being clear. Let me use an example:
Are the following two expressions valid? If I am not mistaken, they are if we use the "equivalent" expression for br_on_func I mentioned above:
(block $l (result) (br_on_func $l arg))
(block $l (result i32 funcref) (i32.const 0) (br_on_func $l arg))

@rossberg
Copy link
Member

rossberg commented Jan 21, 2021

Ah, I see. No, these examples aren't valid, but the "equivalent" ones wouldn't type-check either.

For the latter example, you'd need to replace the br_on_func with something like

(if (param i32) (result i32 ta) (ref.is_func arg) (then (ref.as_func arg) (br $l)) (else arg))

That is, the exact expansion would be dependent on the label's type. That said, it would be possible to generalise br_on_* to match this, but it adds some amount of complexity -- are potential use cases sufficiently plausible to justify that?

I'm afraid the former example doesn't make sense to me, since it would require br_on_func to consume arg from the stack instead of leaving it there, in violation of its overall instruction type?

@manoskouk
Copy link
Contributor Author

I'm afraid the former example doesn't make sense to me, since it would require br_on_func to consume arg from the stack instead of leaving it there, in violation of its overall instruction type?

Right. What I meant is (block $l (result) (br_on_func $l arg) drop), i.e., the question is if it should be legal to jump to a void label.
Thanks for the clarification.

@rossberg
Copy link
Member

You mean the result type of the instruction would depend on the label? That would be pretty ad-hoc.

If the label has no parameters, then why would you want to use br_on_t in the first place? You wouldn't need the cast, so (ref.is_t) (br_if) would be sufficient in that case.

@rossberg
Copy link
Member

Fixed via #184.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants