-
-
Notifications
You must be signed in to change notification settings - Fork 666
Memory
Similar to other languages that use linear memory, all data in AssemblyScript is located at a specific memory offset. There are two parts of memory that the compiler is aware of:
The compiler will leave some initial space to take care of the null
pointer, followed by static memory, if any static data segments are present.
For example, whenever a constant string or array literal is encountered while compiling a source, the compiler creates a static memory segment from it.
Note that the following implies that a runtime header is present right before. This explains why some of the following structures appear to miss a length field, which is actually computed from the runtime header.
For example
const str = "Hello";
will be compiled to an immutable global variable named str
that is initialized with the offset of the "Hello"
string in memory. Strings are encoded as UTF-16LE in AssemblyScript.
╒════════════════════ String layout (32-bit) ═══════════════════╕
3 2 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
│ .charCodeAt(0) │ .charCodeAt(1) │ N=.length 16-bit
├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ character codes
│ .charCodeAt(2) │ .charCodeAt(3) ... │
Hence, the string "Hello"
would look like this (remember, .length
is computed from the runtime header):
48 00 H
65 00 e
6C 00 l
6C 00 l
6F 00 o
When calling a JavaScript import like
declare function log(str: string): void;
log(str);
the JavaScript side receives the pointer to the string that is stored in the str
global.
All arrays store their contents in an ArrayBuffer
behind the scenes. These can be accessed naturally on typed arrays, but are private on normal arrays. Like strings, everything is stored in little endian byte order. For example in 32-bit WebAssembly:
╒══════════════════ ArrayBuffer layout (32-bit) ════════════════╕
3 2 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
│ [0] │ [1] │ [2] │ [3] │ N=.byteLength
├ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ ─ ─ ─ ┤ 8-bit bytes
│ [4] │ [5] │ [6] │ [7] ... │
Both the typed arrays and normal arrays extend an internal ArrayBufferView
class to simplify the implementation.
╒═════════════ Common ArrayBufferView layout (32-bit) ══════════╕
3 2 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
│ .data points at the backing ArrayBuffer │
├───────────────────────────────────────────────────────────────┤
│ .dataStart points at the starting position in .data │
├───────────────────────────────────────────────────────────────┤
│ .dataEnd the size in bytes from .dataStart │
└───────────────────────────────────────────────────────────────┘
Typed arrays are essentially just ArrayBufferView
s of a specific type:
╒═══════════════════ TypedArray layout (32-bit) ════════════════╕
3 2 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
│ ... ArrayBufferView ... │
└───────────────────────────────────────────────────────────────┘
Normal arrays have an additional mutable .length
property so it can grow and shrink just like one would expect:
╒════════════════════ Array<T> layout (32-bit) ═════════════════╕
3 2 1
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤
│ ... ArrayBufferView ... │
├───────────────────────────────────────────────────────────────┤
│ .length number of array elements of size sizeof<T>() │
└───────────────────────────────────────────────────────────────┘
Alignment of class members is currently performed quite similar to C/C++ structs without packing. Pointers to a class instance are guaranteed to be alligned to 16 bytes and its fields are layed out in declaration order using the alignment of the respective field's basic type. For example:
class Foo {
a: i16; // 2 bytes at offset base+0
b: Bar; // 4 bytes at offset base+4 (aligned up from base+2) in WASM32 (32-bit pointers)
c: bool; // 1 byte at offset base+8
d: f64; // 8 bytes at offset base+16 (aligned up from base+9)
}
AssemblyScript doesn't utilize the concept of a stack, like C, but instead relies exclusively on WebAssembly's execution stack. There's no stack pointer or similar.
The heap starts right after static data and is managed at runtime. Its start offset can be obtained from the HEAP_BASE
global that points right at the first 16 byte aligned offset after static memory.
For more information about the inner workings of AssemblyScript's runtime, check out the Runtime page.
Maximum allocation size is 1GB. If memory is exceeded, a trap (exception on the JavaScript side) will occur.