ballercat / walt

:zap: Walt is a JavaScript-like syntax for WebAssembly text format :zap:

Home Page:https://ballercat.github.io/walt/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Some issues with arrays and memory

RussCoder opened this issue · comments

Hi there!

I have tried out Walt compiler, and I have some questions.
The code, that I have written and run via Node.js

const compileWalt = require('walt-compiler').default;

const buffer = compileWalt(`
import {memory: Memory} from 'js';

let offset: i32 = 0;
//let array: i32[] = 0;

export function init(off: i32) {
    offset = off;
}

export function main(): i32 {
 
  let sum: i32 = 0;
  let i: i32;
  let array: i32[];
  array = offset;

  for(i = 0; i < 10; i+=1) {
    sum += array[i];
  }

  return sum;
}
`);

require('fs').writeFileSync('bin.wasm', new Uint8Array(buffer));

const mem = new WebAssembly.Memory({ initial: 10 });
const importObj = {
    js: {
        memory: mem
    }
};
WebAssembly.instantiate(buffer, importObj).then(({ instance }) => {
    const offset = 100;
    const array = new Uint32Array(mem.buffer, offset, 10);
    for(var i = 0; i < 10; i++) {
        array[i] = 33;
    }
    instance.exports.init(offset);
    console.log(`Main result: ${instance.exports.main()}`);
});

All the code above works as expected, but there are some issues.

  • First of all, I found out that it's impossible to declare arrays as global vars. So as you may see I declare an let array: i32[] inside the function main, and the same global declaration is commented out, since with the global declaration and without the local one, there is an error thrown by the compiler. Is it a bug? Or not implemented yet?

  • Secondly, as I see the import {memory: Memory} from 'js'; is compiled to (import "js" "memory" (memory $js.memory 1)). Ok, but what if I want to import a memory object with 10 pages? I'm not sure, maybe it will work fine with such import even if I pass a memory object with 10 pages (as I actually do in the code above) and if I work with the memory beyond 64*1024 offset, but I would like you to clarify this moment.

  • Thirdly, I found out that if I declare a counter inside the for loop, to wit for(let i: i32 = 0; ... , an error occurs. Is it a bug or it's not implemented yet? The same for ++ operator.

Also, I have seen inside the binary .wasm file and I saw that you count the address of an array element via something like that:

get_local $l2 // array initial offset
get_local $l1 // index
i32.const 4 // since it's i32 
i32.mul // index*4
i32.add // arrayOffset + index*4

It's sane, but I have a suggestion. You can substitute the multiplication with the bitwise shift, which probably will be faster, that is to say substitute arrayOffset + index*4 with arrayOffset + index << 2. What would you say?

Hey there!

First of all, thank you for reporting all of this in an issue. I rely on others using the tool and reporting inconsistencies and usage issues. Let me address the questions one by one.

Global array

This is a bug, I'll open up an issue to fix this. Should be fine to have a global array, it's simply and i32 offset in the final binary. Probably an inconsistency in the parser.

Memory imports

To allow for a custom memory import type I need to adjust the syntax for Memory definition. It'll probably be a generic type, with syntax similar to Memory<{ initial: #, max: # }>. This should work for both imports and declarations. Currently, it's only possible to declare a custom memory region with an initializer syntax const memory : Memory = { initial: #, max: # };, as you found this isn't implemented for imports and an initializer in an import statement would not really work out well/be weird.

Hasn't been a priority since there is a number of ways to work around it and a { initial: 1 } is permissive enough to work with just about any use-case, for now.

For loop declarations

Declaring variables inside a loop statement is not supported currently. Either in the loop declaration or the loop body. Not that it wouldn't be possible, it hasn't been a priority. Block level variable declarations are not natively supported by wasm(as far as I can tell), it resembles older C in this aspect. Adding such functionality would be pure syntax sugar and would take some creative aliasing of variables to emulate pseudo-blocked scoped variables. I do agree though that it's surprising for sure. I'll open up an issue to track this, but It'll be a bit of time before it's fully implemented. Unless there is a large demand for it of course.

Increment/Decrement

Honestly, both -- and ++ are more trouble than they are worth to implement in the parser. We do support += & -= however. The challenge is that adding a ++ operator, for example, means also supporting prefix and postfix notations which is a giant pain to implement.

Array offsets in binary.

Good point! I need to update some parts of the binary emitted for arrays already so I could make this change. Should be trivial. Thanks for the suggestion!

Ok, thank you for the answers.

Also, I have found out that the initialization for the global variables are required, so the code

let counter: i32;
export function count(): i32 {
    counter = 0;
    return counter;
}

causes the following error:

const { value } = node.params[0];
          ^
TypeError: Cannot destructure property `value` of 'undefined' or 'null'.

A bug? Or web assembly requires the initialization?

As for the declaration of variables inside loops, I think it's not required to make an imitation of block scoped variables, you may just make all variables function scoped, as it is in JS when you use var. It will be enough, I think, since in web assembly all variables are function scoped. But yeas, it's not an important feature for now.

Also, as I see, a semicolon at the end of each line is required (while in JS it's not). It's better to point it out in the documentation.

That's a bug. Immutable(const) globals require an initializer in WASM but not mutable ones(let). I'll patch this shortly.

Thanks again, all issues were addressed with v0.4.0