Skip to content

Conversation

@binji
Copy link
Member

@binji binji commented Mar 24, 2020

Lots of changes necessary to make this work, as well as some bug fixes.

The main change is allowing nan:canonical and nan:arithmetic as a
possible value for each lane of a v128. This needs to propogate
through the parser, IR, the JSON format, and the spec interpreter.

This also changes the format of the spec JSON file, where a SIMD value
is now stored as a list of values instead of a single u128:

{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}

Since the lane type can be i8 and i16, these types can now be used
in more places (not just the decompiler). They'll be used for the GC
proposal too (for packed values), so I've updated them to use the binary
value specified for that proposal.

Here are the actual SIMD fixes:

  • SIMD lanes are malformed if they don't match the binary format, but
    invalid if they are smaller than the lane width. For example,
    i8x16.extract_lane_s is malformed if the lane is >= 256, because the
    lane is stored as a byte. But it is invalid if the lane is >= 16.
  • The i8x16.narrow_i16x8_u, i16x8.narrow_i32x4_u and
    i64x2.load_32x2_u instructions were not handling sign-extension
    propoerly.

TODO: This code is pretty clumsy now; it would be better to have a
universal Value and ExpectedValue that can be used everywhere, so
the logic doesn't need to be duplicated.

Lots of changes necessary to make this work, as well as some bug fixes.

The main change is allowing `nan:canonical` and `nan:arithmetic` as a
possible value for each lane of a `v128`. This needs to propogate
through the parser, IR, the JSON format, and the spec interpreter.

This also changes the format of the spec JSON file, where a SIMD value
is now stored as a list of values instead of a single u128:

```
{"type": "v128", "lane_type": "i32", "value": ["0", "0", "0", "0"]}
```

Since the lane type can be `i8` and `i16`, these types can now be used
in more places (not just the decompiler). They'll be used for the GC
proposal too (for packed values), so I've updated them to use the binary
value specified for that proposal.

Here are the actual SIMD fixes:

* SIMD lanes are malformed if they don't match the binary format, but
  invalid if they are smaller than the lane width. For example,
  `i8x16.extract_lane_s` is malformed if the lane is >= 256, because the
  lane is stored as a byte. But it is invalid if the lane is >= 16.
* The `i8x16.narrow_i16x8_u`, `i16x8.narrow_i32x4_u` and
  `i64x2.load_32x2_u` instructions were not handling sign-extension
  propoerly.

TODO: This code is pretty clumsy now; it would be better to have a
universal `Value` and `ExpectedValue` that can be used everywhere, so
the logic doesn't need to be duplicated.
@binji binji requested a review from sbc100 March 24, 2020 02:04
break;

case ExpectedNan::Arithmetic:
WriteString("nan:arithmetic");
Copy link
Member

Choose a reason for hiding this comment

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

Unrelated: Why is this called BinaryWriter?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good question, it should be JSONWriter I suppose. I think it was historical, because the old binary-writer used to contain this stuff too, so I probably kept the name when I split it out.

case O::I32X4Load16X4S: return DoSimdLoadExtend<s32x4, s16x4>(instr, out_trap);
case O::I32X4Load16X4U: return DoSimdLoadExtend<u32x4, u16x4>(instr, out_trap);
case O::I64X2Load32X2S: return DoSimdLoadExtend<s64x2, s32x2>(instr, out_trap);
case O::I64X2Load32X2U: return DoSimdLoadExtend<s64x2, s32x2>(instr, out_trap);
Copy link
Member

Choose a reason for hiding this comment

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

Is this simply a bugfix here? Why didn't it show up before?

Copy link
Member Author

Choose a reason for hiding this comment

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

There weren't any tests of the output before, this is a relatively new instruction.

void set_f32(ExpectedNan nan) { From<float>(Type::F32, 0); nan_ = nan; }
void set_f64(ExpectedNan nan) { From<double>(Type::F64, 0); nan_ = nan; }
void set_f32(ExpectedNan nan) { set_f32(0); set_expected_nan(0, nan); }
void set_f64(ExpectedNan nan) { set_f64(0); set_expected_nan(0, nan); }
Copy link
Member

Choose a reason for hiding this comment

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

Is clang format OK with multiple statements on one line like this?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, it will expand, though I kind of prefer keeping it on a single line for readability. What do you think?

Copy link
Member

@sbc100 sbc100 Mar 24, 2020

Choose a reason for hiding this comment

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

Depends what you want the policy for wabt to be ? Absolute deferral to clang-format, or programmer discretion. I don't think there is one correct answer. It depends on the project. We don't have a lot of formatting discussions so allowing discretion isn't currently effecting productivity. So I'm OK with either way right now. We should probably document it though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, I'm thinking I'll spend some time writing up a C++ style guide finally this week.

PARSE_KEY_STRING_VALUE("value", &value_str);
EXPECT("}");
ExpectedValue expected;
CHECK_RESULT(ParseExpectedValue(&expected));
Copy link
Member

Choose a reason for hiding this comment

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

Are all the consts in the JSON expected values? What about const argument values?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, the expected values are more expressive. It's nice to be able to share the code since it's a significant amount of code. But yeah, I'll add an enum to make sure we don't parse stuff that isn't valid.

ProcessProposalDir('bulk-memory-operations', '--enable-bulk-memory')
ProcessProposalDir('reference-types', '--enable-reference-types')
ProcessProposalDir('simd', '--enable-simd')

Copy link
Member

Choose a reason for hiding this comment

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

Oops.. is this why there were so many new tests added here? I guess we were not updating the simd tests previously?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, I guess there weren't SIMD tests for a while...

binji added 4 commits March 24, 2020 12:08
* Remove private from v128 (causes gcc warning)
* Fix stack smashing in `Const` (need to use `set_expected_nan` to avoid
  writing past lane 3)
* Add `AllowExpected` enum, used to prevent parsing `nan:canonical` and
  `nan:arithmetic` for const values (i.e. invocation arguments).
`u16 * u16` has the type `int`. If the values are 65535 and 65535, then
the value can't be stored in an int (`0xfffe0001`), which makes UBSan
complain. We want wrapping behavior anyway, so cast this to a T.

Note that this isn't an issue with the other operations, since those all
fit in an `int`.
@binji binji merged commit 1f397a3 into master Mar 25, 2020
@binji binji deleted the test-spec-simd branch March 25, 2020 19:43
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