diff --git a/build.sh b/build.sh index 64a6a49..0c7f54f 100755 --- a/build.sh +++ b/build.sh @@ -30,14 +30,15 @@ echo "$NONIMPORTS" >> $INTERMEDIATE echo "$EPILOGUE" >> $INTERMEDIATE # test file -wasmer compile -o ./output.wasmu $INTERMEDIATE +wat2wasm $INTERMEDIATE result=$? +mv intermediate.wasm output.wasm if [ 0 -ne $result ]; then - echo "Build failed, printing generated wasm:" - cat $INTERMEDIATE + echo "Build failed" + # cat $INTERMEDIATE exit $? else echo "Build succeeded, running:" - wasmer run ./output.wasmu + wasmtime ./output.wasm --wasi-modules wasi-common --tcplisten 127.0.0.1:8080 fi diff --git a/src/entrypoint.wat b/src/entrypoint.wat index eec7ee4..7e9d1c7 100644 --- a/src/entrypoint.wat +++ b/src/entrypoint.wat @@ -1,7 +1,24 @@ -(data $hello (i32.const 20) "Hello world.\0A") -(import "wasi_unstable" "proc_exit" (func $proc_exit (param i32))) +(data $hello "Hello world.\0A") + +(import "wasi_snapshot_preview1" "proc_exit" (func $proc_exit (param i32))) + (func $start (export "_start") (result i32) - i32.const 20 ;; location + (local $stringLocation i32) + + call $memory_init + + i32.const 13 + call $memory_alloc + local.set $stringLocation + + ;; copy string into memory + local.get $stringLocation ;; dest + i32.const 0 ;; index into string + i32.const 13 ;; length + memory.init $hello + + ;; print + local.get $stringLocation ;; location i32.const 13 ;; length call $print diff --git a/src/io.wat b/src/io.wat index b31f3d2..545faa7 100644 --- a/src/io.wat +++ b/src/io.wat @@ -2,26 +2,55 @@ (import "wasi_unstable" "fd_write" (func $fd_write (param $file_descriptor i32) (param $io_vectors i32) (param $io_vecnum i32) (param $num_written_ptr i32) (result i32))) (func $print (param $location i32) (param $length i32) (result i32) - ;; create struct at location 0 - i32.const 0 + (local $structLoc i32) + (local $resultLoc i32) + (local $resultCode i32) + + i32.const 8 ;; 2 x 4bytes per i32 + call $memory_alloc + local.set $structLoc + + i32.const 4 + call $memory_alloc + local.set $resultLoc + + ;; put location at offset 0 + local.get $structLoc local.get $location i32.store + ;; put length at offset 4 + local.get $structLoc i32.const 4 + i32.add local.get $length i32.store - ;; file descriptor - i32.const 1 + ;; call params + i32.const 1 ;; file file_descriptor + local.get $structLoc ;; array pointer + i32.const 1 ;; array length + local.get $resultLoc ;; result pointer - ;; iovs array pointer - i32.const 0 + call $fd_write + local.tee $resultCode - ;; iovs array length - i32.const 1 + ;; if 0 code, make sure all bytes were written + i32.eqz + if + local.get $resultLoc + local.get $length + i32.lt_u + if + i32.const 1 + return + end - ;; where to write length written - i32.const 200 + i32.const 0 + return + end - call $fd_write + ;; otherwise return code + local.get $resultCode + return ) diff --git a/src/memory.wat b/src/memory.wat new file mode 100644 index 0000000..bb589f0 --- /dev/null +++ b/src/memory.wat @@ -0,0 +1,218 @@ +(func $memory_init + (local $memorySize i32) + ;; starting location + i32.const 4 + + ;; get num pages + memory.size + + ;; shift left by 16 bits to get byte size + i32.const 16 + i32.shl + i32.const 4 + i32.sub + + ;; place start/end tags + call $memory_markBlockSize +) + +(func $memory_alloc (export "alloc") (param $requestedBytes i32) (result i32) + (local $blockAddr i32) + local.get $requestedBytes + call $memory_findFreeBlock + local.tee $blockAddr + + ;; check if we found a block + i32.eqz + if + i32.const 0 + return + end + + ;; potentially split block + local.get $blockAddr + local.get $requestedBytes + call $memory_splitFreeBlock + + ;; mark used + local.get $blockAddr + call $memory_markBlockUsed + + ;; offset boundary address to get usable pointer + local.get $blockAddr + i32.const 4 + i32.add + return +) + +;; returns address of boundary word for first free block +(func $memory_findFreeBlock (export "findFree") (param $minimumBytes i32) (result i32) + (local $memorySize i32) + (local $currentBlock i32) + + ;; get memory size in bytes + memory.size + i32.const 16 ;; convert pages to bytes + i32.shl + local.set $memorySize + + ;; start 4 bytes in so we don't ever get a 0 address + i32.const 4 + local.set $currentBlock + + block $exitBlock + loop $blockLoop + ;; make sure we're not past the end + local.get $currentBlock + local.get $memorySize + i32.ge_u + br_if $exitBlock + + ;; check if current block is used (result pushed to stack) + local.get $currentBlock + call $memory_isBlockUsed + + ;; check if current block is smaller than we need (result pushed to stack) + local.get $currentBlock + call $memory_getUsableSizeOfBlock + local.get $minimumBytes + i32.lt_u + + ;; if either is true + i32.or + if + ;; move pointer + local.get $currentBlock + i32.load ;; get size + i32.const -2 ;; mask least-significant bit + i32.and + local.get $currentBlock + i32.add + local.set $currentBlock + + ;; continue loop + br $blockLoop + end + + ;; block is free and big enough + local.get $currentBlock + return + end + end + + ;; reached the end of memory, cannot find a block + i32.const 0 + return +) + +;; checks to see if block at given address can be split into part of at least $minimumBytes +;; and splits if it is able +(func $memory_splitFreeBlock (export "splitFree") (param $boundaryTagAddress i32) (param $minimumBytes i32) + (local $desiredSize i32) + (local $oldBlockSize i32) + (local $newBlockSize i32) + + ;; desiredSize = minimumBytes + tags + local.get $minimumBytes + i32.const 8 + i32.add + local.set $desiredSize + + ;; oldBlockSize + local.get $boundaryTagAddress + i32.load + i32.const -2 + i32.and + local.set $oldBlockSize + + ;; newBlockSize (rounding up to nearest 16-byte chunk) + local.get $desiredSize + i32.const 15 + i32.add + i32.const -16 + i32.and + local.set $newBlockSize + + ;; mark new block + local.get $boundaryTagAddress + local.get $newBlockSize + call $memory_markBlockSize + + ;; mark remaining if it exists + local.get $newBlockSize + local.get $oldBlockSize + i32.lt_u + if + ;; address + local.get $boundaryTagAddress + local.get $newBlockSize + i32.add + + ;; remaining size + local.get $oldBlockSize + local.get $newBlockSize + i32.sub + + call $memory_markBlockSize + end +) + +;; marks a block as used +(func $memory_markBlockUsed (export "markUsed") (param $boundaryTagAddress i32) + (local $blockSize i32) + local.get $boundaryTagAddress + i32.load + i32.const -2 + i32.and + local.set $blockSize + + local.get $boundaryTagAddress + local.get $blockSize + i32.const 1 + i32.or + i32.store + + local.get $boundaryTagAddress + local.get $blockSize + i32.add + i32.const 4 + i32.sub + local.get $blockSize + i32.const 1 + i32.or + i32.store +) + +;; sets boundary tags for unused block +(func $memory_markBlockSize (export "markSize") (param $boundaryTagAddress i32) (param $sizeWithTags i32) + ;; put size at tag location + local.get $boundaryTagAddress + local.get $sizeWithTags + i32.store + + ;; put size at tag + size - 4 + local.get $boundaryTagAddress + local.get $sizeWithTags + i32.add + i32.const 4 + i32.sub + local.get $sizeWithTags + i32.store +) + +;; check if block at given boundary tag address is used +(func $memory_isBlockUsed (export "isUsed") (param $boundaryTagAddress i32) (result i32) + local.get $boundaryTagAddress + i32.load + i32.const 1 + i32.and +) + +(func $memory_getUsableSizeOfBlock (export "getUsableSize") (param $boundaryTagAddress i32) (result i32) + local.get $boundaryTagAddress + i32.load + i32.const -2 + i32.and + i32.const 8 + i32.sub +)