Skip to content

Conversation

@ggreif
Copy link
Collaborator

@ggreif ggreif commented Dec 17, 2021

Adding support for the two most popular bulk memory operations: MemoryFill and MemoryCopy. Imported test suite in a newer version that also exercises these new instructions. Added an exclusion list for tests from the new suite that would need features that winter doesn't support yet, and kept the old suite wholesale.


Stuff to do:

  • bump version? — yes: 1.1.0
  • add other bulk ops
  • in src/Wasm/Syntax/Memory.hs: type Offset = Int32 should this be Nat32?
  • check for cnt > 0 in MemoryFill, MemoryCopy
  • implement directionality in MemoryCopy
  • write tests
  • why is elem.wast failing from the MVP suite? — it doesn't seem to be related.
  • what are the test/wasm and test/wast directories for? — I have nuked them for now.

@ggreif ggreif changed the title Decode some Memory* ops Implement MemoryFill and MemoryCopy bulk memory ops Dec 17, 2021
@ggreif ggreif force-pushed the master branch 2 times, most recently from cb56cce to e3d154d Compare December 17, 2021 12:59
@ggreif
Copy link
Collaborator Author

ggreif commented Dec 17, 2021

@nomeata I am not really in the know about how close we have to follow the spec and how to test new instructions. Can you have a look at what I have now and give some guidance about how to test this?

@nomeata
Copy link
Collaborator

nomeata commented Dec 17, 2021

We can run the official test suite. Does that not include tests for these instructions?

this is shamelessly frobbed from
https://github.com/WebAssembly/testsuite/blob/main/bulk.wast

(the official test suite) but shortened to only those
two instructions
@ggreif
Copy link
Collaborator Author

ggreif commented Dec 18, 2021

I found the official tests too, but like 66c7510 they contain assert_return and other directives, and I cannot manage to run the interpreter with those in the script. E.g.:

[nix-shell:~/winter/test/wast]$ wat2wasm --enable-bulk-memory memory_copy.wast -o ../wasm/memory_copy.wast
memory_copy.wast:15:1: error: unexpected token (, expected EOF.
(invoke "test")
^

Is there a special trick, or another tool to run these?

@nomeata
Copy link
Collaborator

nomeata commented Dec 18, 2021

Yes, these are wast scripts, that's a superset of wat. There is a test runner for them included somewhere in the test suite of winter (which runs the official test suite, maybe if you set an envvar), though. I paged out most of the details.

@ggreif ggreif marked this pull request as ready for review December 19, 2021 02:10
Copy link
Collaborator

@nomeata nomeata left a comment

Choose a reason for hiding this comment

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

There are Wasm files committed; intentionally?

I assume the code was ported, where applicable, from the ocaml reference implementation? (This is desirable as it makes adding more feature in the future easier.)

@ggreif
Copy link
Collaborator Author

ggreif commented Dec 19, 2021

I have blown away all that stuff (but I could bring it back if there is a good reason for it). My hunch is that it was needed while original development. Now running nix-build will do the job. If I would feel the desire to look at the Wasm files I'd just modify the Wat2Wasm.hs to keep them.

Copy link
Collaborator

@nomeata nomeata left a comment

Choose a reason for hiding this comment

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

Ok, I must have misread the github diff, it looked as if some binary files were chnaged but not deleted.

ggreif and others added 2 commits December 20, 2021 10:57
Co-authored-by: Bas van Dijk <bas@van.dijk.ch>
@ggreif
Copy link
Collaborator Author

ggreif commented Dec 20, 2021

The elem.wast fails like this:

    /nix/store/jvzss7p21054dmbjqlbg01ya1h4nzx06-source/elem.wast:                   FAIL
      line 4
        user error (./test2194-1528.wat:13:4: error: redefinition of elem "$t"
          (elem $t (i32.const 0) $f $f)
           ^^^^
        ./test2194-1528.wat:14:4: error: redefinition of elem "$t"
          (elem $t (offset (i32.const 0)))
           ^^^^
        ./test2194-1528.wat:15:4: error: redefinition of elem "$t"
          (elem $t (offset (i32.const 0)) $f $f)
           ^^^^
        )

But I don't think this is related to my changes.


But somehow it is different! When running from origin/master:

[nix-shell:~/winter]$ env WASM_SPEC_TESTS=$(nix-build -A WASM_SPEC_TESTS) cabal test

I get

Build profile: -w ghc-8.10.7 -O1
In order, the following will be built (use -v for more details):
 - winter-1.0.0 (test:wasm-test-suite) (first run)
Preprocessing test suite 'wasm-test-suite' for winter-1.0.0..
Building test suite 'wasm-test-suite' for winter-1.0.0..
Running 1 test suites...
Test suite wasm-test-suite: RUNNING...
Test suite wasm-test-suite: PASS
Test suite logged to:
/Users/ggreif/motoko/winter-WASM/dist-newstyle/build/x86_64-osx/ghc-8.10.7/winter-1.0.0/t/wasm-test-suite/test/winter-1.0.0-wasm-test-suite.log
1 of 1 test suites (1 of 1 test cases) passed.

Mystery resolved: it is due to the --enable-bulk-memory flag. wat2wasm becomes more picky with it. 4817a69 fixes this.

@ggreif ggreif requested a review from nomeata December 20, 2021 18:20
Comment on lines +482 to +501
(MemoryFill, I32 0 : _ : I32 dst : vs') -> {-# SCC step_MemoryFill #-} do
inst <- getFrameInst
mem <- lift $ memory inst (0 @@ at)
-- Zero len with offset out-of-bounds at the end of memory is allowed
sz <- lift $ lift $ Memory.size mem
if Memory.pageSize * sz >= dst
then k vs' es
else k vs' (Trapping (memoryErrorString Memory.MemoryBoundsError) @@ at : es)

(MemoryFill, I32 cnt : v : I32 dst : vs') -> {-# SCC step_MemoryFill #-} do
inst <- getFrameInst
mem <- lift $ memory inst (0 @@ at)
let [addr, count] = fromIntegral . i64_extend_u_i32 . fromIntegral <$> [dst, cnt]
if addr + count > 2^32
then k vs' (Trapping (memoryErrorString Memory.MemoryBoundsError) @@ at : es)
else do
eres <- lift $ lift $ runExceptT $ mapM_ (\off -> Memory.storePacked Pack8 mem addr off v) [0 .. pred cnt]
case eres of
Right () -> k vs' es
Left exn -> k vs' (Trapping (memoryErrorString exn) @@ at : es)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This looks quite different from the reference code in
https://github.com/WebAssembly/spec/blob/ffb5e3b40db7a45237d4e2fc972c1552ac4abef5/interpreter/exec/eval.ml#L413-L428
(and likewise below)

The design of winter is to be a pretty much direct port, so that chances are good to get the semantics right, and also to be able to follow and apply subsequent changes easily.

We have occasionally deviated, for performance reasons (e.g. https://www.joachim-breitner.de/blog/765-Faster_Winter_7__The_Zipper). Is this the reason here? Maybe worth adding a comment here… hmm, but I see you aren’t still implementing this to byte-wise operations here.

So why not just follow the logic of the reference code directly?

Copy link
Collaborator Author

@ggreif ggreif Dec 21, 2021

Choose a reason for hiding this comment

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

Because I didn't even look at the other reference :-)

Will do.

Copy link
Collaborator Author

@ggreif ggreif Feb 13, 2023

Choose a reason for hiding this comment

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

Looking at that reference implementation makes me think it is written with proof assistants in mind. It lowers the complex instructions to simple ones and stucturally "smaller" identical ones. There is a case of MemoryCopy that isn't even tail recursive. So to make this remotely performant we'll have to rewrite these anyway. Otherwise even the Rust-style "do prefix and suffix bytewise and the inner part u64-wise" will be executing faster.

Comment on lines +22 to 23
putStrLn $ "Using wasm MVP spec test directory: " ++ testDirMVP
putStrLn $ "Using wasm spec test directory: " ++ testDir
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we need two versions of the test suite? Can we not simply run the latest, possibly excluding new tests that we don't support?

Copy link
Collaborator Author

@ggreif ggreif Dec 21, 2021

Choose a reason for hiding this comment

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

Because I had to disable too many tests that won't run in the new suite, and became concerned about regressing on those.

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