br_on_null vs br_on_non_null? #45
Description
When starting to work on this in binaryen, there was some confusion. I had some questions on the design that I don't see discussed, so opening this issue.
The spec says:
br_on_null $l : [t* (ref null ht)] -> [t* (ref ht)]
iff $l : [t*]
branches to $l on null, otherwise returns operand as non-null
That is, if the input is null we branch, sending values to the target as necessary. If it is not null we return it as not null, and leave the other values on the stack.
I can see how this makes sense, but I wonder if we've considered the opposite, br_on_non_null
? That could be
br_on_non_null $l : [(ref null ht)] -> [(ref null ht)]
iff $l : [(ref ht)]
branches to $l on non-null, otherwise returns operand
Note how this would match br_on_func, br_on_data, br_on_i31
in the GC proposal: they all get an input, check if it can be converted to something more specific, and if so send it on the branch, and otherwise leave it on the stack.
I think matching the GC proposal makes sense to do for consistency and uniformity - making all br_on_*
behave as similarly as possible.
Alternatively, we can achieve consistency by flipping the GC proposal ones, so we'd have br_on_non_func
etc. That also makes sense in a way, as it would be like ref.as_func
: return the refined value. So ref.as_*
traps if it can't be refined, and br_on_non_*
would branch in that case. (Though personally I think the smaller change, in the previous paragraph, is nicer.)
Are there code size or other concerns here that motivated the current design?