Rename memory instructions; Rework constant handling (#177)

* Rename memory instructions as proposed by the bulk-memory-operations spec.
* Rename memory manager functions to memory.* as well
* Remove automatic inlining of constant globals (Binaryen does this now)
* Improve 'const' enum compatibility
* Improve module-level export generation
* Enable the inline decorator for constant variables
* Add ERROR, WARNING and INFO macros that emit a user-defined diagnostic
* Reintroduce builtin decorator so these can appear anywhere in stdlib again
* Inline isNaN and isFinite by default
* Make an interface around gc.* similar to memory.*
* Emit an error when trying to inline a mutable variable
* Slim down CI stages
* Add a more convenient tracing utility for debugging
* Implement some prequesites for an eventual bundled GC
This commit is contained in:
Daniel Wirtz
2018-07-20 22:53:33 +02:00
committed by GitHub
parent 34e8facfdc
commit 39b489bee2
196 changed files with 28714 additions and 6674 deletions

View File

@ -1,46 +1,24 @@
language: node_js
notifications:
email: false
before_install: npm i -g npm@latest --no-audit
install: npm config set progress=false && npm i --no-audit
before_install: npm config set progress=false && npm i -g npm@latest --no-audit
install: npm i --no-audit
cache:
directories:
- node_modules
stages:
- name: check-pr
if: type = pull_request
jobs:
include:
- stage: check-pr
node_js: lts/*
script: ./scripts/check-pr.sh
env: Checks contributing guidelines before testing pull requests
- stage: lint
node_js: node
script: npm run lint
env: Checks the sources with TSLint
- stage: test
node_js: lts/*
script: npm run clean && node bin/asc -v && npm test
env: Tests the sources on latest node.js LTS
- node_js: node
script: npm run clean && node bin/asc -v && npm test
env: Tests the sources on latest stable node.js
- node_js: lts/*
script:
- npm run clean
- cd $TRAVIS_BUILD_DIR/tests/allocators/arena && npm run build && cd .. && npm test arena
- cd $TRAVIS_BUILD_DIR/tests/allocators/buddy && npm run build && cd .. && npm test buddy
- cd $TRAVIS_BUILD_DIR/tests/allocators/tlsf && npm run build && cd .. && npm test tlsf
env: Tests the allocators on latest node.js LTS
- stage: build
node_js: lts/*
script: npm run build && node bin/asc -v && npm test
env: Builds and tests the bundle on latest node.js LTS
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then ./scripts/check-pr.sh; fi
- npm run all
- if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
cd $TRAVIS_BUILD_DIR/tests/allocators/arena && npm run build && cd .. && npm test arena &&
cd $TRAVIS_BUILD_DIR/tests/allocators/buddy && npm run build && cd .. && npm test buddy &&
cd $TRAVIS_BUILD_DIR/tests/allocators/tlsf && npm run build && cd .. && npm test tlsf;
fi
env: Runs the tests on node.js LTS
- node_js: node
script: npm run build && node bin/asc -v && npm test
env: Builds and tests the bundle on latest stable node.js
script:
- npm run all
env: Runs the tests on node.js stable

View File

@ -414,7 +414,8 @@ exports.main = function main(argv, options, callback) {
// Initialize default aliases
assemblyscript.setGlobalAlias(compilerOptions, "Math", "NativeMath");
assemblyscript.setGlobalAlias(compilerOptions, "Mathf", "NativeMathf");
assemblyscript.setGlobalAlias(compilerOptions, "abort", "~lib/env/abort"); // to disable: --use abort=
assemblyscript.setGlobalAlias(compilerOptions, "abort", "~lib/env/abort");
assemblyscript.setGlobalAlias(compilerOptions, "trace", "~lib/env/trace");
// Add or override aliases if specified
if (args.use) {

View File

@ -11,10 +11,10 @@
(global $assembly/index/w (mut i32) (i32.const 0))
(global $assembly/index/h (mut i32) (i32.const 0))
(global $assembly/index/s (mut i32) (i32.const 0))
(export "memory" (memory $0))
(export "init" (func $assembly/index/init))
(export "step" (func $assembly/index/step))
(export "fill" (func $assembly/index/fill))
(export "memory" (memory $0))
(func $assembly/index/init (; 1 ;) (type $iiv) (param $0 i32) (param $1 i32)
(local $2 i32)
;;@ assembly/index.ts:29:2

View File

@ -12,10 +12,10 @@
(global $assembly/index/h (mut i32) (i32.const 0))
(global $assembly/index/s (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 8))
(export "memory" (memory $0))
(export "init" (func $assembly/index/init))
(export "step" (func $assembly/index/step))
(export "fill" (func $assembly/index/fill))
(export "memory" (memory $0))
(func $assembly/index/init (; 1 ;) (type $iiv) (param $0 i32) (param $1 i32)
(local $2 i32)
(local $3 i32)

View File

@ -4,7 +4,8 @@
(type $iiiiv (func (param i32 i32 i32 i32)))
(global $assembly/i64/lo (mut i32) (i32.const 0))
(global $assembly/i64/hi (mut i32) (i32.const 0))
(memory $0 1)
(memory $0 0)
(export "memory" (memory $0))
(export "getLo" (func $assembly/i64/getLo))
(export "getHi" (func $assembly/i64/getHi))
(export "clz" (func $assembly/i64/clz))
@ -36,7 +37,6 @@
(export "gt_u" (func $assembly/i64/gt_u))
(export "ge_s" (func $assembly/i64/ge_s))
(export "ge_u" (func $assembly/i64/ge_u))
(export "memory" (memory $0))
(func $assembly/i64/getLo (; 0 ;) (type $i) (result i32)
;;@ assembly/i64.ts:4:9
(get_global $assembly/i64/lo)

View File

@ -7,7 +7,8 @@
(global $NaN f64 (f64.const nan:0x8000000000000))
(global $Infinity f64 (f64.const inf))
(global $HEAP_BASE i32 (i32.const 8))
(memory $0 1)
(memory $0 0)
(export "memory" (memory $0))
(export "getLo" (func $assembly/i64/getLo))
(export "getHi" (func $assembly/i64/getHi))
(export "clz" (func $assembly/i64/clz))
@ -39,7 +40,6 @@
(export "gt_u" (func $assembly/i64/gt_u))
(export "ge_s" (func $assembly/i64/ge_s))
(export "ge_u" (func $assembly/i64/ge_u))
(export "memory" (memory $0))
(func $assembly/i64/getLo (; 0 ;) (type $i) (result i32)
;;@ assembly/i64.ts:4:9
(get_global $assembly/i64/lo)

View File

@ -5,8 +5,8 @@
(import "JSMath" "log" (func $~lib/math/JSMath.log (param f64) (result f64)))
(import "JSMath" "LN2" (global $~lib/math/JSMath.LN2 f64))
(import "env" "memory" (memory $0 0))
(export "computeLine" (func $assembly/index/computeLine))
(export "memory" (memory $0))
(export "computeLine" (func $assembly/index/computeLine))
(func $assembly/index/computeLine (; 2 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
(local $4 f64)
(local $5 f64)

View File

@ -9,18 +9,18 @@
(import "env" "memory" (memory $0 0))
(global $assembly/index/NUM_COLORS i32 (i32.const 2048))
(global $HEAP_BASE i32 (i32.const 8))
(export "computeLine" (func $assembly/index/computeLine))
(export "memory" (memory $0))
(func $isFinite<f64> (; 2 ;) (type $Fi) (param $0 f64) (result i32)
;;@ ~lib/builtins.ts:22:26
(export "computeLine" (func $assembly/index/computeLine))
(func $~lib/builtins/isFinite<f64> (; 2 ;) (type $Fi) (param $0 f64) (result i32)
;;@ ~lib/builtins.ts:16:70
(f64.eq
;;@ ~lib/builtins.ts:22:9
;;@ ~lib/builtins.ts:16:53
(f64.sub
(get_local $0)
;;@ ~lib/builtins.ts:22:17
;;@ ~lib/builtins.ts:16:61
(get_local $0)
)
;;@ ~lib/builtins.ts:22:26
;;@ ~lib/builtins.ts:16:70
(f64.const 0)
)
)
@ -375,7 +375,7 @@
(set_local $17
;;@ assembly/index.ts:37:15
(if (result i32)
(call $isFinite<f64>
(call $~lib/builtins/isFinite<f64>
;;@ assembly/index.ts:37:24
(get_local $15)
)
@ -386,7 +386,7 @@
(f64.convert_s/i32
(i32.sub
;;@ assembly/index.ts:38:15
(i32.const 2048)
(get_global $assembly/index/NUM_COLORS)
;;@ assembly/index.ts:38:28
(i32.const 1)
)
@ -421,7 +421,7 @@
)
;;@ assembly/index.ts:39:8
(i32.sub
(i32.const 2048)
(get_global $assembly/index/NUM_COLORS)
;;@ assembly/index.ts:39:21
(i32.const 1)
)

View File

@ -24,9 +24,8 @@ function asmFunc(global, env, buffer) {
var $lib_allocator_arena_startOffset = 0;
var $lib_allocator_arena_offset = 0;
var assembly_index_system = 0;
var HEAP_BASE = 40;
var i64toi32_i32$HIGH_BITS = 0;
function $lib_allocator_arena_allocate_memory($0) {
function $lib_allocator_arena___memory_allocate($0) {
$0 = $0 | 0;
var $1 = 0, $2 = 0, $3 = 0, wasm2asm_i32$0 = 0, wasm2asm_i32$1 = 0, wasm2asm_i32$2 = 0;
if ($0) {
@ -55,7 +54,7 @@ function asmFunc(global, env, buffer) {
$7 = +$7;
var wasm2asm_i32$0 = 0, wasm2asm_f64$0 = 0.0;
if (($0 | 0) == (0 | 0)) {
$0 = $lib_allocator_arena_allocate_memory(56 | 0) | 0;
$0 = $lib_allocator_arena___memory_allocate(56 | 0) | 0;
wasm2asm_i32$0 = $0;
wasm2asm_f64$0 = $1;
HEAPF64[wasm2asm_i32$0 >> 3] = wasm2asm_f64$0;
@ -81,7 +80,7 @@ function asmFunc(global, env, buffer) {
return $0 | 0;
}
function $lib_memory_set_memory($0, $1, $2) {
function $lib_memory_memset($0, $1, $2) {
$0 = $0 | 0;
$1 = $1 | 0;
$2 = $2 | 0;
@ -253,12 +252,12 @@ function asmFunc(global, env, buffer) {
abort();
}
$2 = $1 << 2 | 0;
$3 = $lib_allocator_arena_allocate_memory(1 << (32 - Math_clz32($2 + 7 | 0) | 0) | 0 | 0) | 0;
$3 = $lib_allocator_arena___memory_allocate(1 << (32 - Math_clz32($2 + 7 | 0) | 0) | 0 | 0) | 0;
wasm2asm_i32$0 = $3;
wasm2asm_i32$1 = $2;
HEAP32[wasm2asm_i32$0 >> 2] = wasm2asm_i32$1;
if (($0 | 0) == (0 | 0)) {
$0 = $lib_allocator_arena_allocate_memory(8 | 0) | 0;
$0 = $lib_allocator_arena___memory_allocate(8 | 0) | 0;
wasm2asm_i32$0 = $0;
wasm2asm_i32$1 = 0;
HEAP32[wasm2asm_i32$0 >> 2] = wasm2asm_i32$1;
@ -272,7 +271,7 @@ function asmFunc(global, env, buffer) {
wasm2asm_i32$0 = $0;
wasm2asm_i32$1 = $1;
HEAP32[(wasm2asm_i32$0 + 4 | 0) >> 2] = wasm2asm_i32$1;
$lib_memory_set_memory($3 + 8 | 0 | 0, 0 | 0, $2 | 0);
$lib_memory_memset($3 + 8 | 0 | 0, 0 | 0, $2 | 0);
return $0 | 0;
}
@ -307,7 +306,7 @@ function asmFunc(global, env, buffer) {
wasm2asm_f64$0 = -$7 / 39.47841760435743;
HEAPF64[(wasm2asm_i32$0 + 40 | 0) >> 3] = wasm2asm_f64$0;
if (($0 | 0) == (0 | 0)) {
$2 = $lib_allocator_arena_allocate_memory(4 | 0) | 0;
$2 = $lib_allocator_arena___memory_allocate(4 | 0) | 0;
wasm2asm_i32$0 = $2;
wasm2asm_i32$1 = $1;
HEAP32[wasm2asm_i32$0 >> 2] = wasm2asm_i32$1;
@ -492,7 +491,7 @@ function asmFunc(global, env, buffer) {
}
function start() {
$lib_allocator_arena_startOffset = (HEAP_BASE + 7 | 0) & 4294967288 | 0;
$lib_allocator_arena_startOffset = 40;
$lib_allocator_arena_offset = $lib_allocator_arena_startOffset;
}
@ -522,10 +521,6 @@ function asmFunc(global, env, buffer) {
}
return {
init: assembly_index_init,
getBody: assembly_index_getBody,
step: assembly_index_step,
bench: assembly_index_bench,
memory: Object.create(Object.prototype, {
grow: {
value: __wasm_grow_memory
@ -536,6 +531,10 @@ function asmFunc(global, env, buffer) {
}
}
})
}),
init: assembly_index_init,
getBody: assembly_index_getBody,
step: assembly_index_step,
bench: assembly_index_bench
};
}

Binary file not shown.

View File

@ -13,16 +13,15 @@
(global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0))
(global $~lib/allocator/arena/offset (mut i32) (i32.const 0))
(global $assembly/index/system (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 40))
(memory $0 1)
(data (i32.const 8) "\0d\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s")
(export "memory" (memory $0))
(export "init" (func $assembly/index/init))
(export "getBody" (func $assembly/index/getBody))
(export "step" (func $assembly/index/step))
(export "bench" (func $assembly/index/bench))
(export "memory" (memory $0))
(start $start)
(func $~lib/allocator/arena/allocate_memory (; 1 ;) (type $ii) (param $0 i32) (result i32)
(func $~lib/allocator/arena/__memory_allocate (; 1 ;) (type $ii) (param $0 i32) (result i32)
(local $1 i32)
(local $2 i32)
(local $3 i32)
@ -116,7 +115,7 @@
(block
(f64.store
(tee_local $0
(call $~lib/allocator/arena/allocate_memory
(call $~lib/allocator/arena/__memory_allocate
(i32.const 56)
)
)
@ -150,7 +149,7 @@
)
(get_local $0)
)
(func $~lib/memory/set_memory (; 3 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32)
(func $~lib/memory/memset (; 3 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32)
(local $3 i32)
(local $4 i64)
(if
@ -500,7 +499,7 @@
)
(i32.store
(tee_local $3
(call $~lib/allocator/arena/allocate_memory
(call $~lib/allocator/arena/__memory_allocate
(i32.shl
(i32.const 1)
(i32.sub
@ -529,7 +528,7 @@
(block
(i32.store
(tee_local $0
(call $~lib/allocator/arena/allocate_memory
(call $~lib/allocator/arena/__memory_allocate
(i32.const 8)
)
)
@ -549,7 +548,7 @@
(get_local $0)
(get_local $1)
)
(call $~lib/memory/set_memory
(call $~lib/memory/memset
(i32.add
(get_local $3)
(i32.const 8)
@ -693,7 +692,7 @@
(block
(i32.store
(tee_local $2
(call $~lib/allocator/arena/allocate_memory
(call $~lib/allocator/arena/__memory_allocate
(i32.const 4)
)
)
@ -1403,13 +1402,7 @@
)
(func $start (; 12 ;) (type $v)
(set_global $~lib/allocator/arena/startOffset
(i32.and
(i32.add
(get_global $HEAP_BASE)
(i32.const 7)
)
(i32.const -8)
)
(i32.const 40)
)
(set_global $~lib/allocator/arena/offset
(get_global $~lib/allocator/arena/startOffset)

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@
(import "pson" "onBinary" (func $assembly/pson/onBinary (param i32 i32)))
(global $assembly/pson/offset (mut i32) (i32.const 0))
(memory $0 0)
(export "memory" (memory $0))
(export "onNull" (func $assembly/pson/onNull))
(export "onTrue" (func $assembly/pson/onTrue))
(export "onFalse" (func $assembly/pson/onFalse))
@ -37,7 +38,6 @@
(export "onString" (func $assembly/pson/onString))
(export "onBinary" (func $assembly/pson/onBinary))
(export "decode" (func $assembly/pson/decode))
(export "memory" (memory $0))
(func $assembly/pson/readVarint32 (; 14 ;) (type $i) (result i32)
(local $0 i32)
(local $1 i32)
@ -190,54 +190,59 @@
(block $case3|0
(block $case2|0
(block $case1|0
(block $case0|0
(block $tablify|0
(if
;;@ assembly/pson.ts:46:28
(block (result i32)
(set_global $assembly/pson/offset
(i32.add
(tee_local $1
;;@ assembly/pson.ts:46:28
(get_global $assembly/pson/offset)
)
(i32.const 1)
)
)
(br_table $case0|0 $case1|0 $case2|0 $case3|0 $case4|0 $case5|0 $case6|0 $case7|0 $case8|0 $case9|0 $case10|0 $case11|0 $case12|0 $tablify|0
(i32.sub
(tee_local $1
;;@ assembly/pson.ts:46:2
(tee_local $0
;;@ assembly/pson.ts:46:19
(i32.load8_u
(get_local $1)
)
(i32.ne
(tee_local $1
;;@ assembly/pson.ts:46:2
(tee_local $0
;;@ assembly/pson.ts:46:19
(i32.load8_u
(get_local $1)
)
)
(i32.const 240)
)
(i32.const 240)
)
)
(br_if $case14|0
(i32.or
(block
(block $tablify|0
(br_table $case1|0 $case2|0 $case3|0 $case4|0 $case5|0 $case6|0 $case7|0 $case8|0 $case9|0 $case10|0 $case11|0 $case12|0 $tablify|0
(i32.sub
(get_local $1)
(i32.const 241)
)
)
)
(br_if $case14|0
(i32.or
(i32.eq
(get_local $1)
(i32.const 253)
)
(i32.eq
(get_local $1)
(i32.const 254)
)
)
)
(br_if $case15|0
(i32.eq
(get_local $1)
;;@ assembly/pson.ts:112:9
(i32.const 253)
)
(i32.eq
(get_local $1)
;;@ assembly/pson.ts:113:9
(i32.const 254)
(i32.const 255)
)
)
(br $case16|0)
)
(br_if $case15|0
(i32.eq
(get_local $1)
;;@ assembly/pson.ts:118:9
(i32.const 255)
)
)
(br $case16|0)
)
;;@ assembly/pson.ts:51:6
(call $assembly/pson/onNull)
@ -500,7 +505,6 @@
;;@ assembly/pson.ts:125:10
(i32.gt_u
(get_local $0)
;;@ assembly/pson.ts:125:18
(i32.const 239)
)
;;@ assembly/pson.ts:125:34

View File

@ -41,6 +41,7 @@
(global $assembly/pson/Token.BINARY i32 (i32.const 255))
(global $HEAP_BASE i32 (i32.const 8))
(memory $0 0)
(export "memory" (memory $0))
(export "onNull" (func $assembly/pson/onNull))
(export "onTrue" (func $assembly/pson/onTrue))
(export "onFalse" (func $assembly/pson/onFalse))
@ -56,7 +57,6 @@
(export "onString" (func $assembly/pson/onString))
(export "onBinary" (func $assembly/pson/onBinary))
(export "decode" (func $assembly/pson/decode))
(export "memory" (memory $0))
(func $assembly/pson/readVarint32 (; 14 ;) (type $i) (result i32)
(local $0 i32)
(local $1 i32)
@ -283,112 +283,112 @@
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:50:9
(i32.const 240)
(get_global $assembly/pson/Token.NULL)
)
)
(br_if $case1|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:54:9
(i32.const 241)
(get_global $assembly/pson/Token.TRUE)
)
)
(br_if $case2|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:58:9
(i32.const 242)
(get_global $assembly/pson/Token.FALSE)
)
)
(br_if $case3|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:62:9
(i32.const 243)
(get_global $assembly/pson/Token.EOBJECT)
)
)
(br_if $case4|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:66:9
(i32.const 244)
(get_global $assembly/pson/Token.EARRAY)
)
)
(br_if $case5|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:70:9
(i32.const 245)
(get_global $assembly/pson/Token.ESTRING)
)
)
(br_if $case6|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:74:9
(i32.const 246)
(get_global $assembly/pson/Token.OBJECT)
)
)
(br_if $case7|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:82:9
(i32.const 247)
(get_global $assembly/pson/Token.ARRAY)
)
)
(br_if $case8|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:87:9
(i32.const 248)
(get_global $assembly/pson/Token.INTEGER)
)
)
(br_if $case9|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:91:9
(i32.const 249)
(get_global $assembly/pson/Token.LONG)
)
)
(br_if $case10|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:96:9
(i32.const 250)
(get_global $assembly/pson/Token.FLOAT)
)
)
(br_if $case11|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:101:9
(i32.const 251)
(get_global $assembly/pson/Token.DOUBLE)
)
)
(br_if $case12|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:106:9
(i32.const 252)
(get_global $assembly/pson/Token.STRING)
)
)
(br_if $case13|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:112:9
(i32.const 253)
(get_global $assembly/pson/Token.STRING_ADD)
)
)
(br_if $case14|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:113:9
(i32.const 254)
(get_global $assembly/pson/Token.STRING_GET)
)
)
(br_if $case15|0
(i32.eq
(get_local $0)
;;@ assembly/pson.ts:118:9
(i32.const 255)
(get_global $assembly/pson/Token.BINARY)
)
)
(br $case16|0)
@ -716,7 +716,7 @@
(i32.gt_u
(get_local $1)
;;@ assembly/pson.ts:125:18
(i32.const 239)
(get_global $assembly/pson/Token.MAX)
)
;;@ assembly/pson.ts:125:34
(unreachable)

View File

@ -245,7 +245,7 @@ class Control {
// TODO: should happen dynamically so it DCE's if all objects are unmanaged
var GC = Control.create(HEAP_BASE);
// var someObject = allocate_memory(64);
// var someObject = memory.allocate(64);
// GC.register(changetype<ObjectHeader>(someObject));
// Exported interface
@ -293,5 +293,5 @@ function gc_free_fn(control: Control, header: ObjectHeader): void {
// array, string: free their data segments
// TODO: might make sense to provide @finalize or similar
// }
free_memory(changetype<usize>(header));
memory.free(changetype<usize>(header));
}

View File

@ -39,8 +39,8 @@ export class Car {
}
dispose(): void {
free_memory(changetype<usize>(this));
memory.free(changetype<usize>(this));
}
}
export { allocate_memory, free_memory };
export { memory };

6
package-lock.json generated
View File

@ -664,9 +664,9 @@
"dev": true
},
"binaryen": {
"version": "48.0.0-nightly.20180712",
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-48.0.0-nightly.20180712.tgz",
"integrity": "sha512-E3DR6Pi2iTSqF4E93fFZrLhv0gZpSIr8MDS9R4P2Lm+/n3CGfkQvYGN20GmFwbOYNLKU68UWOXYdqRFTAkNrTA=="
"version": "49.0.0-nightly.20180718",
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-49.0.0-nightly.20180718.tgz",
"integrity": "sha512-r7gYKbwYYF5DBTN2fSomv1BNUNYSPros20eTng5MV/Bsv7V6DfJtrvietJ35p+jqI797P83vnJ0PEB1vTZ9qoQ=="
},
"bluebird": {
"version": "3.5.1",

View File

@ -12,7 +12,7 @@
},
"dependencies": {
"@protobufjs/utf8": "^1.1.0",
"binaryen": "48.0.0-nightly.20180712",
"binaryen": "49.0.0-nightly.20180718",
"long": "^4.0.0"
},
"devDependencies": {
@ -41,15 +41,15 @@
"scripts": {
"build": "webpack --mode production --display-modules",
"clean": "node scripts/clean",
"lint": "npm run lint:compiler && npm run lint:library",
"lint:compiler": "tslint -c tslint.json --project src --formatters-dir lib/lint/formatters --format as",
"lint:library": "tslint -c tslint.json --project std/assembly --formatters-dir lib/lint/formatters --format as",
"test:config": "tsc --noEmit -p src --diagnostics --listFiles",
"check": "npm run check:config && npm run check:compiler && npm run check:library",
"check:config": "tsc --noEmit -p src --diagnostics --listFiles",
"check:compiler": "tslint -c tslint.json --project src --formatters-dir lib/lint/formatters --format as",
"check:library": "tslint -c tslint.json --project std/assembly --formatters-dir lib/lint/formatters --format as",
"test": "npm run test:parser && npm run test:compiler",
"test:parser": "node tests/parser",
"test:compiler": "node tests/compiler",
"test": "npm run test:config --scripts-prepend-node-path && npm run test:parser --scripts-prepend-node-path && npm run test:compiler --scripts-prepend-node-path",
"test:pr": "npm run clean && npm test && npm run build && npm test && npm run clean",
"all": "npm run lint && npm run clean && npm test && npm run build && npm test",
"make": "npm run clean && npm test && npm run build && npm test",
"all": "npm run check && npm run make",
"docs": "typedoc --tsconfig tsconfig-docs.json --mode modules --name \"AssemblyScript Compiler API\" --out ./docs/api --ignoreCompilerErrors --excludeNotExported --excludePrivate --excludeExternals --exclude **/std/** --includeDeclarations --readme src/README.md"
},
"files": [

View File

@ -1,3 +1,3 @@
Snaps are containerised software packages that are simple to create and install on all major Linux systems without modification. [Learn more](https://docs.snapcraft.io/).
[![Snap Status](https://build.snapcraft.io/badge/AssemblyScript/assemblyscript.svg)](https://build.snapcraft.io/user/AssemblyScript/assemblyscript)
[![Snap Status](https://build.snapcraft.io/badge/AssemblyScript/assemblyscript.svg)](https://snapcraft.io/assemblyscript)

View File

@ -1122,7 +1122,8 @@ export enum DecoratorKind {
UNMANAGED,
SEALED,
INLINE,
EXTERNAL
EXTERNAL,
BUILTIN
}
/** Returns the kind of the specified decorator. Defaults to {@link DecoratorKind.CUSTOM}. */
@ -1132,6 +1133,10 @@ export function decoratorNameToKind(name: Expression): DecoratorKind {
let nameStr = (<IdentifierExpression>name).text;
assert(nameStr.length);
switch (nameStr.charCodeAt(0)) {
case CharCode.b: {
if (nameStr == "builtin") return DecoratorKind.BUILTIN;
break;
}
case CharCode.e: {
if (nameStr == "external") return DecoratorKind.EXTERNAL;
break;

View File

@ -121,8 +121,8 @@ export enum Op {
i64_store8 = 0x3c,
i64_store16 = 0x3d,
i64_store32 = 0x3e,
current_memory = 0x3f,
grow_memory = 0x40,
memory_size = 0x3f,
memory_grow = 0x40,
i32_const = 0x41,
i64_const = 0x42,
f32_const = 0x43,

View File

@ -48,13 +48,19 @@ import {
Class,
Field,
OperatorKind,
FlowFlags
FlowFlags,
Global,
DecoratorFlags
} from "./program";
import {
ReportMode
} from "./resolver";
import {
CommonFlags
} from "./common";
/** Compiles a call to a built-in function. */
export function compileCall(
compiler: Compiler,
@ -134,24 +140,17 @@ export function compileCall(
}
case "isDefined": { // isDefined(expression) -> bool
compiler.currentType = Type.bool;
if (operands.length != 1) {
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
reportNode.range, prototype.internalName
);
}
compiler.error(
DiagnosticCode.Expected_0_arguments_but_got_1,
reportNode.range, "1", operands.length.toString(10)
);
return module.createUnreachable();
}
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
reportNode.range, prototype.internalName
);
}
if (operands.length != 1) {
compiler.error(
DiagnosticCode.Expected_0_arguments_but_got_1,
reportNode.range, "1", operands.length.toString(10)
);
return module.createUnreachable();
}
let element = compiler.resolver.resolveExpression(operands[0], compiler.currentFunction, ReportMode.SWALLOW);
@ -159,24 +158,17 @@ export function compileCall(
}
case "isConstant": { // isConstant(expression) -> bool
compiler.currentType = Type.bool;
if (operands.length != 1) {
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
reportNode.range, prototype.internalName
);
}
compiler.error(
DiagnosticCode.Expected_0_arguments_but_got_1,
reportNode.range, "1", operands.length.toString(10)
);
return module.createUnreachable();
}
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
reportNode.range, prototype.internalName
);
}
if (operands.length != 1) {
compiler.error(
DiagnosticCode.Expected_0_arguments_but_got_1,
reportNode.range, "1", operands.length.toString(10)
);
return module.createUnreachable();
}
let expr = compiler.compileExpressionRetainType(operands[0], Type.i32, WrapMode.NONE);
@ -1879,7 +1871,7 @@ export function compileCall(
// host operations
case "current_memory": { // current_memory() -> i32
case "memory.size": { // memory.size() -> i32
compiler.currentType = Type.i32;
if (operands.length != 0) {
compiler.error(
@ -1895,7 +1887,7 @@ export function compileCall(
}
return module.createHost(HostOp.CurrentMemory);
}
case "grow_memory": { // grow_memory(pages: i32) -> i32
case "memory.grow": { // memory.grow(pages: i32) -> i32
compiler.currentType = Type.i32;
if (operands.length != 1) {
compiler.error(
@ -1915,7 +1907,7 @@ export function compileCall(
return module.createHost(HostOp.GrowMemory, null, [ arg0 ]);
}
// see: https://github.com/WebAssembly/bulk-memory-operations
case "move_memory": { // move_memory(dest: usize, src: usize: n: usize) -> void
case "memory.copy": { // memory.copy(dest: usize, src: usize: n: usize) -> void
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
@ -1952,7 +1944,7 @@ export function compileCall(
throw new Error("not implemented");
// return module.createHost(HostOp.MoveMemory, null, [ arg0, arg1, arg2 ]);
}
case "set_memory": { // set_memory(dest: usize, value: u8, n: usize) -> void
case "memory.fill": { // memory.fill(dest: usize, value: u8, n: usize) -> void
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
@ -2318,6 +2310,30 @@ export function compileCall(
return module.createCallIndirect(arg0, operandExprs, typeName);
}
// user-defined diagnostic macros
case "ERROR": {
compiler.error(
DiagnosticCode.User_defined_0,
reportNode.range, (operands.length ? operands[0] : reportNode).range.toString()
);
return module.createUnreachable();
}
case "WARNING": {
compiler.warning(
DiagnosticCode.User_defined_0,
reportNode.range, (operands.length ? operands[0] : reportNode).range.toString()
);
return module.createNop();
}
case "INFO": {
compiler.info(
DiagnosticCode.User_defined_0,
reportNode.range, (operands.length ? operands[0] : reportNode).range.toString()
);
return module.createNop();
}
// conversions
case "i8": {
@ -2610,6 +2626,45 @@ export function compileCall(
WrapMode.NONE
);
}
// gc
case "iterateRoots": {
if (typeArguments) {
compiler.error(
DiagnosticCode.Type_0_is_not_generic,
reportNode.range, prototype.internalName
);
}
if (operands.length != 1) {
compiler.error(
DiagnosticCode.Expected_0_arguments_but_got_1,
reportNode.range, "1", operands.length.toString(10)
);
compiler.currentType = Type.void;
return module.createUnreachable();
}
let expr = compiler.compileExpressionRetainType(operands[0], Type.u32, WrapMode.NONE);
let type = compiler.currentType;
let signatureReference = type.signatureReference;
compiler.currentType = Type.void;
if (
!type.is(TypeFlags.REFERENCE) ||
!signatureReference ||
signatureReference.parameterTypes.length != 1 ||
signatureReference.parameterTypes[0] != compiler.options.usizeType
) {
compiler.error(
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
reportNode.range, type.toString(), "(ref: usize) => void"
);
return module.createUnreachable();
}
compiler.currentType = Type.void;
// just emit a call even if the function doesn't yet exist
compiler.needsIterateRoots = true;
return module.createCall("~iterateRoots", [ expr ], NativeType.None);
}
}
var expr = deferASMCall(compiler, prototype, operands, contextualType, reportNode);
if (expr) {
@ -2816,8 +2871,6 @@ function evaluateConstantOffset(compiler: Compiler, expression: Expression): i32
return value;
}
const allocateInternalName = "allocate_memory";
/** Compiles a memory allocation for an instance of the specified class. */
export function compileAllocate(
compiler: Compiler,
@ -2828,29 +2881,15 @@ export function compileAllocate(
assert(classInstance.program == program);
var module = compiler.module;
var options = compiler.options;
var allocatePrototype = program.elementsLookup.get(allocateInternalName);
if (!allocatePrototype) {
var allocateInstance = program.memoryAllocateInstance;
if (!allocateInstance) {
program.error(
DiagnosticCode.Cannot_find_name_0,
reportNode.range, allocateInternalName
);
program.info(
DiagnosticCode.An_allocator_must_be_declared_to_allocate_memory_Try_importing_allocator_arena_or_allocator_tlsf,
reportNode.range
reportNode.range, "memory.allocate"
);
return module.createUnreachable();
}
if (allocatePrototype.kind != ElementKind.FUNCTION_PROTOTYPE) {
program.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
reportNode.range, allocatePrototype.internalName
);
return module.createUnreachable();
}
var allocateInstance = compiler.resolver.resolveFunction(<FunctionPrototype>allocatePrototype, null);
if (!(allocateInstance && compiler.compileFunction(allocateInstance))) return module.createUnreachable();
if (!compiler.compileFunction(allocateInstance)) return module.createUnreachable();
compiler.currentType = classInstance.type;
return module.createCall(
@ -2863,8 +2902,6 @@ export function compileAllocate(
);
}
const abortInternalName = "abort";
/** Compiles an abort wired to the conditionally imported 'abort' function. */
export function compileAbort(
compiler: Compiler,
@ -2874,13 +2911,10 @@ export function compileAbort(
var program = compiler.program;
var module = compiler.module;
var stringType = program.typesLookup.get("string"); // might be intended
var stringType = program.typesLookup.get("string");
if (!stringType) return module.createUnreachable();
var abortPrototype = program.elementsLookup.get(abortInternalName); // might be intended
if (!abortPrototype || abortPrototype.kind != ElementKind.FUNCTION_PROTOTYPE) return module.createUnreachable();
var abortInstance = compiler.resolver.resolveFunction(<FunctionPrototype>abortPrototype, null);
var abortInstance = program.abortInstance;
if (!(abortInstance && compiler.compileFunction(abortInstance))) return module.createUnreachable();
var messageArg = message != null
@ -2903,3 +2937,54 @@ export function compileAbort(
module.createUnreachable()
]);
}
/** Compiles the iterateRoots function if requires. */
export function compileIterateRoots(compiler: Compiler): void {
var module = compiler.module;
var exprs = new Array<ExpressionRef>();
for (let element of compiler.program.elementsLookup.values()) {
if (element.kind != ElementKind.GLOBAL) continue;
let global = <Global>element;
let classReference = global.type.classReference;
if (
global.is(CommonFlags.COMPILED) &&
classReference !== null &&
!classReference.hasDecorator(DecoratorFlags.UNMANAGED)
) {
if (global.is(CommonFlags.INLINED)) {
let value = global.constantIntegerValue;
exprs.push(
module.createCallIndirect(
module.createGetLocal(0, NativeType.I32),
[
compiler.options.isWasm64
? module.createI64(i64_low(value), i64_high(value))
: module.createI32(i64_low(value))
],
"iv"
)
);
} else {
exprs.push(
module.createCallIndirect(
module.createGetLocal(0, NativeType.I32),
[
module.createGetGlobal(
global.internalName,
compiler.options.nativeSizeType
)
],
"iv"
)
);
}
}
}
var typeRef = compiler.ensureFunctionType([ Type.i32 ], Type.void);
module.addFunction("~iterateRoots", typeRef, [],
exprs.length
? module.createBlock(null, exprs)
: module.createNop()
);
}

View File

@ -58,24 +58,22 @@ export enum CommonFlags {
// Compilation states
/** Is a builtin. */
BUILTIN = 1 << 21,
/** Is compiled. */
COMPILED = 1 << 22,
COMPILED = 1 << 21,
/** Has a constant value and is therefore inlined. */
INLINED = 1 << 23,
INLINED = 1 << 22,
/** Is scoped. */
SCOPED = 1 << 24,
SCOPED = 1 << 23,
/** Is a trampoline. */
TRAMPOLINE = 1 << 25,
TRAMPOLINE = 1 << 24,
/** Is a virtual method. */
VIRTUAL = 1 << 26,
VIRTUAL = 1 << 25,
/** Is the main function. */
MAIN = 1 << 27,
MAIN = 1 << 26,
// Other
QUOTED = 1 << 28
QUOTED = 1 << 27
}
/** Path delimiter inserted between file system levels. */

View File

@ -5,8 +5,9 @@
import {
compileCall as compileBuiltinCall,
compileAllocate as compileBuiltinAllocate,
compileAbort as compileBuiltinAbort
compileAllocate,
compileAbort,
compileIterateRoots
} from "./builtins";
import {
@ -32,7 +33,6 @@ import {
getConstValueI64High,
getConstValueF32,
getConstValueF64,
getFunctionBody,
getGetLocalIndex,
getBlockChildCount,
getBlockChild,
@ -287,6 +287,8 @@ export class Compiler extends DiagnosticEmitter {
argcVar: GlobalRef = 0;
/** Argument count helper setter. */
argcSet: FunctionRef = 0;
/** Indicates whether the iterateRoots function must be generated. */
needsIterateRoots: bool = false;
/** Compiles a {@link Program} to a {@link Module} using the specified options. */
static compile(program: Program, options: Options | null = null): Module {
@ -404,9 +406,175 @@ export class Compiler extends DiagnosticEmitter {
if (!functionTableExported) module.addTableExport("0", "table");
}
// set up module exports
for (let [name, moduleExport] of program.moduleLevelExports) {
this.makeModuleExport(name, moduleExport.element);
}
// set up gc
if (this.needsIterateRoots) compileIterateRoots(this);
return module;
}
/** Applies the respective module export(s) for the specified element. */
private makeModuleExport(name: string, element: Element, prefix: string = ""): void {
// traverse members
var members = element.members;
if (members) {
let subPrefix = prefix + name + (element.kind == ElementKind.CLASS
? INSTANCE_DELIMITER
: STATIC_DELIMITER
);
if (element.kind == ElementKind.NAMESPACE) {
for (let member of members.values()) {
if (!member.is(CommonFlags.EXPORT)) continue;
this.makeModuleExport(member.simpleName, member, subPrefix);
}
} else {
for (let member of members.values()) {
if (member.is(CommonFlags.PRIVATE)) continue;
this.makeModuleExport(member.simpleName, member, subPrefix);
}
}
}
switch (element.kind) {
// export global
case ElementKind.GLOBAL: {
let isConst = element.is(CommonFlags.CONST) || element.is(CommonFlags.STATIC | CommonFlags.READONLY);
if (!isConst && !this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
let declaration = (<Global>element).declaration;
if (declaration) {
this.error(
DiagnosticCode.Cannot_export_a_mutable_global,
declaration.name.range
);
}
} else {
this.module.addGlobalExport(element.internalName, prefix + name);
}
break;
}
case ElementKind.ENUMVALUE: {
if (!assert(element.parent).is(CommonFlags.CONST) && !this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
let declaration = (<EnumValue>element).declaration;
if (declaration) {
this.error(
DiagnosticCode.Cannot_export_a_mutable_global,
declaration.name.range
);
}
} else {
this.module.addGlobalExport(element.internalName, prefix + name);
}
break;
}
// export function
case ElementKind.FUNCTION: {
let instance = <Function>element;
let signature = instance.signature;
if (signature.requiredParameters < signature.parameterTypes.length) {
// utilize trampoline to fill in omitted arguments
instance = this.ensureTrampoline(instance);
this.ensureArgcSet();
}
if (instance.is(CommonFlags.COMPILED)) this.module.addFunctionExport(instance.internalName, prefix + name);
break;
}
// export getter and setter
case ElementKind.PROPERTY: {
let getter = assert((<Property>element).getterPrototype);
this.makeModuleExport(GETTER_PREFIX + name, getter, prefix);
let setter = (<Property>element).setterPrototype;
if (setter) this.makeModuleExport(SETTER_PREFIX + name, setter, prefix);
break;
}
// export a getter and a setter
case ElementKind.FIELD: {
let module = this.module;
let type = (<Field>element).type;
let nativeType = type.toNativeType();
let offset = (<Field>element).memoryOffset;
let usizeType = this.options.usizeType;
let nativeSizeType = this.options.nativeSizeType;
// make a getter
let getterName = prefix + GETTER_PREFIX + name;
module.addFunction(
getterName,
this.ensureFunctionType(null, type, usizeType),
null,
module.createLoad(
type.byteSize,
type.is(TypeFlags.SIGNED),
module.createGetLocal(0, nativeSizeType),
nativeType,
offset
)
);
module.addFunctionExport(getterName, getterName);
// make a setter
if (!element.is(CommonFlags.READONLY)) {
let setterName = prefix + SETTER_PREFIX + name;
module.addFunction(
setterName,
this.ensureFunctionType([ type ], Type.void, usizeType),
null,
module.createStore(
type.byteSize,
module.createGetLocal(0, nativeSizeType),
module.createGetLocal(1, nativeType),
nativeType,
offset
)
);
module.addFunctionExport(setterName, setterName);
}
break;
}
// skip prototype and export instances
case ElementKind.FUNCTION_PROTOTYPE: {
for (let instance of (<FunctionPrototype>element).instances.values()) {
let instanceName = name;
if (instance.is(CommonFlags.GENERIC)) {
let fullName = instance.internalName;
instanceName += fullName.substring(fullName.lastIndexOf("<"));
}
this.makeModuleExport(instanceName, instance, prefix);
}
break;
}
case ElementKind.CLASS_PROTOTYPE: {
for (let instance of (<ClassPrototype>element).instances.values()) {
let instanceName = name;
if (instance.is(CommonFlags.GENERIC)) {
let fullName = instance.internalName;
instanceName += fullName.substring(fullName.lastIndexOf("<"));
}
let ctor = instance.constructorInstance;
if (ctor) this.makeModuleExport(instanceName + INSTANCE_DELIMITER + ctor.simpleName, ctor, prefix);
this.makeModuleExport(instanceName, instance, prefix);
}
break;
}
// all possible members already handled above
case ElementKind.ENUM:
case ElementKind.CLASS:
case ElementKind.NAMESPACE: break;
default: assert(false);
}
}
// sources
/** Compiles a source by looking it up by path first. */
@ -565,16 +733,16 @@ export class Compiler extends DiagnosticEmitter {
}
// ambient builtins like 'HEAP_BASE' need to be resolved but are added explicitly
if (global.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) return true;
if (global.is(CommonFlags.AMBIENT) && global.hasDecorator(DecoratorFlags.BUILTIN)) return true;
var nativeType = global.type.toNativeType();
var isConstant = global.isAny(CommonFlags.CONST) || global.is(CommonFlags.STATIC | CommonFlags.READONLY);
var isDeclaredConstant = global.is(CommonFlags.CONST) || global.is(CommonFlags.STATIC | CommonFlags.READONLY);
// handle imports
if (global.is(CommonFlags.AMBIENT)) {
// constant global
if (isConstant || this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
if (isDeclaredConstant || this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
global.set(CommonFlags.MODULE_IMPORT);
if (declaration) {
mangleImportName(global, declaration, global.parent);
@ -601,49 +769,77 @@ export class Compiler extends DiagnosticEmitter {
return false;
}
// the MVP does not yet support initializer expressions other than constant values (and
// the MVP does not yet support initializer expressions other than constant values (and constant
// get_globals), hence such initializations must be performed in the start function for now.
var initializeInStart = false;
// inlined constant can be compiled as-is
if (global.is(CommonFlags.INLINED)) {
initExpr = this.compileInlineConstant(global, global.type, true);
// evaluate initializer if present
if (declaration !== null && declaration.initializer !== null) {
if (!initExpr) {
initExpr = this.compileExpression(
declaration.initializer,
global.type,
ConversionKind.IMPLICIT,
WrapMode.WRAP
);
}
} else {
// evaluate initializer if present
if (declaration && declaration.initializer) {
if (!initExpr) {
initExpr = this.compileExpression(
declaration.initializer,
global.type,
ConversionKind.IMPLICIT,
WrapMode.WRAP
);
}
// check if the initializer is constant
if (getExpressionId(initExpr) != ExpressionId.Const) {
// if a constant global, check if the initializer becomes constant after precompute
if (isConstant) {
initExpr = this.precomputeExpressionRef(initExpr);
if (getExpressionId(initExpr) != ExpressionId.Const) {
this.warning(
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
declaration.range
);
initializeInStart = true;
}
} else {
if (getExpressionId(initExpr) != ExpressionId.Const) {
if (isDeclaredConstant) {
initExpr = module.precomputeExpression(initExpr);
if (getExpressionId(initExpr) != ExpressionId.Const) {
this.warning(
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
declaration.range
);
initializeInStart = true;
}
} else {
initializeInStart = true;
}
// initialize to zero if there's no initializer
} else {
initExpr = global.type.toNativeZero(module);
}
// explicitly inline if annotated
if (global.hasDecorator(DecoratorFlags.INLINE)) {
if (!initializeInStart) { // reported above
assert(getExpressionId(initExpr) == ExpressionId.Const);
let exprType = getExpressionType(initExpr);
switch (exprType) {
case NativeType.I32: {
global.constantValueKind = ConstantValueKind.INTEGER;
global.constantIntegerValue = i64_new(getConstValueI32(initExpr), 0);
break;
}
case NativeType.I64: {
global.constantValueKind = ConstantValueKind.INTEGER;
global.constantIntegerValue = i64_new(
getConstValueI64Low(initExpr),
getConstValueI64High(initExpr)
);
break;
}
case NativeType.F32: {
global.constantValueKind = ConstantValueKind.FLOAT;
global.constantFloatValue = getConstValueF32(initExpr);
break;
}
case NativeType.F64: {
global.constantValueKind = ConstantValueKind.FLOAT;
global.constantFloatValue = getConstValueF64(initExpr);
break;
}
default: {
assert(false);
return false;
}
}
global.set(CommonFlags.INLINED); // inline the value from now on
}
}
// initialize to zero if there's no initializer
} else {
initExpr = global.type.toNativeZero(module);
}
var internalName = global.internalName;
@ -652,50 +848,8 @@ export class Compiler extends DiagnosticEmitter {
module.addGlobal(internalName, nativeType, true, global.type.toNativeZero(module));
this.startFunctionBody.push(module.createSetGlobal(internalName, initExpr));
} else { // compile as-is
if (isConstant) {
let exprType = getExpressionType(initExpr);
switch (exprType) {
case NativeType.I32: {
global.constantValueKind = ConstantValueKind.INTEGER;
global.constantIntegerValue = i64_new(getConstValueI32(initExpr), 0);
break;
}
case NativeType.I64: {
global.constantValueKind = ConstantValueKind.INTEGER;
global.constantIntegerValue = i64_new(
getConstValueI64Low(initExpr),
getConstValueI64High(initExpr)
);
break;
}
case NativeType.F32: {
global.constantValueKind = ConstantValueKind.FLOAT;
global.constantFloatValue = getConstValueF32(initExpr);
break;
}
case NativeType.F64: {
global.constantValueKind = ConstantValueKind.FLOAT;
global.constantFloatValue = getConstValueF64(initExpr);
break;
}
default: {
assert(false);
return false;
}
}
global.set(CommonFlags.INLINED); // inline the value from now on
if (global.is(CommonFlags.MODULE_EXPORT)) {
module.addGlobal(internalName, nativeType, false, initExpr);
module.addGlobalExport(internalName, mangleExportName(global));
} else if (declaration && declaration.isTopLevel) { // might become re-exported
module.addGlobal(internalName, nativeType, false, initExpr);
}
} else /* mutable */ {
module.addGlobal(internalName, nativeType, !isConstant, initExpr);
}
} else { // compile normally
module.addGlobal(internalName, nativeType, !isDeclaredConstant, initExpr);
}
return true;
}
@ -716,6 +870,7 @@ export class Compiler extends DiagnosticEmitter {
var module = this.module;
this.currentEnum = element;
var previousValue: EnumValue | null = null;
var previousValueIsMut = false;
if (element.members) {
for (let member of element.members.values()) {
@ -724,86 +879,61 @@ export class Compiler extends DiagnosticEmitter {
let val = <EnumValue>member;
let valueDeclaration = val.declaration;
val.set(CommonFlags.COMPILED);
if (val.is(CommonFlags.INLINED)) {
if (element.declaration.isTopLevelExport) {
module.addGlobal(
val.internalName,
NativeType.I32,
false, // constant
module.createI32(val.constantValue)
);
}
} else {
let initExpr: ExpressionRef;
if (valueDeclaration.value) {
initExpr = this.compileExpression(
<Expression>valueDeclaration.value,
Type.i32,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
if (getExpressionId(initExpr) != ExpressionId.Const) {
initExpr = this.precomputeExpressionRef(initExpr);
let initExpr: ExpressionRef;
if (valueDeclaration.value) {
initExpr = this.compileExpression(
<Expression>valueDeclaration.value,
Type.i32,
ConversionKind.IMPLICIT,
WrapMode.NONE
);
if (getExpressionId(initExpr) != ExpressionId.Const) {
if (element.is(CommonFlags.CONST)) {
initExpr = module.precomputeExpression(initExpr);
if (getExpressionId(initExpr) != ExpressionId.Const) {
if (element.is(CommonFlags.CONST)) {
this.warning(
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
valueDeclaration.range
);
}
this.error(
DiagnosticCode.In_const_enum_declarations_member_initializer_must_be_constant_expression,
valueDeclaration.value.range
);
initInStart = true;
}
} else {
initInStart = true;
}
} else if (previousValue == null) {
initExpr = module.createI32(0);
} else if (previousValue.is(CommonFlags.INLINED)) {
initExpr = module.createI32(previousValue.constantValue + 1);
} else {
// in TypeScript this errors with TS1061, but actually we can do:
initExpr = module.createBinary(BinaryOp.AddI32,
module.createGetGlobal(previousValue.internalName, NativeType.I32),
module.createI32(1)
}
} else if (previousValue == null) {
initExpr = module.createI32(0);
} else {
if (previousValueIsMut) {
this.error(
DiagnosticCode.Enum_member_must_have_initializer,
valueDeclaration.range
);
}
initExpr = module.createBinary(BinaryOp.AddI32,
module.createGetGlobal(previousValue.internalName, NativeType.I32),
module.createI32(1)
);
initExpr = module.precomputeExpression(initExpr);
if (getExpressionId(initExpr) != ExpressionId.Const) {
if (element.is(CommonFlags.CONST)) {
this.warning(
DiagnosticCode.Compiling_constant_with_non_constant_initializer_as_mutable,
this.error(
DiagnosticCode.In_const_enum_declarations_member_initializer_must_be_constant_expression,
valueDeclaration.range
);
}
initInStart = true;
}
if (initInStart) {
module.addGlobal(
val.internalName,
NativeType.I32,
true, // mutable
module.createI32(0)
);
this.startFunctionBody.push(module.createSetGlobal(val.internalName, initExpr));
} else {
module.addGlobal(val.internalName, NativeType.I32, false, initExpr);
if (getExpressionType(initExpr) == NativeType.I32) {
val.constantValue = getConstValueI32(initExpr);
val.set(CommonFlags.INLINED);
} else {
assert(false);
val.constantValue = 0;
}
}
}
if (initInStart) {
module.addGlobal(val.internalName, NativeType.I32, true, module.createI32(0));
this.startFunctionBody.push(module.createSetGlobal(val.internalName, initExpr));
previousValueIsMut = true;
} else {
module.addGlobal(val.internalName, NativeType.I32, !element.is(CommonFlags.CONST), initExpr);
previousValueIsMut = false;
}
previousValue = <EnumValue>val;
// export values if the enum is exported
if (element.is(CommonFlags.MODULE_EXPORT)) {
if (member.is(CommonFlags.INLINED) || this.options.hasFeature(Feature.MUTABLE_GLOBAL)) {
module.addGlobalExport(member.internalName, mangleExportName(member));
} else if (valueDeclaration) {
this.warning(
DiagnosticCode.Cannot_export_a_mutable_global,
valueDeclaration.range
);
}
}
}
}
this.currentEnum = null;
@ -850,7 +980,7 @@ export class Compiler extends DiagnosticEmitter {
}
/** Either reuses or creates the function type matching the specified signature. */
private ensureFunctionType(
ensureFunctionType(
parameterTypes: Type[] | null,
returnType: Type,
thisType: Type | null = null
@ -883,7 +1013,7 @@ export class Compiler extends DiagnosticEmitter {
/** Compiles a readily resolved function instance. */
compileFunction(instance: Function): bool {
if (instance.is(CommonFlags.COMPILED)) return true;
assert(!instance.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN));
assert(!(instance.is(CommonFlags.AMBIENT) && instance.hasDecorator(DecoratorFlags.BUILTIN)));
instance.set(CommonFlags.COMPILED);
// check that modifiers are matching
@ -1015,16 +1145,6 @@ export class Compiler extends DiagnosticEmitter {
);
}
// check module-level export
if (instance.is(CommonFlags.MODULE_EXPORT)) {
if (signature.requiredParameters < signature.parameterTypes.length) {
// export the trampoline if the function takes optional parameters
instance = this.ensureTrampoline(instance);
this.ensureArgcSet();
}
module.addFunctionExport(instance.internalName, mangleExportName(instance));
}
instance.finalize(module, ref);
return true;
}
@ -1115,6 +1235,7 @@ export class Compiler extends DiagnosticEmitter {
noTreeShaking || (<FunctionPrototype>element).is(CommonFlags.EXPORT)
) && !(<FunctionPrototype>element).is(CommonFlags.GENERIC)
) {
if (element.hasDecorator(DecoratorFlags.BUILTIN)) break;
this.compileFunctionUsingTypeArguments(
<FunctionPrototype>element,
[],
@ -1140,18 +1261,14 @@ export class Compiler extends DiagnosticEmitter {
// exports
compileExportStatement(statement: ExportStatement): void {
var module = this.module;
var fileLevelExports = this.program.fileLevelExports;
var members = statement.members;
if (!members) return; // filespace
for (let i = 0, k = members.length; i < k; ++i) {
let member = members[i];
let internalExportName = (
statement.range.source.internalPath +
PATH_DELIMITER +
member.externalName.text
let element = fileLevelExports.get(
statement.range.source.internalPath + PATH_DELIMITER + member.externalName.text
);
let element = fileLevelExports.get(internalExportName);
if (!element) continue; // reported in Program#initialize
switch (element.kind) {
case ElementKind.CLASS_PROTOTYPE: {
@ -1169,36 +1286,18 @@ export class Compiler extends DiagnosticEmitter {
!(<FunctionPrototype>element).is(CommonFlags.GENERIC) &&
statement.range.source.isEntry
) {
let functionInstance = this.compileFunctionUsingTypeArguments(
this.compileFunctionUsingTypeArguments(
<FunctionPrototype>element,
[],
null, // no contextual type arguments
null, // no outer scope
(<FunctionPrototype>element).declaration.name
);
if (functionInstance) {
let functionDeclaration = functionInstance.prototype.declaration;
if (functionDeclaration && functionDeclaration.needsExplicitExport(member)) {
module.addFunctionExport(functionInstance.internalName, member.externalName.text);
}
}
}
break;
}
case ElementKind.GLOBAL: {
if (this.compileGlobal(<Global>element) && statement.range.source.isEntry) {
let globalDeclaration = (<Global>element).declaration;
if (globalDeclaration && globalDeclaration.needsExplicitExport(member)) {
if ((<Global>element).is(CommonFlags.INLINED)) {
module.addGlobalExport(element.internalName, member.externalName.text);
} else {
this.warning(
DiagnosticCode.Cannot_export_a_mutable_global,
member.range
);
}
}
}
this.compileGlobal(<Global>element);
break;
}
case ElementKind.NAMESPACE: {
@ -1310,47 +1409,6 @@ export class Compiler extends DiagnosticEmitter {
}
case ElementKind.FIELD: {
element.set(CommonFlags.COMPILED);
if (!instance.is(CommonFlags.MODULE_EXPORT) || element.is(CommonFlags.PRIVATE)) break;
let module = this.module;
let name = (<Field>element).simpleName;
let type = (<Field>element).type;
let nativeType = type.toNativeType();
let offset = (<Field>element).memoryOffset;
let usizeType = this.options.usizeType;
let nativeSizeType = this.options.nativeSizeType;
// export an implicit getter: get:fieldName(this: usize) -> fieldType
let getterName = mangleExportName(element, GETTER_PREFIX + name);
module.addFunction(
getterName,
this.ensureFunctionType(null, type, usizeType),
null,
module.createLoad(
type.byteSize,
type.is(TypeFlags.SIGNED),
module.createGetLocal(0, nativeSizeType),
nativeType,
offset
)
);
module.addFunctionExport(getterName, getterName);
// export an implicit setter: set:fieldName(this: usize, value: fieldType) -> void
if (element.is(CommonFlags.READONLY)) break;
let setterName = mangleExportName(element, SETTER_PREFIX + name);
module.addFunction(
setterName,
this.ensureFunctionType([ type ], Type.void, usizeType),
null,
module.createStore(
type.byteSize,
module.createGetLocal(0, nativeSizeType),
module.createGetLocal(1, nativeType),
nativeType,
offset
)
);
module.addFunctionExport(setterName, setterName);
break;
}
case ElementKind.PROPERTY: {
@ -1676,7 +1734,7 @@ export class Compiler extends DiagnosticEmitter {
this.currentType
);
// check if the condition is always true
let condPre = this.precomputeExpressionRef(condExpr);
let condPre = module.precomputeExpression(condExpr);
if (getExpressionId(condPre) == ExpressionId.Const) {
assert(getExpressionType(condPre) == NativeType.I32);
if (getConstValueI32(condPre) != 0) alwaysTrue = true;
@ -1766,7 +1824,7 @@ export class Compiler extends DiagnosticEmitter {
this.currentFunction.isAny(CommonFlags.GENERIC | CommonFlags.GENERIC_CONTEXT)
) {
// Try to eliminate unnecesssary branches if the condition is constant
let condExprPrecomp = this.precomputeExpressionRef(condExpr);
let condExprPrecomp = module.precomputeExpression(condExpr);
if (
getExpressionId(condExprPrecomp) == ExpressionId.Const &&
getExpressionType(condExprPrecomp) == NativeType.I32
@ -1970,7 +2028,7 @@ export class Compiler extends DiagnosticEmitter {
flow.set(FlowFlags.RETURNS);
// TODO: requires exception-handling spec.
return compileBuiltinAbort(this, null, statement);
return compileAbort(this, null, statement);
}
compileTryStatement(statement: TryStatement): ExpressionRef {
@ -2057,7 +2115,7 @@ export class Compiler extends DiagnosticEmitter {
let isInlined = false;
if (declaration.is(CommonFlags.CONST)) {
if (initExpr) {
initExpr = this.precomputeExpressionRef(initExpr);
initExpr = this.module.precomputeExpression(initExpr);
if (getExpressionId(initExpr) == ExpressionId.Const) {
let local = new Local(program, name, -1, type);
switch (getExpressionType(initExpr)) {
@ -2162,7 +2220,7 @@ export class Compiler extends DiagnosticEmitter {
this.currentFunction.isAny(CommonFlags.GENERIC | CommonFlags.GENERIC_CONTEXT)
) {
// Try to eliminate unnecesssary loops if the condition is constant
let condExprPrecomp = this.precomputeExpressionRef(condExpr);
let condExprPrecomp = module.precomputeExpression(condExpr);
if (
getExpressionId(condExprPrecomp) == ExpressionId.Const &&
getExpressionType(condExprPrecomp) == NativeType.I32
@ -2291,7 +2349,8 @@ export class Compiler extends DiagnosticEmitter {
: this.module.createI64(0);
}
case TypeKind.F64: {
if (!(element.is(CommonFlags.BUILTIN) && contextualType == Type.f32)) {
// monkey-patch for converting built-in floats to f32 implicitly
if (!(element.hasDecorator(DecoratorFlags.BUILTIN) && contextualType == Type.f32)) {
return this.module.createF64((<VariableLikeElement>element).constantFloatValue);
}
// otherwise fall-through: basically precomputes f32.demote/f64 of NaN / Infinity
@ -2432,22 +2491,11 @@ export class Compiler extends DiagnosticEmitter {
conversionKind: ConversionKind,
wrapMode: WrapMode
): ExpressionRef {
return this.precomputeExpressionRef(
return this.module.precomputeExpression(
this.compileExpression(expression, contextualType, conversionKind, wrapMode)
);
}
precomputeExpressionRef(expr: ExpressionRef): ExpressionRef {
var module = this.module;
var type = this.currentType;
var nativeType = type.toNativeType();
var funcRef = module.addTemporaryFunction(nativeType, null, expr);
module.runPasses([ "precompute" ], funcRef);
var ret = getFunctionBody(funcRef);
module.removeTemporaryFunction();
return ret;
}
convertExpression(
expr: ExpressionRef,
fromType: Type,
@ -4927,7 +4975,7 @@ export class Compiler extends DiagnosticEmitter {
let typeArguments = expression.typeArguments;
// builtins handle present respectively omitted type arguments on their own
if (prototype.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN)) {
if (prototype.hasDecorator(DecoratorFlags.BUILTIN)) {
return this.compileCallExpressionBuiltin(prototype, expression, contextualType);
}
@ -5497,8 +5545,8 @@ export class Compiler extends DiagnosticEmitter {
names[i] = label;
}
var body = module.createBlock(names[0], [
module.createBlock("oob", [
module.createSwitch(names, "oob",
module.createBlock("outOfRange", [
module.createSwitch(names, "outOfRange",
// condition is number of provided optional arguments, so subtract required arguments
minArguments
? module.createBinary(
@ -5946,10 +5994,11 @@ export class Compiler extends DiagnosticEmitter {
switch (target.kind) {
case ElementKind.LOCAL: {
let localType = (<Local>target).type;
assert(localType != Type.void);
if ((<Local>target).is(CommonFlags.INLINED)) {
return this.compileInlineConstant(<Local>target, contextualType, retainConstantType);
}
let localType = (<Local>target).type;
let localIndex = (<Local>target).index;
assert(localIndex >= 0);
this.currentType = localType;
@ -6252,7 +6301,7 @@ export class Compiler extends DiagnosticEmitter {
? this.compileExpression(<Expression>expressions[i], elementType, ConversionKind.IMPLICIT, WrapMode.NONE)
: elementType.toNativeZero(module);
if (isStatic) {
expr = this.precomputeExpressionRef(exprs[i]);
expr = module.precomputeExpression(exprs[i]);
if (getExpressionId(expr) == ExpressionId.Const) {
assert(getExpressionType(expr) == nativeElementType);
switch (nativeElementType) {
@ -6481,7 +6530,7 @@ export class Compiler extends DiagnosticEmitter {
// allocate a new instance first and assign 'this' to the temp. local
exprs[0] = module.createSetLocal(
tempLocal.index,
compileBuiltinAllocate(this, classReference, expression)
compileAllocate(this, classReference, expression)
);
// once all field values have been set, return 'this'
@ -6534,12 +6583,13 @@ export class Compiler extends DiagnosticEmitter {
var expr: ExpressionRef;
// traverse to the first matching constructor
// traverse to the top-most visible constructor
var currentClassInstance: Class | null = classInstance;
var constructorInstance = classInstance.constructorInstance;
while (!constructorInstance && (currentClassInstance = classInstance.base)) {
var constructorInstance: Function | null = null;
do {
constructorInstance = currentClassInstance.constructorInstance;
}
if (constructorInstance) break; // TODO: check visibility
} while (currentClassInstance = currentClassInstance.base);
// if a constructor is present, call it with a zero `this`
if (constructorInstance) {
@ -6601,6 +6651,7 @@ export class Compiler extends DiagnosticEmitter {
let parent = (<EnumValue>target).parent;
assert(parent !== null && parent.kind == ElementKind.ENUM);
if (!this.compileEnum(<Enum>parent)) {
this.currentType = Type.i32;
return this.module.createUnreachable();
}
this.currentType = Type.i32;
@ -6688,7 +6739,7 @@ export class Compiler extends DiagnosticEmitter {
this.currentFunction.isAny(CommonFlags.GENERIC | CommonFlags.GENERIC_CONTEXT)
) {
// Try to eliminate unnecesssary branches if the condition is constant
let condExprPrecomp = this.precomputeExpressionRef(condExpr);
let condExprPrecomp = this.module.precomputeExpression(condExpr);
if (
getExpressionId(condExprPrecomp) == ExpressionId.Const &&
getExpressionType(condExprPrecomp) == NativeType.I32
@ -7452,7 +7503,7 @@ export class Compiler extends DiagnosticEmitter {
var initializers = new Array<ExpressionRef>();
initializers.push(
module.createSetLocal(tempLocal.index,
compileBuiltinAllocate(this, classInstance, reportNode)
compileAllocate(this, classInstance, reportNode)
)
);
@ -7536,47 +7587,6 @@ export class Compiler extends DiagnosticEmitter {
// helpers
function mangleExportName(element: Element, simpleName: string = element.simpleName): string {
switch (element.kind) {
case ElementKind.FUNCTION: {
let parent = (<Function>element).parent || (<Function>element).prototype.parent;
return parent
? mangleExportName(parent)
+ (element.is(CommonFlags.INSTANCE) ? INSTANCE_DELIMITER : STATIC_DELIMITER)
+ simpleName
: simpleName;
}
case ElementKind.FIELD: {
let parent = assert((<Field>element).parent);
return mangleExportName(parent)
+ (element.is(CommonFlags.INSTANCE) ? INSTANCE_DELIMITER : STATIC_DELIMITER)
+ simpleName;
}
case ElementKind.ENUMVALUE: {
let parent = assert((<EnumValue>element).parent);
return mangleExportName(parent)
+ (element.is(CommonFlags.INSTANCE) ? INSTANCE_DELIMITER : STATIC_DELIMITER)
+ simpleName;
}
case ElementKind.CLASS: {
let parent = (<Class>element).prototype.parent;
return parent
? mangleExportName(parent)
+ STATIC_DELIMITER
+ simpleName
: simpleName;
}
default: {
let parent = element.parent;
return parent
? mangleExportName(parent)
+ STATIC_DELIMITER
+ simpleName
: simpleName;
}
}
}
function mangleImportName(
element: Element,
declaration: DeclarationStatement,

View File

@ -834,11 +834,11 @@ export class Decompiler {
case ExpressionId.Host: {
switch (getHostOp(expr)) {
case HostOp.CurrentMemory: {
this.push("current_memory()");
this.push("memory.size()");
return;
}
case HostOp.GrowMemory: {
this.push("grow_memory(");
this.push("memory.grow(");
this.decompileExpression(getHostOperand(expr, 0));
this.push(")");
return;

View File

@ -54,7 +54,10 @@ abstract class ExportsWalker {
/** Walks all exports and calls the respective handlers. */
walk(): void {
for (let element of this.program.moduleLevelExports.values()) this.visitElement(element);
for (let moduleExport of this.program.moduleLevelExports.values()) {
// FIXME: doesn't honor the actual externally visible name
this.visitElement(moduleExport.element);
}
var todo = this.todo;
for (let i = 0; i < todo.length; ) this.visitElement(todo[i]);
}

View File

@ -9,6 +9,7 @@
export enum DiagnosticCode {
Operation_not_supported = 100,
Operation_is_unsafe = 101,
User_defined_0 = 102,
Conversion_from_type_0_to_1_requires_an_explicit_cast = 200,
Conversion_from_type_0_to_1_will_require_an_explicit_cast_when_switching_between_32_64_bit = 201,
Type_0_cannot_be_changed_to_type_1 = 202,
@ -36,7 +37,6 @@ export enum DiagnosticCode {
A_rest_parameter_must_be_last_in_a_parameter_list = 1014,
Parameter_cannot_have_question_mark_and_initializer = 1015,
A_required_parameter_cannot_follow_an_optional_parameter = 1016,
Enum_member_must_have_initializer = 1061,
Statements_are_not_allowed_in_ambient_contexts = 1036,
Initializers_are_not_allowed_in_ambient_contexts = 1039,
_0_modifier_cannot_be_used_here = 1042,
@ -45,6 +45,7 @@ export enum DiagnosticCode {
A_set_accessor_must_have_exactly_one_parameter = 1049,
A_set_accessor_parameter_cannot_have_an_initializer = 1052,
A_get_accessor_cannot_have_parameters = 1054,
Enum_member_must_have_initializer = 1061,
Type_parameters_cannot_appear_on_a_constructor_declaration = 1092,
Type_annotation_cannot_appear_on_a_constructor_declaration = 1093,
An_accessor_cannot_have_type_parameters = 1094,
@ -105,6 +106,7 @@ export enum DiagnosticCode {
Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local = 2395,
Type_0_has_no_property_1 = 2460,
The_0_operator_cannot_be_applied_to_type_1 = 2469,
In_const_enum_declarations_member_initializer_must_be_constant_expression = 2474,
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540,
The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
@ -126,6 +128,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
switch (code) {
case 100: return "Operation not supported.";
case 101: return "Operation is unsafe.";
case 102: return "User-defined: {0}";
case 200: return "Conversion from type '{0}' to '{1}' requires an explicit cast.";
case 201: return "Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit.";
case 202: return "Type '{0}' cannot be changed to type '{1}'.";
@ -153,7 +156,6 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 1014: return "A rest parameter must be last in a parameter list.";
case 1015: return "Parameter cannot have question mark and initializer.";
case 1016: return "A required parameter cannot follow an optional parameter.";
case 1061: return "Enum member must have initializer.";
case 1036: return "Statements are not allowed in ambient contexts.";
case 1039: return "Initializers are not allowed in ambient contexts.";
case 1042: return "'{0}' modifier cannot be used here.";
@ -162,6 +164,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 1049: return "A 'set' accessor must have exactly one parameter.";
case 1052: return "A 'set' accessor parameter cannot have an initializer.";
case 1054: return "A 'get' accessor cannot have parameters.";
case 1061: return "Enum member must have initializer.";
case 1092: return "Type parameters cannot appear on a constructor declaration.";
case 1093: return "Type annotation cannot appear on a constructor declaration.";
case 1094: return "An accessor cannot have type parameters.";
@ -222,6 +225,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
case 2395: return "Individual declarations in merged declaration '{0}' must be all exported or all local.";
case 2460: return "Type '{0}' has no property '{1}'.";
case 2469: return "The '{0}' operator cannot be applied to type '{1}'.";
case 2474: return "In 'const' enum declarations member initializer must be constant expression.";
case 2484: return "Export declaration conflicts with exported declaration of '{0}'.";
case 2540: return "Cannot assign to '{0}' because it is a constant or a read-only property.";
case 2541: return "The target of an assignment must be a variable or a property access.";

View File

@ -1,6 +1,7 @@
{
"Operation not supported.": 100,
"Operation is unsafe.": 101,
"User-defined: {0}": 102,
"Conversion from type '{0}' to '{1}' requires an explicit cast.": 200,
"Conversion from type '{0}' to '{1}' will require an explicit cast when switching between 32/64-bit.": 201,
"Type '{0}' cannot be changed to type '{1}'.": 202,
@ -29,7 +30,6 @@
"A rest parameter must be last in a parameter list.": 1014,
"Parameter cannot have question mark and initializer.": 1015,
"A required parameter cannot follow an optional parameter.": 1016,
"Enum member must have initializer.": 1061,
"Statements are not allowed in ambient contexts.": 1036,
"Initializers are not allowed in ambient contexts.": 1039,
"'{0}' modifier cannot be used here.": 1042,
@ -38,6 +38,7 @@
"A 'set' accessor must have exactly one parameter.": 1049,
"A 'set' accessor parameter cannot have an initializer.": 1052,
"A 'get' accessor cannot have parameters.": 1054,
"Enum member must have initializer.": 1061,
"Type parameters cannot appear on a constructor declaration.": 1092,
"Type annotation cannot appear on a constructor declaration.": 1093,
"An accessor cannot have type parameters.": 1094,
@ -99,6 +100,7 @@
"Individual declarations in merged declaration '{0}' must be all exported or all local.": 2395,
"Type '{0}' has no property '{1}'.": 2460,
"The '{0}' operator cannot be applied to type '{1}'.": 2469,
"In 'const' enum declarations member initializer must be constant expression.": 2474,
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
"Cannot assign to '{0}' because it is a constant or a read-only property.": 2540,
"The target of an assignment must be a variable or a property access.": 2541,

View File

@ -2,5 +2,10 @@
/// <reference path="../binaryen.d.ts" />
declare function allocate_memory(size: usize): usize;
declare function free_memory(ptr: usize): void;
declare namespace binaryen {
class Module {
constructor(ref: number);
emitText(): string;
emitAsmjs(): string;
}
}

View File

@ -1,42 +1,16 @@
// Copy Binaryen exports to global scope
const binaryen = global.Binaryen || require("binaryen");
global.binaryen = binaryen;
for (var key in binaryen)
if (key.startsWith("_Binaryen") || key.startsWith("_Relooper"))
global[key] = binaryen[key];
for (var key in binaryen) {
if (key.startsWith("_Binaryen") || key.startsWith("_Relooper")) global[key] = binaryen[key];
}
// Utilize Binaryen's heap
global.allocate_memory = function(size) {
if (!size) return 0; // should be safe in our case
return binaryen._malloc(size);
};
global.free_memory = function(ptr) {
if (ptr) binaryen._free(ptr);
};
global.move_memory = function(dest, src, n) {
return binaryen._memmove(dest, src, n);
};
global.store = function(ptr, val) {
binaryen.HEAPU8[ptr] = val;
};
global.load = function(ptr) {
return binaryen.HEAPU8[ptr];
};
// Implement module stubs
const Module = require("../../module").Module;
Module.prototype.toText = function() {
return new binaryen.Module(this.ref).emitText();
};
Module.prototype.toAsmjs = function() {
return new binaryen.Module(this.ref).emitAsmjs();
};
global.__memory_allocate = binaryen._malloc;
global.__memory_free = binaryen._free;
global.__memory_copy = binaryen._memmove;
global.__store = function(ptr, val) { binaryen.HEAPU8[ptr] = val; };
global.__load = function(ptr) { return binaryen.HEAPU8[ptr]; };

View File

@ -6,7 +6,17 @@
/// <reference path="./node.d.ts" />
import "./binaryen"; // must be first so portable can pick up the memory implementation
import "../../../std/portable/index";
import "./binaryen";
import "./float";
import "./i64";
import { Module } from "../../module";
Module.prototype.toText = function(this: Module) {
return new binaryen.Module(this.ref).emitText();
};
Module.prototype.toAsmjs = function(this: Module) {
return new binaryen.Module(this.ref).emitAsmjs();
};

View File

@ -251,7 +251,7 @@ export class Module {
static create(): Module {
var module = new Module();
module.ref = _BinaryenModuleCreate();
module.cachedByValue = allocate_memory(16);
module.cachedByValue = memory.allocate(16);
return module;
}
@ -260,10 +260,10 @@ export class Module {
try {
let module = new Module();
module.ref = _BinaryenModuleRead(cArr, buffer.length);
module.cachedByValue = allocate_memory(3 * 8); // LLVM C-ABI, max used is 3 * usize
module.cachedByValue = memory.allocate(3 * 8); // LLVM C-ABI, max used is 3 * usize
return module;
} finally {
free_memory(changetype<usize>(cArr));
memory.free(changetype<usize>(cArr));
}
}
@ -281,8 +281,8 @@ export class Module {
try {
return _BinaryenAddFunctionType(this.ref, cStr, result, cArr, paramTypes ? paramTypes.length : 0);
} finally {
free_memory(cArr);
free_memory(cStr);
memory.free(cArr);
memory.free(cStr);
}
}
@ -294,7 +294,7 @@ export class Module {
try {
return _BinaryenGetFunctionTypeBySignature(this.ref, result, cArr, paramTypes ? paramTypes.length : 0);
} finally {
free_memory(cArr);
memory.free(cArr);
}
}
@ -303,7 +303,7 @@ export class Module {
try {
_BinaryenRemoveFunctionType(this.ref, cStr);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -360,8 +360,8 @@ export class Module {
try {
return _BinaryenHost(this.ref, op, cStr, cArr, operands ? (<ExpressionRef[]>operands).length : 0);
} finally {
free_memory(cArr);
free_memory(cStr);
memory.free(cArr);
memory.free(cStr);
}
}
@ -387,7 +387,7 @@ export class Module {
try {
return _BinaryenGetGlobal(this.ref, cStr, type);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -485,7 +485,7 @@ export class Module {
try {
return _BinaryenSetGlobal(this.ref, cStr, value);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -499,8 +499,8 @@ export class Module {
try {
return _BinaryenBlock(this.ref, cStr, cArr, children.length, type);
} finally {
free_memory(cArr);
free_memory(cStr);
memory.free(cArr);
memory.free(cStr);
}
}
@ -513,7 +513,7 @@ export class Module {
try {
return _BinaryenBreak(this.ref, cStr, condition, value);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -531,7 +531,7 @@ export class Module {
try {
return _BinaryenLoop(this.ref, cStr, body);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -577,9 +577,9 @@ export class Module {
try {
return _BinaryenSwitch(this.ref, cArr, numNames, cStr, condition, value);
} finally {
free_memory(cStr);
free_memory(cArr);
for (let i = numNames - 1; i >= 0; --i) free_memory(strs[i]);
memory.free(cStr);
memory.free(cArr);
for (let i = numNames - 1; i >= 0; --i) memory.free(strs[i]);
}
}
@ -593,8 +593,8 @@ export class Module {
try {
return _BinaryenCall(this.ref, cStr, cArr, operands && operands.length || 0, returnType);
} finally {
free_memory(cArr);
free_memory(cStr);
memory.free(cArr);
memory.free(cStr);
}
}
@ -608,8 +608,8 @@ export class Module {
try {
return _BinaryenCallImport(this.ref, cStr, cArr, operands && operands.length || 0, returnType);
} finally {
free_memory(cArr);
free_memory(cStr);
memory.free(cArr);
memory.free(cStr);
}
}
@ -623,8 +623,8 @@ export class Module {
try {
return _BinaryenCallIndirect(this.ref, index, cArr, operands && operands.length || 0, cStr);
} finally {
free_memory(cStr);
free_memory(cArr);
memory.free(cStr);
memory.free(cArr);
}
}
@ -644,7 +644,7 @@ export class Module {
try {
return _BinaryenAddGlobal(this.ref, cStr, type, mutable ? 1 : 0, initializer);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -659,8 +659,8 @@ export class Module {
try {
return _BinaryenAddFunction(this.ref, cStr, type, cArr, varTypes ? varTypes.length : 0, body);
} finally {
free_memory(cArr);
free_memory(cStr);
memory.free(cArr);
memory.free(cStr);
}
}
@ -669,7 +669,7 @@ export class Module {
try {
_BinaryenRemoveFunction(this.ref, cStr);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -685,7 +685,7 @@ export class Module {
let typeRef = _BinaryenAddFunctionType(this.ref, tempName, result, cArr, paramTypes ? paramTypes.length : 0);
return _BinaryenAddFunction(this.ref, tempName, typeRef, 0, 0, body);
} finally {
free_memory(cArr);
memory.free(cArr);
}
}
@ -705,8 +705,8 @@ export class Module {
try {
return _BinaryenAddFunctionExport(this.ref, cStr1, cStr2);
} finally {
free_memory(cStr2);
free_memory(cStr1);
memory.free(cStr2);
memory.free(cStr1);
}
}
@ -719,8 +719,8 @@ export class Module {
try {
return _BinaryenAddTableExport(this.ref, cStr1, cStr2);
} finally {
free_memory(cStr2);
free_memory(cStr1);
memory.free(cStr2);
memory.free(cStr1);
}
}
@ -733,8 +733,8 @@ export class Module {
try {
return _BinaryenAddMemoryExport(this.ref, cStr1, cStr2);
} finally {
free_memory(cStr2);
free_memory(cStr1);
memory.free(cStr2);
memory.free(cStr1);
}
}
@ -747,8 +747,8 @@ export class Module {
try {
return _BinaryenAddGlobalExport(this.ref, cStr1, cStr2);
} finally {
free_memory(cStr2);
free_memory(cStr1);
memory.free(cStr2);
memory.free(cStr1);
}
}
@ -757,7 +757,7 @@ export class Module {
try {
_BinaryenRemoveExport(this.ref, cStr);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -773,9 +773,9 @@ export class Module {
try {
return _BinaryenAddFunctionImport(this.ref, cStr1, cStr2, cStr3, functionType);
} finally {
free_memory(cStr3);
free_memory(cStr2);
free_memory(cStr1);
memory.free(cStr3);
memory.free(cStr2);
memory.free(cStr1);
}
}
@ -790,9 +790,9 @@ export class Module {
try {
return _BinaryenAddTableImport(this.ref, cStr1, cStr2, cStr3);
} finally {
free_memory(cStr3);
free_memory(cStr2);
free_memory(cStr1);
memory.free(cStr3);
memory.free(cStr2);
memory.free(cStr1);
}
}
@ -807,9 +807,9 @@ export class Module {
try {
return _BinaryenAddMemoryImport(this.ref, cStr1, cStr2, cStr3);
} finally {
free_memory(cStr3);
free_memory(cStr2);
free_memory(cStr1);
memory.free(cStr3);
memory.free(cStr2);
memory.free(cStr1);
}
}
@ -825,9 +825,9 @@ export class Module {
try {
return _BinaryenAddGlobalImport(this.ref, cStr1, cStr2, cStr3, globalType);
} finally {
free_memory(cStr3);
free_memory(cStr2);
free_memory(cStr1);
memory.free(cStr3);
memory.free(cStr2);
memory.free(cStr1);
}
}
@ -836,7 +836,7 @@ export class Module {
try {
_BinaryenRemoveImport(this.ref, cStr);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -867,11 +867,11 @@ export class Module {
try {
_BinaryenSetMemory(this.ref, initial, maximum, cStr, cArr1, cArr2, cArr3, k);
} finally {
free_memory(cArr3);
free_memory(cArr2);
free_memory(cArr1);
for (let i = k - 1; i >= 0; --i) free_memory(segs[i]);
free_memory(cStr);
memory.free(cArr3);
memory.free(cArr2);
memory.free(cArr1);
for (let i = k - 1; i >= 0; --i) memory.free(segs[i]);
memory.free(cStr);
}
}
@ -880,7 +880,7 @@ export class Module {
try {
_BinaryenSetFunctionTable(this.ref, cArr, funcs.length);
} finally {
free_memory(cArr);
memory.free(cArr);
}
}
@ -888,10 +888,18 @@ export class Module {
_BinaryenSetStart(this.ref, func);
}
getOptimizeLevel(): i32 {
return _BinaryenGetOptimizeLevel();
}
setOptimizeLevel(level: i32 = 2): void {
_BinaryenSetOptimizeLevel(level);
}
getShrinkLevel(): i32 {
return _BinaryenGetShrinkLevel();
}
setShrinkLevel(level: i32 = 1): void {
_BinaryenSetShrinkLevel(level);
}
@ -922,17 +930,26 @@ export class Module {
_BinaryenModuleRunPasses(this.ref, cArr, numNames);
}
} finally {
free_memory(cArr);
for (let i = numNames; i >= 0; --i) {
free_memory(names[i]);
}
memory.free(cArr);
for (let i = numNames; i >= 0; --i) memory.free(names[i]);
}
}
private cachedPrecomputeName: usize = 0;
private cachedPrecomputeName: usize = 0; // for free'ing
private cachedPrecomputeNames: usize = 0;
precomputeFunction(func: FunctionRef): void {
precomputeExpression(expr: ExpressionRef): ExpressionRef {
// remember the previous optimize levels and set to max instead, to be sure
var previousOptimizeLevel = _BinaryenGetOptimizeLevel();
var previousShrinkLevel = _BinaryenGetShrinkLevel();
var previousDebugInfo = _BinaryenGetDebugInfo();
_BinaryenSetOptimizeLevel(4);
_BinaryenSetShrinkLevel(0);
_BinaryenSetDebugInfo(false);
// wrap the expression in a temp. function and run the precompute pass on it
var type = _BinaryenExpressionGetType(expr);
var func = this.addTemporaryFunction(type, null, expr);
var names = this.cachedPrecomputeNames;
if (!names) {
let name = allocString("precompute");
@ -940,6 +957,14 @@ export class Module {
this.cachedPrecomputeNames = names = allocI32Array([ name ]);
}
_BinaryenFunctionRunPasses(func, this.ref, names, 1);
expr = _BinaryenFunctionGetBody(func);
this.removeTemporaryFunction();
// reset optimize levels to previous
_BinaryenSetOptimizeLevel(previousOptimizeLevel);
_BinaryenSetShrinkLevel(previousShrinkLevel);
_BinaryenSetDebugInfo(previousDebugInfo);
return expr;
}
validate(): bool {
@ -965,9 +990,9 @@ export class Module {
ret.sourceMap = readString(sourceMapPtr);
return ret;
} finally {
if (cStr) free_memory(cStr);
if (binaryPtr) free_memory(binaryPtr);
if (sourceMapPtr) free_memory(sourceMapPtr);
if (cStr) memory.free(cStr);
if (binaryPtr) memory.free(binaryPtr);
if (sourceMapPtr) memory.free(sourceMapPtr);
}
}
@ -981,10 +1006,10 @@ export class Module {
dispose(): void {
assert(this.ref);
free_memory(this.cachedByValue);
free_memory(this.cachedTemporaryName);
free_memory(this.cachedPrecomputeName);
free_memory(this.cachedPrecomputeNames);
memory.free(this.cachedByValue);
memory.free(this.cachedTemporaryName);
memory.free(this.cachedPrecomputeName);
memory.free(this.cachedPrecomputeNames);
_BinaryenModuleDispose(this.ref);
this.ref = 0;
}
@ -1086,7 +1111,7 @@ export class Module {
try {
return _BinaryenModuleAddDebugInfoFileName(this.ref, cStr);
} finally {
free_memory(cStr);
memory.free(cStr);
}
}
@ -1336,7 +1361,7 @@ export class Relooper {
try {
_RelooperAddBranchForSwitch(from, to, cArr, indexes.length, code);
} finally {
free_memory(cArr);
memory.free(cArr);
}
}
@ -1390,7 +1415,7 @@ export class Relooper {
function allocU8Array(u8s: Uint8Array | null): usize {
if (!u8s) return 0;
var numValues = u8s.length;
var ptr = allocate_memory(numValues);
var ptr = memory.allocate(numValues);
var idx = ptr;
for (let i = 0; i < numValues; ++i) {
store<u8>(idx++, u8s[i]);
@ -1400,7 +1425,7 @@ function allocU8Array(u8s: Uint8Array | null): usize {
function allocI32Array(i32s: i32[] | null): usize {
if (!i32s) return 0;
var ptr = allocate_memory(i32s.length << 2);
var ptr = memory.allocate(i32s.length << 2);
var idx = ptr;
for (let i = 0, k = i32s.length; i < k; ++i) {
let val = i32s[i];
@ -1444,7 +1469,7 @@ function stringLengthUTF8(str: string): usize {
function allocString(str: string | null): usize {
if (str == null) return 0;
var ptr = allocate_memory(stringLengthUTF8(str) + 1);
var ptr = memory.allocate(stringLengthUTF8(str) + 1);
// the following is based on Emscripten's stringToUTF8Array
var idx = ptr;
for (let i = 0, k = str.length; i < k; ++i) {

View File

@ -86,8 +86,6 @@ import {
nodeIsGenericCallable
} from "./ast";
const builtinsFile = LIBRARY_PREFIX + "builtins.ts";
/** Parser interface. */
export class Parser extends DiagnosticEmitter {
@ -135,11 +133,6 @@ export class Parser extends DiagnosticEmitter {
var program = this.program;
program.sources.push(source);
// mark the special builtins library file
if (source.normalizedPath == builtinsFile) {
source.set(CommonFlags.BUILTIN);
}
// tokenize and parse
var tn = new Tokenizer(source, program.diagnostics);
tn.onComment = this.onComment;
@ -176,9 +169,6 @@ export class Parser extends DiagnosticEmitter {
decorators.push(decorator);
}
// mark builtins
flags |= (tn.source.flags & CommonFlags.BUILTIN);
// check modifiers
var exportStart: i32 = 0;
var exportEnd: i32 = 0;

View File

@ -66,7 +66,8 @@ import {
VariableLikeDeclarationStatement,
VariableStatement,
decoratorNameToKind
decoratorNameToKind,
findDecorator
} from "./ast";
import {
@ -132,6 +133,12 @@ class TypeAlias {
type: CommonTypeNode;
}
/** Represents a module-level export. */
class ModuleExport {
element: Element;
identifier: IdentifierExpression;
}
/** Represents the kind of an operator overload. */
export enum OperatorKind {
INVALID,
@ -319,7 +326,7 @@ export class Program extends DiagnosticEmitter {
/** File-level exports by exported name. */
fileLevelExports: Map<string,Element> = new Map();
/** Module-level exports by exported name. */
moduleLevelExports: Map<string,Element> = new Map();
moduleLevelExports: Map<string,ModuleExport> = new Map();
/** Array prototype reference. */
arrayPrototype: ClassPrototype | null = null;
@ -329,6 +336,10 @@ export class Program extends DiagnosticEmitter {
startFunction: FunctionPrototype;
/** Main function reference, if present. */
mainFunction: FunctionPrototype | null = null;
/** Abort function reference, if present. */
abortInstance: Function | null = null;
/** Memory allocation function. */
memoryAllocateInstance: Function | null = null;
/** Currently processing filespace. */
currentFilespace: Filespace;
@ -569,52 +580,54 @@ export class Program extends DiagnosticEmitter {
}
// set up global aliases
var globalAliases = options.globalAliases;
if (globalAliases) {
for (let [alias, name] of globalAliases) {
if (!name.length) continue; // explicitly disabled
let element = this.elementsLookup.get(name);
if (element) this.elementsLookup.set(alias, element);
else throw new Error("element not found: " + name);
{
let globalAliases = options.globalAliases;
if (globalAliases) {
for (let [alias, name] of globalAliases) {
if (!name.length) continue; // explicitly disabled
let element = this.elementsLookup.get(name);
if (element) this.elementsLookup.set(alias, element);
else throw new Error("element not found: " + name);
}
}
}
// register 'Array'
var arrayPrototype = this.elementsLookup.get("Array");
if (arrayPrototype) {
assert(arrayPrototype.kind == ElementKind.CLASS_PROTOTYPE);
this.arrayPrototype = <ClassPrototype>arrayPrototype;
if (this.elementsLookup.has("Array")) {
let element = assert(this.elementsLookup.get("Array"));
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
this.arrayPrototype = <ClassPrototype>element;
}
// register 'String'
var stringPrototype = this.elementsLookup.get("String");
if (stringPrototype) {
assert(stringPrototype.kind == ElementKind.CLASS_PROTOTYPE);
let stringInstance = resolver.resolveClass(<ClassPrototype>stringPrototype, null);
if (stringInstance) {
if (this.elementsLookup.has("String")) {
let element = assert(this.elementsLookup.get("String"));
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
let instance = resolver.resolveClass(<ClassPrototype>element, null);
if (instance) {
if (this.typesLookup.has("string")) {
let declaration = (<ClassPrototype>stringPrototype).declaration;
let declaration = (<ClassPrototype>element).declaration;
this.error(
DiagnosticCode.Duplicate_identifier_0,
declaration.name.range, declaration.programLevelInternalName
);
} else {
this.stringInstance = stringInstance;
this.typesLookup.set("string", stringInstance.type);
this.stringInstance = instance;
this.typesLookup.set("string", instance.type);
}
}
}
// register 'start'
{
let element = <Element>assert(this.elementsLookup.get("start"));
let element = assert(this.elementsLookup.get("start"));
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
this.startFunction = <FunctionPrototype>element;
}
// register 'main' if present
if (this.moduleLevelExports.has("main")) {
let element = <Element>this.moduleLevelExports.get("main");
let element = (<ModuleExport>this.moduleLevelExports.get("main")).element;
if (
element.kind == ElementKind.FUNCTION_PROTOTYPE &&
!(<FunctionPrototype>element).isAny(CommonFlags.GENERIC | CommonFlags.AMBIENT)
@ -623,6 +636,28 @@ export class Program extends DiagnosticEmitter {
this.mainFunction = <FunctionPrototype>element;
}
}
// register 'abort' if present
if (this.elementsLookup.has("abort")) {
let element = <Element>this.elementsLookup.get("abort");
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
let instance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
if (instance) this.abortInstance = instance;
}
// register 'memory.allocate' if present
if (this.elementsLookup.has("memory")) {
let element = <Element>this.elementsLookup.get("memory");
let members = element.members;
if (members) {
if (members.has("allocate")) {
element = assert(members.get("allocate"));
assert(element.kind == ElementKind.FUNCTION_PROTOTYPE);
let instance = this.resolver.resolveFunction(<FunctionPrototype>element, null);
if (instance) this.memoryAllocateInstance = instance;
}
}
}
}
/** Sets a constant integer value. */
@ -674,7 +709,16 @@ export class Program extends DiagnosticEmitter {
let kind = decoratorNameToKind(decorator.name);
let flag = decoratorKindToFlag(kind);
if (flag) {
if (!(acceptedFlags & flag)) {
if (flag == DecoratorFlags.BUILTIN) {
if (decorator.range.source.isLibrary) {
presentFlags |= flag;
} else {
this.error(
DiagnosticCode.Decorator_0_is_not_valid_here,
decorator.range, decorator.name.range.toString()
);
}
} else if (!(acceptedFlags & flag)) {
this.error(
DiagnosticCode.Decorator_0_is_not_valid_here,
decorator.range, decorator.name.range.toString()
@ -698,13 +742,9 @@ export class Program extends DiagnosticEmitter {
declaration: DeclarationStatement
): void {
var parentNode = declaration.parent;
// alias the element globally if it is ...
// alias globally if explicitly annotated @global or exported from a top-level library file
if (
// explicitly annotated with @global - or -
(element.hasDecorator(DecoratorFlags.GLOBAL)) ||
// part of the special builtins library file - or -
(declaration.range.source.is(CommonFlags.BUILTIN)) ||
// exported from a top-level library file
(
declaration.range.source.isLibrary &&
element.is(CommonFlags.EXPORT) &&
@ -724,10 +764,12 @@ export class Program extends DiagnosticEmitter {
);
} else {
this.elementsLookup.set(globalName, element);
// builtins can use the global name directly instead of being just an alias
if (element.is(CommonFlags.BUILTIN)) element.internalName = globalName;
}
}
// builtins use the global name directly
if (element.hasDecorator(DecoratorFlags.BUILTIN)) {
element.internalName = declaration.programLevelInternalName;
}
}
/** Initializes a class declaration. */
@ -824,14 +866,18 @@ export class Program extends DiagnosticEmitter {
this.currentFilespace.members.set(simpleName, prototype);
if (prototype.is(CommonFlags.EXPORT) && declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(simpleName)) {
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
this.error(
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
declaration.name.range, existingExport.element.internalName
);
return;
}
prototype.set(CommonFlags.MODULE_EXPORT);
this.moduleLevelExports.set(simpleName, prototype);
this.moduleLevelExports.set(simpleName, <ModuleExport>{
element: prototype,
identifier: declaration.name
});
}
}
@ -903,7 +949,7 @@ export class Program extends DiagnosticEmitter {
Type.void, // resolved later on
declaration,
decorators
? this.checkDecorators(decorators, DecoratorFlags.NONE)
? this.checkDecorators(decorators, DecoratorFlags.INLINE)
: DecoratorFlags.NONE
);
staticField.parent = classPrototype;
@ -913,6 +959,13 @@ export class Program extends DiagnosticEmitter {
staticField.set(CommonFlags.MODULE_EXPORT);
}
if (staticField.hasDecorator(DecoratorFlags.INLINE) && !staticField.is(CommonFlags.READONLY)) {
this.error(
DiagnosticCode.Decorator_0_is_not_valid_here,
assert(findDecorator(DecoratorKind.INLINE, decorators)).range, "inline"
);
}
// instance fields are remembered until resolved
} else {
if (isInterface) {
@ -1269,14 +1322,18 @@ export class Program extends DiagnosticEmitter {
this.currentFilespace.members.set(simpleName, element);
if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(simpleName)) {
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
this.error(
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
declaration.name.range, existingExport.element.internalName
);
return;
}
element.set(CommonFlags.MODULE_EXPORT);
this.moduleLevelExports.set(simpleName, element);
this.moduleLevelExports.set(simpleName, <ModuleExport>{
element,
identifier: declaration.name
});
}
}
@ -1332,31 +1389,39 @@ export class Program extends DiagnosticEmitter {
private setExportAndCheckLibrary(
internalName: string,
element: Element,
identifier: IdentifierExpression
externalIdentifier: IdentifierExpression
): void {
// add to file-level exports
this.fileLevelExports.set(internalName, element);
// add to filespace
var internalPath = identifier.range.source.internalPath;
var internalPath = externalIdentifier.range.source.internalPath;
var prefix = FILESPACE_PREFIX + internalPath;
var filespace = this.elementsLookup.get(prefix);
if (!filespace) filespace = assert(this.elementsLookup.get(prefix + PATH_DELIMITER + "index"));
assert(filespace.kind == ElementKind.FILESPACE);
var simpleName = identifier.text;
var simpleName = externalIdentifier.text;
(<Filespace>filespace).members.set(simpleName, element);
// add global alias if from a library file
if (identifier.range.source.isLibrary) {
// add global alias if a top-level export of a library file
var source = externalIdentifier.range.source;
if (source.isLibrary) {
if (this.elementsLookup.has(simpleName)) {
this.error(
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
identifier.range, simpleName
externalIdentifier.range, simpleName
);
} else {
element.internalName = simpleName;
this.elementsLookup.set(simpleName, element);
}
// add module level export if a top-level export of an entry file
} else if (source.isEntry) {
this.moduleLevelExports.set(externalIdentifier.text, <ModuleExport>{
element,
identifier: externalIdentifier
});
}
}
@ -1382,10 +1447,10 @@ export class Program extends DiagnosticEmitter {
referencedName = member.range.source.internalPath + PATH_DELIMITER + member.name.text;
// resolve right away if the element exists
if (referencedElement = this.elementsLookup.get(referencedName)) {
if (this.elementsLookup.has(referencedName)) {
this.setExportAndCheckLibrary(
externalName,
referencedElement,
<Element>this.elementsLookup.get(referencedName),
member.externalName
);
return;
@ -1526,14 +1591,18 @@ export class Program extends DiagnosticEmitter {
this.currentFilespace.members.set(simpleName, prototype);
if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(simpleName)) {
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
this.error(
DiagnosticCode.Duplicate_identifier_0,
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
declaration.name.range, existingExport.element.internalName
);
return;
}
prototype.set(CommonFlags.MODULE_EXPORT);
this.moduleLevelExports.set(simpleName, prototype);
this.moduleLevelExports.set(simpleName, <ModuleExport>{
element: prototype,
identifier: declaration.name
});
}
}
@ -1687,14 +1756,18 @@ export class Program extends DiagnosticEmitter {
this.currentFilespace.members.set(simpleName, prototype);
if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(simpleName)) {
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
this.error(
DiagnosticCode.Duplicate_identifier_0,
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
declaration.name.range, existingExport.element.internalName
);
return;
}
prototype.set(CommonFlags.MODULE_EXPORT);
this.moduleLevelExports.set(simpleName, prototype);
this.moduleLevelExports.set(simpleName, <ModuleExport>{
element: prototype,
identifier: declaration.name
});
}
}
@ -1772,15 +1845,19 @@ export class Program extends DiagnosticEmitter {
this.currentFilespace.members.set(simpleName, namespace);
if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(simpleName)) {
if (this.moduleLevelExports.get(simpleName) !== namespace) { // not merged
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
if (existingExport.element !== namespace) { // not merged
this.error(
DiagnosticCode.Duplicate_identifier_0,
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
declaration.name.range, existingExport.element.internalName
);
return;
}
} else {
this.moduleLevelExports.set(simpleName, namespace);
this.moduleLevelExports.set(simpleName, <ModuleExport>{
element: namespace,
identifier: declaration.name
});
}
namespace.set(CommonFlags.MODULE_EXPORT);
}
@ -1869,6 +1946,7 @@ export class Program extends DiagnosticEmitter {
decorators
? this.checkDecorators(decorators,
DecoratorFlags.GLOBAL |
DecoratorFlags.INLINE |
DecoratorFlags.EXTERNAL
)
: DecoratorFlags.NONE
@ -1876,6 +1954,13 @@ export class Program extends DiagnosticEmitter {
global.parent = namespace;
this.elementsLookup.set(internalName, global);
if (global.hasDecorator(DecoratorFlags.INLINE) && !global.is(CommonFlags.CONST)) {
this.error(
DiagnosticCode.Decorator_0_is_not_valid_here,
assert(findDecorator(DecoratorKind.INLINE, decorators)).range, "inline"
);
}
if (namespace) {
if (namespace.members) {
if (namespace.members.has(simpleName)) {
@ -1904,14 +1989,18 @@ export class Program extends DiagnosticEmitter {
this.currentFilespace.members.set(simpleName, global);
if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(simpleName)) {
let existingExport = <ModuleExport>this.moduleLevelExports.get(simpleName);
this.error(
DiagnosticCode.Duplicate_identifier_0,
declaration.name.range, (<Element>this.moduleLevelExports.get(simpleName)).internalName
declaration.name.range, existingExport.element.internalName
);
continue;
}
global.set(CommonFlags.MODULE_EXPORT);
this.moduleLevelExports.set(simpleName, global);
this.moduleLevelExports.set(simpleName, <ModuleExport>{
element: global,
identifier: declaration.name
});
}
}
this.checkGlobal(global, declaration);
@ -1973,7 +2062,9 @@ export enum DecoratorFlags {
/** Is always inlined. */
INLINE = 1 << 6,
/** Is using a different external name. */
EXTERNAL = 1 << 7
EXTERNAL = 1 << 7,
/** Is a builtin. */
BUILTIN = 1 << 8
}
export function decoratorKindToFlag(kind: DecoratorKind): DecoratorFlags {
@ -1987,6 +2078,7 @@ export function decoratorKindToFlag(kind: DecoratorKind): DecoratorFlags {
case DecoratorKind.SEALED: return DecoratorFlags.SEALED;
case DecoratorKind.INLINE: return DecoratorFlags.INLINE;
case DecoratorKind.EXTERNAL: return DecoratorFlags.EXTERNAL;
case DecoratorKind.BUILTIN: return DecoratorFlags.BUILTIN;
default: return DecoratorFlags.NONE;
}
}
@ -2308,7 +2400,7 @@ export class Function extends Element {
this.flags = prototype.flags;
this.decoratorFlags = prototype.decoratorFlags;
this.contextualTypeArguments = contextualTypeArguments;
if (!(prototype.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN) || prototype.is(CommonFlags.DECLARE))) {
if (!(prototype.is(CommonFlags.AMBIENT))) {
let localIndex = 0;
if (parent && parent.kind == ElementKind.CLASS) {
assert(this.is(CommonFlags.INSTANCE));

View File

@ -1,7 +1,7 @@
/**
* Arena Memory Allocator
*
* Provides a `reset_memory` function to reset the heap to its initial state. A user has to make
* Provides a `memory.reset` function to reset the heap to its initial state. A user has to make
* sure that there are no more references to cleared memory afterwards. Always aligns to 8 bytes.
*
* @module std/assembly/allocator/arena
@ -12,18 +12,19 @@ import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator";
var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
var offset: usize = startOffset;
@global
export function allocate_memory(size: usize): usize {
// Memory allocator interface
@global export function __memory_allocate(size: usize): usize {
if (size) {
if (size > MAX_SIZE_32) unreachable();
let ptr = offset;
let newPtr = (ptr + size + AL_MASK) & ~AL_MASK;
let pagesBefore = current_memory();
let pagesBefore = memory.size();
if (newPtr > <usize>pagesBefore << 16) {
let pagesNeeded = ((newPtr - ptr + 0xffff) & ~0xffff) >>> 16;
let pagesWanted = max(pagesBefore, pagesNeeded); // double memory
if (grow_memory(pagesWanted) < 0) {
if (grow_memory(pagesNeeded) < 0) {
if (memory.grow(pagesWanted) < 0) {
if (memory.grow(pagesNeeded) < 0) {
unreachable(); // out of memory
}
}
@ -34,12 +35,8 @@ export function allocate_memory(size: usize): usize {
return 0;
}
@global
export function free_memory(ptr: usize): void {
// nop
}
@global export function __memory_free(ptr: usize): void { /* nop */ }
@global
export function reset_memory(): void {
@global export function __memory_reset(): void {
offset = startOffset;
}

View File

@ -176,10 +176,10 @@ function update_max_ptr(new_value: usize): i32 {
// if (brk(new_value)) {
// return 0;
// }
let oldPages = <u32>current_memory();
let newPages = <u32>(((new_value + 0xffff) & ~0xffff) >> 16);
let oldPages = <i32>memory.size();
let newPages = <i32>(((new_value + 0xffff) & ~0xffff) >>> 16);
assert(newPages > oldPages);
if (grow_memory(newPages - oldPages) < 0) {
if (memory.grow(newPages - oldPages) < 0) {
return 0;
}
// max_ptr = new_value;
@ -338,8 +338,9 @@ function lower_bucket_limit(bucket: usize): u32 {
return 1;
}
@global
export function allocate_memory(request: usize): usize {
// Memory allocator interface
@global export function __memory_allocate(request: usize): usize {
var original_bucket: usize, bucket: usize;
/*
@ -357,7 +358,7 @@ export function allocate_memory(request: usize): usize {
if (base_ptr == 0) {
// base_ptr = max_ptr = (uint8_t *)sbrk(0);
base_ptr = (NODE_IS_SPLIT_END + 7) & ~7; // must be aligned
max_ptr = <usize>current_memory() << 16; // must grow first
max_ptr = <usize>memory.size() << 16; // must grow first
bucket_limit = BUCKET_COUNT - 1;
if (!update_max_ptr(base_ptr + List.SIZE)) {
return 0;
@ -473,8 +474,7 @@ export function allocate_memory(request: usize): usize {
return 0;
}
@global
export function free_memory(ptr: usize): void {
@global export function __memory_free(ptr: usize): void {
var bucket: usize, i: usize;
/*
@ -538,8 +538,3 @@ export function free_memory(ptr: usize): void {
*/
list_push(buckets$get(bucket), changetype<List>(ptr_for_node(i, bucket)));
}
@global
export function reset_memory(): void {
unreachable();
}

View File

@ -11,17 +11,12 @@
declare function _malloc(size: usize): usize;
declare function _free(ptr: usize): void;
@global
export function allocate_memory(size: usize): usize {
// Memory allocator interface
@global export function __memory_allocate(size: usize): usize {
return _malloc(size);
}
@global
export function free_memory(ptr: usize): void {
@global export function __memory_free(ptr: usize): void {
_free(ptr);
}
@global
export function reset_memory(): void {
unreachable();
}

View File

@ -10,17 +10,12 @@
declare function malloc(size: usize): usize;
declare function free(ptr: usize): void;
@global
export function allocate_memory(size: usize): usize {
// Memory allocator interface
@global export function __memory_allocate(size: usize): usize {
return malloc(size);
}
@global
export function free_memory(ptr: usize): void {
@global export function __memory_free(ptr: usize): void {
free(ptr);
}
@global
export function reset_memory(): void {
unreachable();
}

View File

@ -433,19 +433,18 @@ function fls<T>(word: T): T {
/** Reference to the initialized {@link Root} structure, once initialized. */
var ROOT: Root = changetype<Root>(0);
// External interface
// Memory allocator interface
/** Allocates a chunk of memory. */
@global
export function allocate_memory(size: usize): usize {
@global export function __memory_allocate(size: usize): usize {
// initialize if necessary
var root = ROOT;
if (!root) {
let rootOffset = (HEAP_BASE + AL_MASK) & ~AL_MASK;
let pagesBefore = current_memory();
let pagesBefore = memory.size();
let pagesNeeded = <i32>((((rootOffset + Root.SIZE) + 0xffff) & ~0xffff) >>> 16);
if (pagesNeeded > pagesBefore && grow_memory(pagesNeeded - pagesBefore) < 0) unreachable();
if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable();
ROOT = root = changetype<Root>(rootOffset);
root.tailRef = 0;
root.flMap = 0;
@ -455,7 +454,7 @@ export function allocate_memory(size: usize): usize {
root.setHead(fl, sl, null);
}
}
root.addMemory((rootOffset + Root.SIZE + AL_MASK) & ~AL_MASK, current_memory() << 16);
root.addMemory((rootOffset + Root.SIZE + AL_MASK) & ~AL_MASK, memory.size() << 16);
}
// search for a suitable block
@ -469,15 +468,15 @@ export function allocate_memory(size: usize): usize {
if (!block) {
// request more memory
let pagesBefore = current_memory();
let pagesBefore = memory.size();
let pagesNeeded = <i32>(((size + 0xffff) & ~0xffff) >>> 16);
let pagesWanted = max(pagesBefore, pagesNeeded); // double memory
if (grow_memory(pagesWanted) < 0) {
if (grow_memory(pagesNeeded) < 0) {
if (memory.grow(pagesWanted) < 0) {
if (memory.grow(pagesNeeded) < 0) {
unreachable(); // out of memory
}
}
let pagesAfter = current_memory();
let pagesAfter = memory.size();
root.addMemory(<usize>pagesBefore << 16, <usize>pagesAfter << 16);
block = assert(root.search(size)); // must be found now
}
@ -490,8 +489,7 @@ export function allocate_memory(size: usize): usize {
}
/** Frees the chunk of memory at the specified address. */
@global
export function free_memory(data: usize): void {
@global export function __memory_free(data: usize): void {
if (data) {
let root = ROOT;
if (root) {
@ -504,7 +502,6 @@ export function free_memory(data: usize): void {
}
}
@global
export function reset_memory(): void {
@global export function __memory_reset(): void {
unreachable();
}

View File

@ -25,7 +25,7 @@ export class Array<T> {
var buffer = allocUnsafe(byteLength);
this.buffer_ = buffer;
this.length_ = length;
set_memory(
memory.fill(
changetype<usize>(buffer) + HEADER_SIZE_AB,
0,
<usize>byteLength
@ -216,7 +216,7 @@ export class Array<T> {
var buffer = this.buffer_;
var element = loadUnsafe<T,T>(buffer, 0);
var lastIndex = length - 1;
move_memory(
memory.copy(
changetype<usize>(buffer) + HEADER_SIZE_AB,
changetype<usize>(buffer) + HEADER_SIZE_AB + sizeof<T>(),
<usize>lastIndex << alignof<T>()
@ -246,7 +246,7 @@ export class Array<T> {
capacity = buffer.byteLength >>> alignof<T>();
this.buffer_ = buffer;
}
move_memory(
memory.copy(
changetype<usize>(buffer) + HEADER_SIZE_AB + sizeof<T>(),
changetype<usize>(buffer) + HEADER_SIZE_AB,
<usize>(capacity - 1) << alignof<T>()
@ -267,7 +267,7 @@ export class Array<T> {
assert(newLength >= 0);
var sliced = new Array<T>(newLength);
if (newLength) {
move_memory(
memory.copy(
changetype<usize>(sliced.buffer_) + HEADER_SIZE_AB,
changetype<usize>(this.buffer_) + HEADER_SIZE_AB + (<usize>begin << alignof<T>()),
<usize>newLength << alignof<T>()
@ -283,7 +283,7 @@ export class Array<T> {
if (start >= length) return;
deleteCount = min(deleteCount, length - start);
var buffer = this.buffer_;
move_memory(
memory.copy(
changetype<usize>(buffer) + HEADER_SIZE_AB + (<usize>start << alignof<T>()),
changetype<usize>(buffer) + HEADER_SIZE_AB + (<usize>(start + deleteCount) << alignof<T>()),
<usize>deleteCount << alignof<T>()

View File

@ -12,7 +12,7 @@ export class ArrayBuffer {
constructor(length: i32, unsafe: bool = false) {
if (<u32>length > <u32>MAX_BLENGTH) throw new RangeError("Invalid array buffer length");
var buffer = allocUnsafe(length);
if (!unsafe) set_memory(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>length);
if (!unsafe) memory.fill(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>length);
return buffer;
}
@ -24,7 +24,7 @@ export class ArrayBuffer {
else end = min(end, len);
var newLen = max(end - begin, 0);
var buffer = allocUnsafe(newLen);
move_memory(changetype<usize>(buffer) + HEADER_SIZE, changetype<usize>(this) + HEADER_SIZE + begin, newLen);
memory.copy(changetype<usize>(buffer) + HEADER_SIZE, changetype<usize>(this) + HEADER_SIZE + begin, newLen);
return buffer;
}
}

View File

@ -1,147 +1,103 @@
export declare function isInteger<T>(value?: T): bool;
/* tslint:disable */
export declare function isFloat<T>(value?: T): bool;
@builtin @inline export const NaN: f64 = 0 / 0;
@builtin @inline export const Infinity: f64 = 1 / 0;
@builtin export declare const HEAP_BASE: usize;
export declare function isSigned<T>(value?: T): bool;
@builtin export declare function isInteger<T>(value?: T): bool;
@builtin export declare function isFloat<T>(value?: T): bool;
@builtin export declare function isSigned<T>(value?: T): bool;
@builtin export declare function isReference<T>(value?: T): bool;
@builtin export declare function isString<T>(value?: T): bool;
@builtin export declare function isArray<T>(value?: T): bool;
@builtin export declare function isDefined(expression: void): bool;
@builtin export declare function isConstant(expression: void): bool;
@inline export function isNaN<T>(value: T): bool { return value != value; }
@inline export function isFinite<T>(value: T): bool { return value - value == 0; }
export declare function isReference<T>(value?: T): bool;
@builtin export declare function clz<T>(value: T): T;
@builtin export declare function ctz<T>(value: T): T;
@builtin export declare function popcnt<T>(value: T): T;
@builtin export declare function rotl<T>(value: T, shift: T): T;
@builtin export declare function rotr<T>(value: T, shift: T): T;
@builtin export declare function abs<T>(value: T): T;
@builtin export declare function max<T>(left: T, right: T): T;
@builtin export declare function min<T>(left: T, right: T): T;
@builtin export declare function ceil<T>(value: T): T;
@builtin export declare function floor<T>(value: T): T;
@builtin export declare function copysign<T>(left: T, right: T): T;
@builtin export declare function nearest<T>(value: T): T;
@builtin export declare function reinterpret<T>(value: void): T;
@builtin export declare function sqrt<T>(value: T): T;
@builtin export declare function trunc<T>(value: T): T;
@builtin export declare function load<T>(offset: usize, constantOffset?: usize): T;
@builtin export declare function store<T>(offset: usize, value: void, constantOffset?: usize): void;
@builtin export declare function sizeof<T>(): usize; // | u32 / u64
@builtin export declare function alignof<T>(): usize; // | u32 / u64
@builtin export declare function offsetof<T>(fieldName?: string): usize; // | u32 / u64
@builtin export declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
@builtin export declare function unreachable(): void;
@builtin export declare function changetype<T>(value: void): T;
@builtin export declare function assert<T>(isTrueish: T, message?: string): T;
@builtin export declare function unchecked<T>(expr: T): T;
@builtin export declare function call_indirect<T>(target: void, ...args: void[]): T;
export declare function isString<T>(value?: T): bool;
export declare function isArray<T>(value?: T): bool;
export declare function isDefined(expression: void): bool;
export declare function isConstant(expression: void): bool;
export const NaN: f64 = 0 / 0;
export const Infinity: f64 = 1 / 0;
export function isNaN<T>(value: T): bool {
return value != value;
}
export function isFinite<T>(value: T): bool {
return value - value == 0;
}
export declare function clz<T>(value: T): T;
export declare function ctz<T>(value: T): T;
export declare function popcnt<T>(value: T): T;
export declare function rotl<T>(value: T, shift: T): T;
export declare function rotr<T>(value: T, shift: T): T;
export declare function abs<T>(value: T): T;
export declare function max<T>(left: T, right: T): T;
export declare function min<T>(left: T, right: T): T;
export declare function ceil<T>(value: T): T;
export declare function floor<T>(value: T): T;
export declare function copysign<T>(left: T, right: T): T;
export declare function nearest<T>(value: T): T;
export declare function reinterpret<T>(value: void): T;
export declare function sqrt<T>(value: T): T;
export declare function trunc<T>(value: T): T;
export declare function load<T>(offset: usize, constantOffset?: usize): T;
export declare function store<T>(offset: usize, value: void, constantOffset?: usize): void;
export declare function sizeof<T>(): usize; // | u32 / u64
export declare function alignof<T>(): usize; // | u32 / u64
export declare function offsetof<T>(fieldName?: string): usize; // | u32 / u64
export declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
export declare function unreachable(): void;
export declare function current_memory(): i32;
export declare function grow_memory(pages: i32): i32;
// export declare function move_memory(dest: usize, src: usize: n: usize): void;
// export declare function set_memory(dest: usize, value: u32, n: usize): void;
export declare function changetype<T>(value: void): T;
export declare function assert<T>(isTrueish: T, message?: string): T;
export declare function unchecked<T>(expr: T): T;
export declare function call_indirect<T>(target: void, ...args: void[]): T;
export declare function i8(value: void): i8;
@builtin export declare function i8(value: void): i8;
export namespace i8 {
export const MIN_VALUE: i8 = -128;
export const MAX_VALUE: i8 = 127;
}
export declare function i16(value: void): i16;
@builtin export declare function i16(value: void): i16;
export namespace i16 {
export const MIN_VALUE: i16 = -32768;
export const MAX_VALUE: i16 = 32767;
}
export declare function i32(value: void): i32;
@builtin export declare function i32(value: void): i32;
export namespace i32 {
export const MIN_VALUE: i32 = -2147483648;
export const MAX_VALUE: i32 = 2147483647;
export declare function clz(value: i32): i32;
export declare function ctz(value: i32): i32;
export declare function popcnt(value: i32): i32;
export declare function rotl(value: i32, shift: i32): i32;
export declare function rotr(value: i32, shift: i32): i32;
export declare function reinterpret_f32(value: f32): i32;
export declare function load8_s(offset: usize, constantOffset?: usize): i32;
export declare function load8_u(offset: usize, constantOffset?: usize): i32;
export declare function load16_s(offset: usize, constantOffset?: usize): i32;
export declare function load16_u(offset: usize, constantOffset?: usize): i32;
export declare function load(offset: usize, constantOffset?: usize): i32;
export declare function store8(offset: usize, value: i32, constantOffset?: usize): void;
export declare function store16(offset: usize, value: i32, constantOffset?: usize): void;
export declare function store(offset: usize, value: i32, constantOffset?: usize): void;
@builtin export declare function clz(value: i32): i32;
@builtin export declare function ctz(value: i32): i32;
@builtin export declare function popcnt(value: i32): i32;
@builtin export declare function rotl(value: i32, shift: i32): i32;
@builtin export declare function rotr(value: i32, shift: i32): i32;
@builtin export declare function reinterpret_f32(value: f32): i32;
@builtin export declare function load8_s(offset: usize, constantOffset?: usize): i32;
@builtin export declare function load8_u(offset: usize, constantOffset?: usize): i32;
@builtin export declare function load16_s(offset: usize, constantOffset?: usize): i32;
@builtin export declare function load16_u(offset: usize, constantOffset?: usize): i32;
@builtin export declare function load(offset: usize, constantOffset?: usize): i32;
@builtin export declare function store8(offset: usize, value: i32, constantOffset?: usize): void;
@builtin export declare function store16(offset: usize, value: i32, constantOffset?: usize): void;
@builtin export declare function store(offset: usize, value: i32, constantOffset?: usize): void;
}
export declare function i64(value: void): i64;
@builtin export declare function i64(value: void): i64;
export namespace i64 {
export const MIN_VALUE: i64 = -9223372036854775808;
export const MAX_VALUE: i64 = 9223372036854775807;
export declare function clz(value: i64): i64;
export declare function ctz(value: i64): i64;
export declare function load8_s(offset: usize, constantOffset?: usize): i64;
export declare function load8_u(offset: usize, constantOffset?: usize): u64;
export declare function load16_s(offset: usize, constantOffset?: usize): i64;
export declare function load16_u(offset: usize, constantOffset?: usize): u64;
export declare function load32_s(offset: usize, constantOffset?: usize): i64;
export declare function load32_u(offset: usize, constantOffset?: usize): u64;
export declare function load(offset: usize, constantOffset?: usize): i64;
export declare function popcnt(value: i64): i64;
export declare function rotl(value: i64, shift: i64): i64;
export declare function rotr(value: i64, shift: i64): i64;
export declare function reinterpret_f64(value: f64): i64;
export declare function store8(offset: usize, value: i64, constantOffset?: usize): void;
export declare function store16(offset: usize, value: i64, constantOffset?: usize): void;
export declare function store32(offset: usize, value: i64, constantOffset?: usize): void;
export declare function store(offset: usize, value: i64, constantOffset?: usize): void;
@builtin export declare function clz(value: i64): i64;
@builtin export declare function ctz(value: i64): i64;
@builtin export declare function load8_s(offset: usize, constantOffset?: usize): i64;
@builtin export declare function load8_u(offset: usize, constantOffset?: usize): u64;
@builtin export declare function load16_s(offset: usize, constantOffset?: usize): i64;
@builtin export declare function load16_u(offset: usize, constantOffset?: usize): u64;
@builtin export declare function load32_s(offset: usize, constantOffset?: usize): i64;
@builtin export declare function load32_u(offset: usize, constantOffset?: usize): u64;
@builtin export declare function load(offset: usize, constantOffset?: usize): i64;
@builtin export declare function popcnt(value: i64): i64;
@builtin export declare function rotl(value: i64, shift: i64): i64;
@builtin export declare function rotr(value: i64, shift: i64): i64;
@builtin export declare function reinterpret_f64(value: f64): i64;
@builtin export declare function store8(offset: usize, value: i64, constantOffset?: usize): void;
@builtin export declare function store16(offset: usize, value: i64, constantOffset?: usize): void;
@builtin export declare function store32(offset: usize, value: i64, constantOffset?: usize): void;
@builtin export declare function store(offset: usize, value: i64, constantOffset?: usize): void;
}
export declare function isize(value: void): isize;
@builtin export declare function isize(value: void): isize;
export namespace isize {
export const MIN_VALUE: isize = sizeof<i32>() == sizeof<isize>()
? -2147483648
@ -151,31 +107,31 @@ export namespace isize {
: <isize>9223372036854775807;
}
export declare function u8(value: void): u8;
@builtin export declare function u8(value: void): u8;
export namespace u8 {
export const MIN_VALUE: u8 = 0;
export const MAX_VALUE: u8 = 255;
}
export declare function u16(value: void): u16;
@builtin export declare function u16(value: void): u16;
export namespace u16 {
export const MIN_VALUE: u16 = 0;
export const MAX_VALUE: u16 = 65535;
}
export declare function u32(value: void): u32;
@builtin export declare function u32(value: void): u32;
export namespace u32 {
export const MIN_VALUE: u32 = 0;
export const MAX_VALUE: u32 = 4294967295;
}
export declare function u64(value: void): u64;
@builtin export declare function u64(value: void): u64;
export namespace u64 {
export const MIN_VALUE: u64 = 0;
export const MAX_VALUE: u64 = 18446744073709551615;
}
export declare function usize(value: void): usize;
@builtin export declare function usize(value: void): usize;
export namespace usize {
export const MIN_VALUE: usize = 0;
export const MAX_VALUE: usize = sizeof<u32>() == sizeof<usize>()
@ -183,13 +139,13 @@ export namespace usize {
: <usize>18446744073709551615;
}
export declare function bool(value: void): bool;
@builtin export declare function bool(value: void): bool;
export namespace bool {
export const MIN_VALUE: bool = false;
export const MAX_VALUE: bool = true;
}
export declare function f32(value: void): f32;
@builtin export declare function f32(value: void): f32;
export namespace f32 {
export const MIN_VALUE = reinterpret<f32>(0xFF7FFFFF); // -0x1.fffffep+127f
export const MAX_VALUE = reinterpret<f32>(0x7F7FFFFF); // 0x1.fffffep+127f
@ -197,21 +153,21 @@ export namespace f32 {
export const MIN_SAFE_INTEGER: f32 = -16777215;
export const MAX_SAFE_INTEGER: f32 = 16777215;
export const EPSILON = reinterpret<f32>(0x34000000); // 0x1p-23f
export declare function abs(value: f32): f32;
export declare function ceil(value: f32): f32;
export declare function copysign(x: f32, y: f32): f32;
export declare function floor(value: f32): f32;
export declare function load(offset: usize, constantOffset?: usize): f32;
export declare function max(left: f32, right: f32): f32;
export declare function min(left: f32, right: f32): f32;
export declare function nearest(value: f32): f32;
export declare function reinterpret_i32(value: i32): f32;
export declare function sqrt(value: f32): f32;
export declare function store(offset: usize, value: f32, constantOffset?: usize): void;
export declare function trunc(value: f32): f32;
@builtin export declare function abs(value: f32): f32;
@builtin export declare function ceil(value: f32): f32;
@builtin export declare function copysign(x: f32, y: f32): f32;
@builtin export declare function floor(value: f32): f32;
@builtin export declare function load(offset: usize, constantOffset?: usize): f32;
@builtin export declare function max(left: f32, right: f32): f32;
@builtin export declare function min(left: f32, right: f32): f32;
@builtin export declare function nearest(value: f32): f32;
@builtin export declare function reinterpret_i32(value: i32): f32;
@builtin export declare function sqrt(value: f32): f32;
@builtin export declare function store(offset: usize, value: f32, constantOffset?: usize): void;
@builtin export declare function trunc(value: f32): f32;
}
export declare function f64(value: void): f64;
@builtin export declare function f64(value: void): f64;
export namespace f64 {
export const MIN_VALUE = reinterpret<f64>(0xFFEFFFFFFFFFFFFF); // -0x1.fffffffffffffp+1023
export const MAX_VALUE = reinterpret<f64>(0x7FEFFFFFFFFFFFFF); // 0x1.fffffffffffffp+1023
@ -219,20 +175,24 @@ export namespace f64 {
export const MIN_SAFE_INTEGER: f64 = -9007199254740991;
export const MAX_SAFE_INTEGER: f64 = 9007199254740991;
export const EPSILON = reinterpret<f64>(0x3CB0000000000000); // 0x1p-52
export declare function abs(value: f64): f64;
export declare function ceil(value: f64): f64;
export declare function copysign(x: f64, y: f64): f64;
export declare function floor(value: f64): f64;
export declare function load(offset: usize, constantOffset?: usize): f64;
export declare function max(left: f64, right: f64): f64;
export declare function min(left: f64, right: f64): f64;
export declare function nearest(value: f64): f64;
export declare function reinterpret_i64(value: i64): f64;
export declare function sqrt(value: f64): f64;
export declare function store(offset: usize, value: f64, constantOffset?: usize): void;
export declare function trunc(value: f64): f64;
@builtin export declare function abs(value: f64): f64;
@builtin export declare function ceil(value: f64): f64;
@builtin export declare function copysign(x: f64, y: f64): f64;
@builtin export declare function floor(value: f64): f64;
@builtin export declare function load(offset: usize, constantOffset?: usize): f64;
@builtin export declare function max(left: f64, right: f64): f64;
@builtin export declare function min(left: f64, right: f64): f64;
@builtin export declare function nearest(value: f64): f64;
@builtin export declare function reinterpret_i64(value: i64): f64;
@builtin export declare function sqrt(value: f64): f64;
@builtin export declare function store(offset: usize, value: f64, constantOffset?: usize): void;
@builtin export declare function trunc(value: f64): f64;
}
export declare const HEAP_BASE: usize;
@builtin export declare function start(): void;
export declare function start(): void;
@builtin export declare function ERROR(message?: void): void;
@builtin export declare function WARNING(message?: void): void;
@builtin export declare function INFO(message?: void): void;
@builtin export declare function __gc_iterate_roots(fn: (ref: usize) => void): void;

View File

@ -4,116 +4,18 @@
* @module std/assembly/collector/itcm
*//***/
// Based on the concepts of Bach Le's μgc, see: https://github.com/bullno1/ugc
// Largely based on the Bach Le's μgc, see: https://github.com/bullno1/ugc
const TRACE = false;
import {
AL_MASK,
MAX_SIZE_32
} from "../internal/allocator";
// ╒═══════════════ Managed object 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
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┤ ┐
// │ next │ F │ ◄─┐ = nextWithFlags
// ├─────────────────────────────────────────────────────────┴─────┤ │ usize
// │ prev │ ◄─┘
// ╞═══════════════════════════════════════════════════════════════╡ SIZE ┘
// │ ... data ... │
// └───────────────────────────────────────────────────────────────┘
// F: flags
/** Managed object flags. */
namespace Flags {
/** Object is unreachable (so far). */
export var WHITE = 0;
/** Object is reachable. */
export var BLACK = 1;
/** Object is reachable but its children have not yet been scanned. */
export const GRAY = 2;
/** Mask to obtain just the flag bits. */
export const MASK = AL_MASK;
}
/** Represents a managed object in memory, consisting of a header followed by the object's data. */
@unmanaged
class ManagedObject {
/** Pointer to the next object with additional flags stored in the alignment bits. */
nextWithFlags: usize;
/** Pointer to the previous object. */
prev: ManagedObject;
/** Visitor function called with the data pointer (excl. header). */
visitFn: (obj: usize) => void;
/** Size of a managed object after alignment. */
static readonly SIZE: usize = (offsetof<ManagedObject>() + AL_MASK) & ~AL_MASK;
/** Gets the pointer to the next object in the list. */
get next(): ManagedObject {
return changetype<ManagedObject>(this.nextWithFlags & ~Flags.MASK);
}
/** Sets the pointer to the next object in the list. */
set next(obj: ManagedObject) {
this.nextWithFlags = changetype<usize>(obj) | (this.nextWithFlags & Flags.MASK);
}
/** Inserts an object to this list. */
insert(obj: ManagedObject): void {
var prev = this.prev;
obj.next = this;
obj.prev = prev;
prev.next = obj;
this.prev = obj;
}
/** Removes this object from its list. */
remove(): void {
var next = this.next;
var prev = this.prev;
next.prev = prev;
prev.next = next;
}
/** Tests if this object is white, that is unreachable (so far). */
get isWhite(): bool {
return (this.nextWithFlags & Flags.MASK) == Flags.WHITE;
}
/** Marks this object as white, that is unreachable (so far). */
makeWhite(): void {
this.nextWithFlags = (this.nextWithFlags & ~Flags.MASK) | Flags.WHITE;
}
/** Tests if this object is black, that is reachable. Root objects are always reachable. */
get isBlack(): bool {
return (this.nextWithFlags & Flags.MASK) == Flags.BLACK;
}
/** Marks this object as black, that is reachable. */
makeBlack(): void {
this.nextWithFlags = (this.nextWithFlags & ~Flags.MASK) | Flags.BLACK;
}
/** Tests if this object is gray, that is reachable with unscanned children. */
get isGray(): bool {
return (this.nextWithFlags & Flags.MASK) == Flags.GRAY;
}
/** Marks this object as gray, that is reachable with unscanned children. */
makeGray(): void {
if (this != iter) {
this.remove();
to.insert(this);
} else {
iter = iter.prev;
}
this.nextWithFlags = (this.nextWithFlags & ~Flags.MASK) | Flags.GRAY;
}
}
import {
iterateRoots
} from "../gc";
/** Collector states. */
const enum State {
@ -129,125 +31,213 @@ const enum State {
/** Current collector state. */
var state = State.INIT;
/** Current white color value. */
var white = 0;
// From and to spaces
var from: ManagedObject;
var to: ManagedObject;
var from: ManagedObjectSet;
var to: ManagedObjectSet;
var iter: ManagedObject;
// ╒═══════════════ Managed object 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
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┤ ┐
// │ next │ F │ ◄─┐ = nextWithFlags
// ├─────────────────────────────────────────────────────────┴─────┤ │ usize
// │ prev │ ◄─┘
// ╞═══════════════════════════════════════════════════════════════╡ SIZE ┘
// │ ... data ... │
// └───────────────────────────────────────────────────────────────┘
// F: flags
/** Represents a managed object in memory, consisting of a header followed by the object's data. */
@unmanaged
class ManagedObject {
/** Pointer to the next object with color flags stored in the alignment bits. */
nextWithColor: usize;
/** Pointer to the previous object. */
prev: ManagedObject;
/** Visitor function called with the payload reference. */
visitFn: (ref: usize) => void;
/** Size of a managed object after alignment. */
static readonly SIZE: usize = (offsetof<ManagedObject>() + AL_MASK) & ~AL_MASK;
/** Gets the pointer to the next object in the list. */
get next(): ManagedObject {
return changetype<ManagedObject>(this.nextWithColor & ~3);
}
/** Sets the pointer to the next object in the list. */
set next(obj: ManagedObject) {
this.nextWithColor = changetype<usize>(obj) | (this.nextWithColor & 3);
}
/** Gets this object's color. */
get color(): i32 {
return this.nextWithColor & 3;
}
/** Sets this object's color. */
set color(color: i32) {
this.nextWithColor = (this.nextWithColor & ~3) | color;
}
/** Unlinks this object from its list. */
unlink(): void {
var next = this.next;
var prev = this.prev;
if (TRACE) trace(" unlink", 3, objToRef(prev), objToRef(this), objToRef(next));
next.prev = prev;
prev.next = next;
}
/** Marks this object as gray, that is reachable with unscanned children. */
makeGray(): void {
if (TRACE) trace(" makeGray", 1, objToRef(this));
const gray = 2;
if (this == iter) iter = this.prev;
this.unlink();
to.push(this);
this.nextWithColor = (this.nextWithColor & ~3) | gray;
}
}
/** A set of managed objects. Used for the from and to spaces. */
@unmanaged
class ManagedObjectSet extends ManagedObject {
/** Inserts an object. */
push(obj: ManagedObject): void {
var prev = this.prev;
if (TRACE) trace(" push", 3, objToRef(prev), objToRef(obj), objToRef(this));
obj.next = this;
obj.prev = prev;
prev.next = obj;
this.prev = obj;
}
/** Clears this list. */
clear(): void {
if (TRACE) trace(" clear", 1, objToRef(this));
this.nextWithColor = changetype<usize>(this);
this.prev = this;
}
}
/** Performs a single step according to the current state. */
function gc_step(): void {
function step(): void {
var obj: ManagedObject;
switch (state) {
case State.INIT: {
from = changetype<ManagedObject>(allocate_memory(ManagedObject.SIZE));
from.nextWithFlags = changetype<usize>(from);
from.prev = from;
to = changetype<ManagedObject>(allocate_memory(ManagedObject.SIZE));
to.nextWithFlags = changetype<usize>(to);
to.prev = to;
if (TRACE) trace("gc~step/INIT");
from = changetype<ManagedObjectSet>(memory.allocate(ManagedObject.SIZE));
from.visitFn = changetype<(ref: usize) => void>(<u32>-1); // would error
from.clear();
to = changetype<ManagedObjectSet>(memory.allocate(ManagedObject.SIZE));
to.visitFn = changetype<(ref: usize) => void>(<u32>-1); // would error
to.clear();
iter = to;
state = State.IDLE;
if (TRACE) trace("gc~state = IDLE");
// fall-through
}
case State.IDLE: {
if (TRACE) trace("gc~step/IDLE");
iterateRoots(__gc_mark);
state = State.MARK;
if (TRACE) trace("gc~state = MARK");
break;
}
case State.MARK: {
obj = iter.next;
if (obj != to) {
if (obj !== to) {
if (TRACE) trace("gc~step/MARK iterate", 1, objToRef(obj));
iter = obj;
obj.makeBlack();
obj.visitFn(changetype<usize>(obj) + ManagedObject.SIZE);
obj.color = <i32>!white;
obj.visitFn(objToRef(obj));
} else {
if (TRACE) trace("gc~step/MARK finish");
iterateRoots(__gc_mark);
obj = iter.next;
if (obj == to) {
let temp = from;
if (obj === to) {
let prevFrom = from;
from = to;
to = temp;
Flags.WHITE ^= 1;
Flags.BLACK ^= 1;
iter = from.next;
to = prevFrom;
white = <i32>!white;
iter = prevFrom.next;
state = State.SWEEP;
if (TRACE) trace("gc~state = SWEEP");
}
}
break;
}
case State.SWEEP: {
obj = iter;
if (obj != to) {
if (obj !== to) {
if (TRACE) trace("gc~step/SWEEP free", 1, objToRef(obj));
iter = obj.next;
free_memory(changetype<usize>(obj));
memory.free(changetype<usize>(obj));
} else {
to.nextWithFlags = changetype<usize>(to);
to.prev = to;
if (TRACE) trace("gc~step/SWEEP finish");
to.clear();
state = State.IDLE;
if (TRACE) trace("gc~state = IDLE");
}
break;
}
}
}
/** Allocates a managed object. */
@global
export function gc_allocate(
size: usize,
visitFn: (obj: usize) => void
): usize {
assert(size <= MAX_SIZE_32 - ManagedObject.SIZE);
var obj = changetype<ManagedObject>(allocate_memory(ManagedObject.SIZE + size));
obj.makeWhite();
obj.visitFn = visitFn;
from.insert(obj);
@inline function refToObj(ref: usize): ManagedObject {
return changetype<ManagedObject>(ref - ManagedObject.SIZE);
}
@inline function objToRef(obj: ManagedObject): usize {
return changetype<usize>(obj) + ManagedObject.SIZE;
}
/** Visits a reachable object. Called from the visitFn functions. */
@global
export function gc_visit(obj: ManagedObject): void {
if (state == State.SWEEP) return;
if (obj.isWhite) obj.makeGray();
// Garbage collector interface
@global export function __gc_allocate(
size: usize,
visitFn: (ref: usize) => void
): usize {
if (TRACE) trace("gc.allocate", 1, size);
if (size > MAX_SIZE_32 - ManagedObject.SIZE) unreachable();
step(); // also makes sure it's initialized
var obj = changetype<ManagedObject>(memory.allocate(ManagedObject.SIZE + size));
obj.visitFn = visitFn;
obj.color = white;
from.push(obj);
return objToRef(obj);
}
/** Registers a managed child object with its parent object. */
@global
export function gc_register(parent: ManagedObject, child: ManagedObject): void {
if (parent.isBlack && child.isWhite) parent.makeGray();
@global export function __gc_link(parentRef: usize, childRef: usize): void {
if (TRACE) trace("gc.link", 2, parentRef, childRef);
var parent = refToObj(parentRef);
if (parent.color == <i32>!white && refToObj(childRef).color == white) parent.makeGray();
}
/** Iterates the root set. Provided by the compiler according to the program. */
@global
export declare function gc_roots(): void;
@global export function __gc_mark(ref: usize): void {
if (TRACE) trace("gc.mark", 1, ref);
if (ref) {
let obj = refToObj(ref);
if (obj.color == white) obj.makeGray();
}
}
/** Performs a full garbage collection cycle. */
@global
export function gc_collect(): void {
@global export function __gc_collect(): void {
if (TRACE) trace("gc.collect");
// begin collecting if not yet collecting
switch (state) {
case State.INIT:
case State.IDLE: gc_step();
case State.IDLE: step();
}
// finish the cycle
while (state != State.IDLE) gc_step();
while (state != State.IDLE) step();
}
declare function allocate_memory(size: usize): usize;
declare function free_memory(ptr: usize): void;
// Considerations
//
// - An API that consists mostly of just replacing `allocate_memory` would be ideal, possibly taking
// any additional number of parameters that are necessary, like the parent and the visitor.
//
// - Not having to generate a helper function for iterating globals but instead marking specific
// nodes as roots could simplify the embedding, but whether this is feasible or not depends on its
// performance characteristics and the possibility of tracking root status accross assignments.
// For example, root status could be implemented as some sort of referenced-by-globals counting
// and a dedicated list of root objects.
//
// - In 32-bit specifically, there is some free space in TLSF object headers due to alignment that
// could be repurposed to store some GC information, like a class id. Certainly, this somewhat
// depends on the efficiency of the used mechanism to detect this at compile time, including when
// a different allocator is used.
//
// - Think about generations.

View File

@ -1,7 +1,16 @@
/** Environment abort function called where assertions evaluate to false / on throw. */
declare function abort(
message?: string | null,
fileName?: string | null,
lineNumber?: u32,
columnNumber?: u32
): void;
declare function trace(
message: string,
n?: i32,
a0?: f64,
a1?: f64,
a2?: f64,
a3?: f64,
a4?: f64
): void;

28
std/assembly/gc.ts Normal file
View File

@ -0,0 +1,28 @@
@builtin export declare function iterateRoots(fn: (ref: usize) => void): void; // tslint:disable-line
export namespace gc {
export function allocate(size: usize, visitFn: (ref: usize) => void): usize {
if (isDefined(__gc_allocate)) return __gc_allocate(size, visitFn); // tslint:disable-line
WARNING("Calling 'gc.allocate' requires a garbage collector to be present.");
return <usize>unreachable();
}
export function collect(): void {
if (isDefined(__gc_collect)) { __gc_collect(); return; } // tslint:disable-line
WARNING("Calling 'gc.collect' requires a garbage collector to be present.");
unreachable();
}
export function link(parentRef: usize, childRef: usize): void {
if (isDefined(__gc_link)) { __gc_link(parentRef, childRef); return; } // tslint:disable-line
WARNING("Calling 'gc.link' requires a garbage collector to be present.");
unreachable();
}
export function mark(ref: usize): void {
if (isDefined(__gc_mark)) { __gc_mark(ref); return; } // tslint:disable-line
WARNING("Calling 'gc.mark' requires a garbage collector to be present.");
unreachable();
}
}

View File

@ -34,6 +34,8 @@ declare type f32 = number;
/** A 64-bit float. */
declare type f64 = number;
// Compiler hints
/** Compiler target. 0 = JS, 1 = WASM32, 2 = WASM64. */
declare const ASC_TARGET: i32;
/** Provided noTreeshaking option. */
@ -51,6 +53,100 @@ declare const ASC_FEATURE_MUTABLE_GLOBAL: bool;
/** Whether the sign extension feature is enabled. */
declare const ASC_FEATURE_SIGN_EXTENSION: bool;
// Builtins
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
declare function clz<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered trailing if the value is zero. */
declare function ctz<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic count number of one bits operation on a 32-bit or 64-bit integer. */
declare function popcnt<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic rotate left operation on a 32-bit or 64-bit integer. */
declare function rotl<T = i32 | i64>(value: T, shift: T): T;
/** Performs the sign-agnostic rotate right operation on a 32-bit or 64-bit integer. */
declare function rotr<T = i32 | i64>(value: T, shift: T): T;
/** Computes the absolute value of an integer or float. */
declare function abs<T = i32 | i64 | f32 | f64>(value: T): T;
/** Determines the maximum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function max<T = i32 | i64 | f32 | f64>(left: T, right: T): T;
/** Determines the minimum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function min<T = i32 | i64 | f32 | f64>(left: T, right: T): T;
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
declare function ceil<T = f32 | f64>(value: T): T;
/** Composes a 32-bit or 64-bit float from the magnitude of `x` and the sign of `y`. */
declare function copysign<T = f32 | f64>(x: T, y: T): T;
/** Performs the floor operation on a 32-bit or 64-bit float. */
declare function floor<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer tied to even of a 32-bit or 64-bit float. */
declare function nearest<T = f32 | f64>(value: T): T;
/** Reinterprets the bits of the specified value as type `T`. Valid reinterpretations are u32/i32 to/from f32 and u64/i64 to/from f64. */
declare function reinterpret<T = i32 | i64 | f32 | f64>(value: number): T;
/** Selects one of two pre-evaluated values depending on the condition. */
declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
/** Calculates the square root of a 32-bit or 64-bit float. */
declare function sqrt<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
declare function trunc<T = f32 | f64>(value: T): T;
/** Loads a value of the specified type from memory. Equivalent to dereferncing a pointer in other languages. */
declare function load<T>(ptr: usize, constantOffset?: usize): T;
/** Stores a value of the specified type to memory. Equivalent to dereferencing a pointer in other languages when assigning a value. */
declare function store<T>(ptr: usize, value: any, constantOffset?: usize): void;
/** Emits an unreachable operation that results in a runtime error when executed. Both a statement and an expression of any type. */
declare function unreachable(): any; // sic
/** NaN (not a number) as a 32-bit or 64-bit float depending on context. */
declare const NaN: f32 | f64;
/** Positive infinity as a 32-bit or 64-bit float depending on context. */
declare const Infinity: f32 | f64;
/** Heap base offset. */
declare const HEAP_BASE: usize;
/** Determines the byte size of the specified underlying core type. Compiles to a constant. */
declare function sizeof<T>(): usize;
/** Determines the alignment (log2) of the specified underlying core type. Compiles to a constant. */
declare function alignof<T>(): usize;
/** Determines the offset of the specified field within the given class type. Returns the class type's end offset if field name has been omitted. Compiles to a constant. */
declare function offsetof<T>(fieldName?: string): usize;
/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/
declare function changetype<T>(value: any): T;
/** Explicitly requests no bounds checks on the provided expression. Useful for array accesses. */
declare function unchecked<T>(value: T): T;
/** Emits a `call_indirect` instruction, calling the specified function in the function table by index with the specified arguments. Does result in a runtime error if the arguments do not match the called function. */
declare function call_indirect<T>(target: Function | u32, ...args: any[]): T;
/** Tests if a 32-bit or 64-bit float is `NaN`. */
declare function isNaN<T = f32 | f64>(value: T): bool;
/** Tests if a 32-bit or 64-bit float is finite, that is not `NaN` or +/-`Infinity`. */
declare function isFinite<T = f32 | f64>(value: T): bool;
/** Tests if the specified type *or* expression is of an integer type and not a reference. Compiles to a constant. */
declare function isInteger<T>(value?: any): value is number;
/** Tests if the specified type *or* expression is of a float type. Compiles to a constant. */
declare function isFloat<T>(value?: any): value is number;
/** Tests if the specified type *or* expression can represent negative numbers. Compiles to a constant. */
declare function isSigned<T>(value?: any): value is number;
/** Tests if the specified type *or* expression is of a reference type. Compiles to a constant. */
declare function isReference<T>(value?: any): value is object | string;
/** Tests if the specified type *or* expression can be used as a string. Compiles to a constant. */
declare function isString<T>(value?: any): value is string | String;
/** Tests if the specified type *or* expression can be used as an array. Compiles to a constant. */
declare function isArray<T>(value?: any): value is Array<any>;
/** Tests if the specified expression resolves to a defined element. Compiles to a constant. */
declare function isDefined(expression: any): bool;
/** Tests if the specified expression evaluates to a constant value. Compiles to a constant. */
declare function isConstant(expression: any): bool;
/** Traps if the specified value is not true-ish, otherwise returns the (non-nullable) value. */
declare function assert<T>(isTrueish: T, message?: string): T & object; // any better way to model `: T != null`?
/** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64;
/** Parses an integer string to a 32-bit integer. */
declare function parseI32(str: string, radix?: i32): i32;
/** Parses an integer string to a 64-bit integer. */
declare function parseI64(str: string, radix?: i32): i64;
/** Parses a string to a 64-bit float. */
declare function parseFloat(str: string): f64;
/** Returns the 64-bit floating-point remainder of `x/y`. */
declare function fmod(x: f64, y: f64): f64;
/** Returns the 32-bit floating-point remainder of `x/y`. */
declare function fmodf(x: f32, y: f32): f32;
/** Converts any other numeric value to an 8-bit signed integer. */
declare function i8(value: i8 | i16 | i32 | i64 | isize | u8 | u16 | u32 | u64 | usize | bool | f32 | f64): i8;
declare namespace i8 {
@ -210,121 +306,70 @@ declare namespace f64 {
export function store(offset: usize, value: f64, constantOffset?: usize): void;
}
// Built-ins
// User-defined diagnostic macros
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
declare function clz<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered trailing if the value is zero. */
declare function ctz<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic count number of one bits operation on a 32-bit or 64-bit integer. */
declare function popcnt<T = i32 | i64>(value: T): T;
/** Performs the sign-agnostic rotate left operation on a 32-bit or 64-bit integer. */
declare function rotl<T = i32 | i64>(value: T, shift: T): T;
/** Performs the sign-agnostic rotate right operation on a 32-bit or 64-bit integer. */
declare function rotr<T = i32 | i64>(value: T, shift: T): T;
/** Computes the absolute value of an integer or float. */
declare function abs<T = i32 | i64 | f32 | f64>(value: T): T;
/** Determines the maximum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function max<T = i32 | i64 | f32 | f64>(left: T, right: T): T;
/** Determines the minimum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function min<T = i32 | i64 | f32 | f64>(left: T, right: T): T;
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
declare function ceil<T = f32 | f64>(value: T): T;
/** Composes a 32-bit or 64-bit float from the magnitude of `x` and the sign of `y`. */
declare function copysign<T = f32 | f64>(x: T, y: T): T;
/** Performs the floor operation on a 32-bit or 64-bit float. */
declare function floor<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer tied to even of a 32-bit or 64-bit float. */
declare function nearest<T = f32 | f64>(value: T): T;
/** Reinterprets the bits of the specified value as type `T`. Valid reinterpretations are u32/i32 to/from f32 and u64/i64 to/from f64. */
declare function reinterpret<T = i32 | i64 | f32 | f64>(value: number): T;
/** Selects one of two pre-evaluated values depending on the condition. */
declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
/** Calculates the square root of a 32-bit or 64-bit float. */
declare function sqrt<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
declare function trunc<T = f32 | f64>(value: T): T;
/** Loads a value of the specified type from memory. Equivalent to dereferncing a pointer in other languages. */
declare function load<T>(ptr: usize, constantOffset?: usize): T;
/** Stores a value of the specified type to memory. Equivalent to dereferencing a pointer in other languages when assigning a value. */
declare function store<T>(ptr: usize, value: any, constantOffset?: usize): void;
/** Returns the current memory size in units of pages. One page is 64kb. */
declare function current_memory(): i32;
/** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */
declare function grow_memory(value: i32): i32;
/** Copies n bytes from the specified source to the specified destination in memory. These regions may overlap. */
declare function move_memory(destination: usize, source: usize, n: usize): void;
/** Sets n bytes beginning at the specified destination in memory to the specified byte value. */
declare function set_memory(destination: usize, value: u8, count: usize): void;
/** Compares two chunks of memory. Returns `0` if equal, otherwise the difference of the first differing bytes. */
declare function compare_memory(vl: usize, vr: usize, n: usize): i32;
/** Allocates a chunk of memory of the specified size and returns a pointer to it. */
declare function allocate_memory(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
declare function free_memory(ptr: usize): void;
/** Emits an unreachable operation that results in a runtime error when executed. Both a statement and an expression of any type. */
declare function unreachable(): any; // sic
/** Emits a user-defined diagnostic error when encountered. */
declare function ERROR(message?: any): void;
/** Emits a user-defined diagnostic warning when encountered. */
declare function WARNING(message?: any): void;
/** Emits a user-defined diagnostic info when encountered. */
declare function INFO(message?: any): void;
/** [Polyfill] Performs the sign-agnostic reverse bytes **/
// Polyfills
/** Performs the sign-agnostic reverse bytes **/
declare function bswap<T = i8 | u8 | i16 | u16 | i32 | u32 | i64 | u64 | isize | usize>(value: T): T;
/** [Polyfill] Performs the sign-agnostic reverse bytes only for last 16-bit **/
/** Performs the sign-agnostic reverse bytes only for last 16-bit **/
declare function bswap16<T = i8 | u8 | i16 | u16 | i32 | u32>(value: T): T;
/** NaN (not a number) as a 32-bit or 64-bit float depending on context. */
declare const NaN: f32 | f64;
/** Positive infinity as a 32-bit or 64-bit float depending on context. */
declare const Infinity: f32 | f64;
/** Heap base offset. */
declare const HEAP_BASE: usize;
/** Determines the byte size of the specified underlying core type. Compiles to a constant. */
declare function sizeof<T>(): usize;
/** Determines the alignment (log2) of the specified underlying core type. Compiles to a constant. */
declare function alignof<T>(): usize;
/** Determines the offset of the specified field within the given class type. Returns the class type's end offset if field name has been omitted. Compiles to a constant. */
declare function offsetof<T>(fieldName?: string): usize;
/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/
declare function changetype<T>(value: any): T;
/** Explicitly requests no bounds checks on the provided expression. Useful for array accesses. */
declare function unchecked<T>(value: T): T;
/** Emits a `call_indirect` instruction, calling the specified function in the function table by index with the specified arguments. Does result in a runtime error if the arguments do not match the called function. */
declare function call_indirect<T>(target: Function | u32, ...args: any[]): T;
/** Tests if a 32-bit or 64-bit float is `NaN`. */
declare function isNaN<T = f32 | f64>(value: T): bool;
/** Tests if a 32-bit or 64-bit float is finite, that is not `NaN` or +/-`Infinity`. */
declare function isFinite<T = f32 | f64>(value: T): bool;
/** Tests if the specified type *or* expression is of an integer type and not a reference. Compiles to a constant. */
declare function isInteger<T>(value?: any): value is number;
/** Tests if the specified type *or* expression is of a float type. Compiles to a constant. */
declare function isFloat<T>(value?: any): value is number;
/** Tests if the specified type *or* expression can represent negative numbers. Compiles to a constant. */
declare function isSigned<T>(value?: any): value is number;
/** Tests if the specified type *or* expression is of a reference type. Compiles to a constant. */
declare function isReference<T>(value?: any): value is object | string;
/** Tests if the specified type *or* expression can be used as a string. Compiles to a constant. */
declare function isString<T>(value?: any): value is string | String;
/** Tests if the specified type *or* expression can be used as an array. Compiles to a constant. */
declare function isArray<T>(value?: any): value is Array<any>;
/** Tests if the specified expression resolves to a defined element. Compiles to a constant. */
declare function isDefined(expression: any): bool;
/** Tests if the specified expression evaluates to a constant value. Compiles to a constant. */
declare function isConstant(expression: any): bool;
/** Traps if the specified value is not true-ish, otherwise returns the (non-nullable) value. */
declare function assert<T>(isTrueish: T, message?: string): T & object; // any better way to model `: T != null`?
/** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64;
/** Parses an integer string to a 32-bit integer. */
declare function parseI32(str: string, radix?: i32): i32;
/** Parses an integer string to a 64-bit integer. */
declare function parseI64(str: string, radix?: i32): i64;
/** Parses a string to a 64-bit float. */
declare function parseFloat(str: string): f64;
/** Returns the 64-bit floating-point remainder of `x/y`. */
declare function fmod(x: f64, y: f64): f64;
/** Returns the 32-bit floating-point remainder of `x/y`. */
declare function fmodf(x: f32, y: f32): f32;
// Standard library
/** Memory operations. */
declare namespace memory {
/** Returns the current memory size in units of pages. One page is 64kb. */
export function size(): i32;
/** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */
export function grow(value: i32): i32;
/** Sets n bytes beginning at the specified destination in memory to the specified byte value. */
export function fill(dst: usize, value: u8, count: usize): void;
/** Copies n bytes from the specified source to the specified destination in memory. These regions may overlap. */
export function copy(dst: usize, src: usize, n: usize): void;
/** Copies elements from a passive element segment to a table. */
// export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void;
/** Prevents further use of a passive element segment. */
// export function drop(segmentIndex: u32): void;
/** Copies elements from one region of a table to another region. */
export function allocate(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
export function free(ptr: usize): void;
/** Compares two chunks of memory. Returns `0` if equal, otherwise the difference of the first differing bytes. */
export function compare(vl: usize, vr: usize, n: usize): i32;
/** Resets the allocator to its initial state, if supported. */
export function reset(): void;
}
/** Garbage collector operations. */
declare namespace gc {
/** Allocates a managed object identified by its visitor function. */
export function allocate(size: usize, visitFn: (ref: usize) => void): usize;
/** Performs a full garbage collection cycle. */
export function collect(): void;
/** Must be called when a managed object becomes a child of another one. */
export function link(parentRef: usize, childRef: usize): void;
/** Must be called when a managed object is found reachable. */
export function mark(ref: usize): void;
}
/** Table operations. */
declare namespace table {
/** Copies elements from a passive element segment to a table. */
// export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void;
/** Prevents further use of a passive element segment. */
// export function drop(elementIndex: u32): void;
/** Copies elements from one region of a table to another region. */
// export function copy(dest: u32, src: u32, n: u32): void;
}
/** Class representing a generic, fixed-length raw binary data buffer. */
declare class ArrayBuffer {
/** The size, in bytes, of the array. */
@ -607,7 +652,10 @@ declare const Math: IMath<f64>;
/** Alias of {@link NativeMathf} or {@link JSMath} respectively. Defaults to `NativeMathf`. */
declare const Mathf: IMath<f32>;
// Internal decorators
/** Environmental tracing function for debugging purposes. */
declare function trace(msg: string, n?: i32, a0?: f64, a1?: f64, a2?: f64, a3?: f64, a4?: f64): void;
// Decorators
/** Annotates an element as a program global. */
declare function global(target: Function, propertyKey: string, descriptor: any): void;

View File

@ -36,8 +36,8 @@ export function weakHeapSort<T>(arr: Array<T>, comparator: (a: T, b: T) => i32):
var length = arr.length;
var bitsetSize = (length + 31) >> 5 << shift32;
var bitset = allocate_memory(bitsetSize); // indexed in 32-bit chunks below
set_memory(bitset, 0, bitsetSize);
var bitset = memory.allocate(bitsetSize); // indexed in 32-bit chunks below
memory.fill(bitset, 0, bitsetSize);
// see: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.21.1863&rep=rep1&type=pdf
@ -83,7 +83,7 @@ export function weakHeapSort<T>(arr: Array<T>, comparator: (a: T, b: T) => i32):
}
}
free_memory(bitset);
memory.free(bitset);
var t = loadUnsafe<T,T>(buffer, 1); // t = arr[1]
storeUnsafe<T,T>(buffer, 1, loadUnsafe<T,T>(buffer, 0)); // arr[1] = arr[0]

View File

@ -20,7 +20,7 @@ export function computeSize(byteLength: i32): usize {
/** Allocates a raw ArrayBuffer. Contents remain uninitialized. */
export function allocUnsafe(byteLength: i32): ArrayBuffer {
assert(<u32>byteLength <= <u32>MAX_BLENGTH);
var buffer = allocate_memory(computeSize(byteLength));
var buffer = memory.allocate(computeSize(byteLength));
store<i32>(buffer, byteLength, offsetof<ArrayBuffer>("byteLength"));
return changetype<ArrayBuffer>(buffer);
}
@ -32,19 +32,19 @@ export function reallocUnsafe(buffer: ArrayBuffer, newByteLength: i32): ArrayBuf
assert(newByteLength <= MAX_BLENGTH);
if (newByteLength <= <i32>(computeSize(oldByteLength) - HEADER_SIZE)) { // fast path: zero out additional space
store<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("byteLength"));
set_memory(
memory.fill(
changetype<usize>(buffer) + HEADER_SIZE + <usize>oldByteLength,
0,
<usize>(newByteLength - oldByteLength)
);
} else { // slow path: copy to new buffer
let newBuffer = allocUnsafe(newByteLength);
move_memory(
memory.copy(
changetype<usize>(newBuffer) + HEADER_SIZE,
changetype<usize>(buffer) + HEADER_SIZE,
<usize>oldByteLength
);
set_memory(
memory.fill(
changetype<usize>(newBuffer) + HEADER_SIZE + <usize>oldByteLength,
0,
<usize>(newByteLength - oldByteLength)

View File

@ -23,7 +23,7 @@ export function clamp<T>(val: T, lo: T, hi: T): T {
/** Allocates a raw String with uninitialized contents. */
export function allocate(length: i32): String {
assert(length > 0 && length <= MAX_LENGTH);
var buffer = allocate_memory(HEADER_SIZE + (<usize>length << 1));
var buffer = memory.allocate(HEADER_SIZE + (<usize>length << 1));
store<i32>(buffer, length);
return changetype<String>(buffer);
}

View File

@ -18,7 +18,7 @@ export abstract class TypedArray<T,V> {
if (<u32>length > MAX_LENGTH) throw new RangeError("Invalid typed array length");
var byteLength = length << alignof<T>();
var buffer = allocUnsafe(byteLength);
set_memory(changetype<usize>(buffer) + HEADER_SIZE_AB, 0, <usize>byteLength);
memory.fill(changetype<usize>(buffer) + HEADER_SIZE_AB, 0, <usize>byteLength);
this.buffer = buffer;
this.byteOffset = 0;
this.byteLength = byteLength;
@ -64,7 +64,7 @@ export abstract class TypedArray<T,V> {
else begin = min(begin, length);
if (end < 0) end = max(length + end, begin);
else end = max(min(end, length), begin);
var slice = allocate_memory(offsetof<this>());
var slice = memory.allocate(offsetof<this>());
store<usize>(slice, this.buffer, offsetof<this>("buffer"));
store<i32>(slice, begin << alignof<T>(), offsetof<this>("byteOffset"));
store<i32>(slice, end << alignof<T>(), offsetof<this>("byteLength"));

View File

@ -1,7 +1,56 @@
function copy_memory(dest: usize, src: usize, n: usize): void {
// based on musl's implementation of memcpy
// not a future instruction and sufficiently covered by the upcoming move_memory intrinsic
export namespace memory {
@builtin export declare function size(): i32; // tslint:disable-line
@builtin export declare function grow(pages: i32): i32; // tslint:disable-line
export function fill(dest: usize, c: u8, n: usize): void { // see: musl/src/string/memset
if (isDefined(__memory_fill)) { __memory_fill(dest, c, n); return; } // tslint:disable-line
memset(dest, c, n);
}
export function copy(dest: usize, src: usize, n: usize): void { // see: musl/src/string/memmove.c
if (isDefined(__memory_copy)) { __memory_copy(dest, src, n); return; } // tslint:disable-line
memmove(dest, src, n);
}
export function compare(vl: usize, vr: usize, n: usize): i32 { // see: musl/src/string/memcmp.c
if (isDefined(__memory_compare)) return __memory_compare(vl, vr, n); // tslint:disable-line
return memcmp(vl, vr, n);
}
// Passive segments
// export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void {
// __memory_init(segmentIndex, srcOffset, dstOffset);
// }
// export function drop(segmentIndex: u32): void {
// __memory_drop(segmentIndex);
// }
// Allocator
export function allocate(size: usize): usize {
if (isDefined(__memory_allocate)) return __memory_allocate(size); // tslint:disable-line
WARNING("Calling 'memory.allocate' requires a memory manager to be present.");
return <usize>unreachable();
}
export function free(ptr: usize): void {
if (isDefined(__memory_free)) { __memory_free(ptr); return; } // tslint:disable-line
WARNING("Calling 'memory.free' requires a memory manager to be present.");
unreachable();
}
export function reset(): void {
if (isDefined(__memory_reset)) { __memory_reset(); return; } // tslint:disable-line
unreachable();
}
}
// this function will go away once `memory.copy` becomes an intrinsic
function memcpy(dest: usize, src: usize, n: usize): void { // see: musl/src/string/memcpy.c
var w: u32, x: u32;
// copy 1 byte each until src is aligned to 4 bytes
@ -144,13 +193,11 @@ function copy_memory(dest: usize, src: usize, n: usize): void {
}
}
export function move_memory(dest: usize, src: usize, n: usize): void {
// based on musl's implementation of memmove
// becomes obsolete once https://github.com/WebAssembly/bulk-memory-operations lands
// this function will go away once `memory.copy` becomes an intrinsic
function memmove(dest: usize, src: usize, n: usize): void { // see: musl/src/string/memmove.c
if (dest == src) return;
if (src + n <= dest || dest + n <= src) {
copy_memory(dest, src, n);
memcpy(dest, src, n);
return;
}
if (dest < src) {
@ -188,9 +235,8 @@ export function move_memory(dest: usize, src: usize, n: usize): void {
}
}
export function set_memory(dest: usize, c: u8, n: usize): void {
// based on musl's implementation of memset
// becomes obsolete once https://github.com/WebAssembly/bulk-memory-operations lands
// this function will go away once `memory.fill` becomes an intrinsic
function memset(dest: usize, c: u8, n: usize): void { // see: musl/src/string/memset
// fill head and tail with minimal branching
if (!n) return;
@ -250,14 +296,10 @@ export function set_memory(dest: usize, c: u8, n: usize): void {
}
}
export function compare_memory(vl: usize, vr: usize, n: usize): i32 {
// based on musl's implementation of memcmp
// provided because there's no proposed alternative
function memcmp(vl: usize, vr: usize, n: usize): i32 { // see: musl/src/string/memcmp.c
if (vl == vr) return 0;
while (n && load<u8>(vl) == load<u8>(vr)) {
n--;
vl++;
vr++;
while (n != 0 && load<u8>(vl) == load<u8>(vr)) {
n--; vl++; vr++;
}
return n ? <i32>load<u8>(vl) - <i32>load<u8>(vr) : 0;
}

View File

@ -90,13 +90,13 @@ export class String {
if (outLen == 0) return EMPTY;
var out = allocate(outLen);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE,
thisLen << 1
);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE + (thisLen << 1),
changetype<usize>(other) + HEADER_SIZE,
otherLen << 1
@ -112,7 +112,7 @@ export class String {
var searchLength: isize = searchString.length;
var start: isize = end - searchLength;
if (start < 0) return false;
return !compare_memory(
return !memory.compare(
changetype<usize>(this) + HEADER_SIZE + (start << 1),
changetype<usize>(searchString) + HEADER_SIZE,
searchLength << 1
@ -127,7 +127,7 @@ export class String {
var leftLength = left.length;
if (leftLength != right.length) return false;
return !compare_memory(
return !memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
(<usize>leftLength << 1)
@ -150,7 +150,7 @@ export class String {
if (!rightLength) return true;
var length = <usize>min<i32>(leftLength, rightLength);
return compare_memory(
return memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
length << 1
@ -169,7 +169,7 @@ export class String {
if (!rightLength) return true;
var length = <usize>min<i32>(leftLength, rightLength);
return compare_memory(
return memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
length << 1
@ -187,7 +187,7 @@ export class String {
if (!leftLength) return true;
var length = <usize>min<i32>(leftLength, rightLength);
return compare_memory(
return memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
length << 1
@ -206,7 +206,7 @@ export class String {
if (!leftLength) return true;
var length = <usize>min<i32>(leftLength, rightLength);
return compare_memory(
return memory.compare(
changetype<usize>(left) + HEADER_SIZE,
changetype<usize>(right) + HEADER_SIZE,
length << 1
@ -228,7 +228,7 @@ export class String {
len -= searchLen;
// TODO: multiple char codes
for (let k: isize = start; k <= len; ++k) {
if (!compare_memory(
if (!memory.compare(
changetype<usize>(this) + HEADER_SIZE + (k << 1),
changetype<usize>(searchString) + HEADER_SIZE,
searchLen << 1
@ -250,7 +250,7 @@ export class String {
// TODO: multiple char codes
for (let k = start; k >= 0; --k) {
if (!compare_memory(
if (!memory.compare(
changetype<usize>(this) + HEADER_SIZE + (k << 1),
changetype<usize>(searchString) + HEADER_SIZE,
searchLen << 1
@ -272,7 +272,7 @@ export class String {
if (searchLength + start > len) {
return false;
}
return !compare_memory(
return !memory.compare(
changetype<usize>(this) + HEADER_SIZE + (start << 1),
changetype<usize>(searchString) + HEADER_SIZE,
searchLength << 1
@ -292,7 +292,7 @@ export class String {
return EMPTY;
}
var out = allocate(resultLength);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE + (intStart << 1),
<usize>resultLength << 1
@ -315,7 +315,7 @@ export class String {
return this;
}
var out = allocate(len);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE + (from << 1),
len << 1
@ -351,7 +351,7 @@ export class String {
return this;
}
var out = allocate(length);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE + (start << 1),
length << 1
@ -379,7 +379,7 @@ export class String {
return EMPTY;
}
var out = allocate(outLen);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE + (start << 1),
outLen << 1
@ -405,7 +405,7 @@ export class String {
return this;
}
var out = allocate(len);
move_memory(
memory.copy(
changetype<usize>(out) + HEADER_SIZE,
changetype<usize>(this) + HEADER_SIZE,
len << 1
@ -433,7 +433,7 @@ export class String {
* 'a' + 'a' => 'aa' + 'aa' => 'aaaa' + 'aaaa' etc
*/
for (let offset = 0, len = strLen * count; offset < len; offset += strLen) {
move_memory(
memory.copy(
changetype<usize>(result) + HEADER_SIZE + offset,
changetype<usize>(this) + HEADER_SIZE,
strLen
@ -472,7 +472,7 @@ export class String {
}
toUTF8(): usize {
var buf = allocate_memory(<usize>this.lengthUTF8);
var buf = memory.allocate(<usize>this.lengthUTF8);
var pos: usize = 0;
var end = <usize>this.length;
var off: usize = 0;

16
std/assembly/table.ts Normal file
View File

@ -0,0 +1,16 @@
export namespace table {
// export function copy(dst: u32, src: u32, n: u32): void {
// __table_copy(dst, src, n);
// }
// Passive elements
// export function init(elementIndex: u32, srcOffset: u32, dstOffset: u32, n: u32): void {
// __table_init(elementIndex, srcOffset, dstOffset, n);
// }
// export function drop(elementIndex: u32): void {
// __table_drop(elementIndex);
// }
}

View File

@ -14,7 +14,7 @@
/// <reference no-default-lib="true"/>
// Portable types
// Types
declare type i8 = number;
declare type i16 = number;
@ -28,9 +28,85 @@ declare type usize = number;
declare type f32 = number;
declare type f64 = number;
// Compiler hints
/** Compiler target. 0 = JS, 1 = WASM32, 2 = WASM64. */
declare const ASC_TARGET: i32;
// Builtins
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit integer. All zero bits are considered leading if the value is zero. */
declare function clz<T = i32>(value: T): T;
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit integer. All zero bits are considered trailing if the value is zero. */
declare function ctz<T = i32>(value: T): T;
/** Performs the sign-agnostic count number of one bits operation on a 32-bit integer. */
declare function popcnt<T = i32>(value: T): T;
/** Performs the sign-agnostic rotate left operation on a 32-bit integer. */
declare function rotl<T = i32>(value: T, shift: T): T;
/** Performs the sign-agnostic rotate right operation on a 32-bit integer. */
declare function rotr<T = i32>(value: T, shift: T): T;
/** Computes the absolute value of an integer or float. */
declare function abs<T = i32 | f32 | f64>(value: T): T;
/** Determines the maximum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function max<T = i32 | f32 | f64>(left: T, right: T): T;
/** Determines the minimum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function min<T = i32 | f32 | f64>(left: T, right: T): T;
/** Composes a 32-bit or 64-bit float from the magnitude of `x` and the sign of `y`. */
declare function copysign<T = f32 | f64>(x: T, y: T): T;
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
declare function ceil<T = f32 | f64>(value: T): T;
/** Performs the floor operation on a 32-bit or 64-bit float. */
declare function floor<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer tied to even of a 32-bit or 64-bit float. */
declare function nearest<T = f32 | f64>(value: T): T;
/** Selects one of two pre-evaluated values depending on the condition. */
declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
/** Calculates the square root of a 32-bit or 64-bit float. */
declare function sqrt<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
declare function trunc<T = f32 | f64>(value: T): T;
/** Loads a value of the specified type from memory. Type must be `u8`. */
declare function load<T = u8>(ptr: usize, constantOffset?: usize): T;
/** Stores a value of the specified type to memory. Type must be `u8`. */
declare function store<T = u8>(ptr: usize, value: T, constantOffset?: usize): void;
/** Emits an unreachable operation that results in a runtime error when executed. */
declare function unreachable(): any; // sic
/** NaN (not a number) as a 32-bit or 64-bit float depending on context. */
declare const NaN: f32 | f64;
/** Positive infinity as a 32-bit or 64-bit float depending on context. */
declare const Infinity: f32 | f64;
/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/
declare function changetype<T>(value: any): T;
/** Explicitly requests no bounds checks on the provided expression. Useful for array accesses. */
declare function unchecked<T>(value: T): T;
/** Tests if a 32-bit or 64-bit float is `NaN`. */
declare function isNaN<T = f32 | f64>(value: T): bool;
/** Tests if a 32-bit or 64-bit float is finite, that is not `NaN` or +/-`Infinity`. */
declare function isFinite<T = f32 | f64>(value: T): bool;
/** Tests if the specified value is a valid integer. Can't distinguish an integer from an integral float. */
declare function isInteger(value: any): value is number;
/** Tests if the specified value is a valid float. Can't distinguish a float from an integer. */
declare function isFloat(value: any): value is number;
/** Tests if the specified value is of a reference type. */
declare function isReference(value: any): value is object | string;
/** Tests if the specified value can be used as a string. */
declare function isString(value: any): value is string | String;
/** Tests if the specified value can be used as an array. */
declare function isArray(value: any): value is Array<any>;
/** Traps if the specified value is not true-ish, otherwise returns the value. */
declare function assert<T>(isTrueish: T | null, message?: string): T;
/** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64;
/** Parses an integer string to a 32-bit integer. */
declare function parseI32(str: string, radix?: i32): i32;
/** Parses a floating point string to a 64-bit float. */
declare function parseFloat(str: string): f64;
/** Returns the 64-bit floating-point remainder of `x/y`. */
declare function fmod(x: f64, y: f64): f64;
/** Returns the 32-bit floating-point remainder of `x/y`. */
declare function fmodf(x: f32, y: f32): f32;
/** Converts any other numeric value to an 8-bit signed integer. */
declare function i8(value: i8 | i16 | i32 | isize | u8 | u16 | u32 | usize | bool | f32 | f64): i8;
declare namespace i8 {
@ -136,92 +212,26 @@ declare namespace f64 {
export const EPSILON: f64;
}
// Portable built-ins
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit integer. All zero bits are considered leading if the value is zero. */
declare function clz<T = i32>(value: T): T;
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit integer. All zero bits are considered trailing if the value is zero. */
declare function ctz<T = i32>(value: T): T;
/** Performs the sign-agnostic count number of one bits operation on a 32-bit integer. */
declare function popcnt<T = i32>(value: T): T;
/** Performs the sign-agnostic rotate left operation on a 32-bit integer. */
declare function rotl<T = i32>(value: T, shift: T): T;
/** Performs the sign-agnostic rotate right operation on a 32-bit integer. */
declare function rotr<T = i32>(value: T, shift: T): T;
/** Computes the absolute value of an integer or float. */
declare function abs<T = i32 | f32 | f64>(value: T): T;
/** Determines the maximum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function max<T = i32 | f32 | f64>(left: T, right: T): T;
/** Determines the minimum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
declare function min<T = i32 | f32 | f64>(left: T, right: T): T;
/** Composes a 32-bit or 64-bit float from the magnitude of `x` and the sign of `y`. */
declare function copysign<T = f32 | f64>(x: T, y: T): T;
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
declare function ceil<T = f32 | f64>(value: T): T;
/** Performs the floor operation on a 32-bit or 64-bit float. */
declare function floor<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer tied to even of a 32-bit or 64-bit float. */
declare function nearest<T = f32 | f64>(value: T): T;
/** Selects one of two pre-evaluated values depending on the condition. */
declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
/** Calculates the square root of a 32-bit or 64-bit float. */
declare function sqrt<T = f32 | f64>(value: T): T;
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
declare function trunc<T = f32 | f64>(value: T): T;
/** Allocates a chunk of memory of the specified size and returns a pointer to it. */
declare function allocate_memory(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
declare function free_memory(ptr: usize): void;
/** Copies n bytes from the specified source to the specified destination in memory. These regions may overlap. */
declare function move_memory(destination: usize, source: usize, n: usize): void;
/** Loads a value of the specified type from memory. Type must be `u8`. */
declare function load<T = u8>(ptr: usize, constantOffset?: usize): T;
/** Stores a value of the specified type to memory. Type must be `u8`. */
declare function store<T = u8>(ptr: usize, value: T, constantOffset?: usize): void;
/** Emits an unreachable operation that results in a runtime error when executed. */
declare function unreachable(): any; // sic
// Polyfills
/** [Polyfill] Performs the sign-agnostic reverse bytes **/
declare function bswap<T = i32 | u32 | isize | usize>(value: T): T;
/** [Polyfill] Performs the sign-agnostic reverse bytes only for last 16-bit **/
declare function bswap16<T = i16 | u16 | i32 | u32>(value: T): T;
/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/
declare function changetype<T>(value: any): T;
/** Explicitly requests no bounds checks on the provided expression. Useful for array accesses. */
declare function unchecked<T>(value: T): T;
/** Tests if a 32-bit or 64-bit float is `NaN`. */
declare function isNaN<T = f32 | f64>(value: T): bool;
/** Tests if a 32-bit or 64-bit float is finite, that is not `NaN` or +/-`Infinity`. */
declare function isFinite<T = f32 | f64>(value: T): bool;
/** Tests if the specified value is a valid integer. Can't distinguish an integer from an integral float. */
declare function isInteger(value: any): value is number;
/** Tests if the specified value is a valid float. Can't distinguish a float from an integer. */
declare function isFloat(value: any): value is number;
/** Tests if the specified value is of a reference type. */
declare function isReference(value: any): value is object | string;
/** Tests if the specified value can be used as a string. */
declare function isString(value: any): value is string | String;
/** Tests if the specified value can be used as an array. */
declare function isArray(value: any): value is Array<any>;
/** Traps if the specified value is not true-ish, otherwise returns the value. */
declare function assert<T>(isTrueish: T | null, message?: string): T;
/** Parses an integer string to a 64-bit float. */
declare function parseInt(str: string, radix?: i32): f64;
/** Parses an integer string to a 32-bit integer. */
declare function parseI32(str: string, radix?: i32): i32;
/** Parses a floating point string to a 64-bit float. */
declare function parseFloat(str: string): f64;
/** Returns the 64-bit floating-point remainder of `x/y`. */
declare function fmod(x: f64, y: f64): f64;
/** Returns the 32-bit floating-point remainder of `x/y`. */
declare function fmodf(x: f32, y: f32): f32;
// Standard library
// Portable standard library
// Everything marked @deprecated is a temporary filler. Do not use.
declare const NaN: f32 | f64;
declare const Infinity: f32 | f64;
/** Memory operations. */
declare namespace memory {
/** Allocates a chunk of memory of the specified size and returns a pointer to it. */
function allocate(size: usize): usize;
/** Disposes a chunk of memory by its pointer. */
function free(ptr: usize): void;
/** Copies n bytes from the specified source to the specified destination in memory. These regions may overlap. */
function copy(dst: usize, src: usize, n: usize): void;
/** Resets the allocator to its initial state, if supported. */
function reset(): void;
}
/** Class representing a generic, fixed-length raw binary data buffer. */
declare class ArrayBuffer {

View File

@ -218,4 +218,32 @@ globalScope["fmodf"] = function fmodf(x, y) {
globalScope["JSMath"] = Math;
require("./memory")(globalScope);
globalScope["memory"] = (() => {
var HEAP = new Uint8Array(0);
var HEAP_OFFSET = 0;
return {
allocate: globalScope["__memory_allocate"] || function allocate(size) {
if (!(size >>>= 0)) return 0;
if (HEAP_OFFSET + size > HEAP.length) {
var oldHeap = HEAP;
HEAP = new Uint8Array(Math.max(65536, HEAP.length + size, HEAP.length * 2));
HEAP.set(oldHeap);
}
var ptr = HEAP_OFFSET;
if ((HEAP_OFFSET += size) & 7) HEAP_OFFSET = (HEAP_OFFSET | 7) + 1;
return ptr;
},
free: globalScope["__memory_free"] || function free(ptr) { },
copy: globalScope["__memory_copy"] || function copy(dest, src, size) {
HEAP.copyWithin(dest, src, src + size);
}
};
})();
globalScope["store"] = globalScope["__store"] || function store(ptr, value, offset) {
HEAP[ptr + (offset | 0)] = value;
};
globalScope["load"] = globalScope["__load"] || function load(ptr, offset) {
return HEAP[ptr + (offset | 0)];
};

View File

@ -1,36 +0,0 @@
module.exports = globalScope => {
var HEAP = new Uint8Array(0);
var HEAP_OFFSET = 0;
globalScope["allocate_memory"] = function allocate_memory(size) {
if (!(size >>>= 0)) return 0;
if (HEAP_OFFSET + size > HEAP.length) {
var oldHeap = HEAP;
HEAP = new Uint8Array(Math.max(65536, HEAP.length + size, HEAP.length * 2));
HEAP.set(oldHeap);
}
var ptr = HEAP_OFFSET;
if ((HEAP_OFFSET += size) & 7)
HEAP_OFFSET = (HEAP_OFFSET | 7) + 1;
return ptr;
};
globalScope["free_memory"] = function free_memory(ptr) {
// TODO
};
globalScope["move_memory"] = function move_memory(dest, src, size) {
HEAP.copyWithin(dest, src, src + size);
};
globalScope["store"] = function store(ptr, value, offset) {
HEAP[ptr + (offset | 0)] = value;
};
globalScope["load"] = function load(ptr, offset) {
return HEAP[ptr + (offset | 0)];
};
};

View File

@ -1,3 +1,2 @@
import "allocator/arena";
export { allocate_memory, free_memory, reset_memory };
// export { set_memory };
export { memory };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,2 @@
import "allocator/buddy";
export { allocate_memory, free_memory };
// export { set_memory };
export { memory };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -25,14 +25,18 @@ function test(file) {
console.log("mem final: " + exports.memory.buffer.byteLength);
console.log();
const alloc = exports["memory.allocate"];
var overflow = false;
try {
exports.allocate_memory(COMMON_MAX + 1); // unreachable
throw Error("allocation is allowed to overflow MAX_SIZE");
alloc(COMMON_MAX + 1); // unreachable
overflow = true;
} catch (e) {}
if (overflow) throw Error("allocation can overflow COMMON_MAX + 1");
try {
exports.allocate_memory(0xffffffff); // unreachable
throw Error("allocation is allowed to overflow INT_MAX");
alloc(0xffffffff); // unreachable
overflow = true;
} catch (e) {}
if (overflow) throw Error("allocation can overflow 0xffffffff");
}
if (process.argv.length > 2) {

View File

@ -1,19 +1,22 @@
function runner(allocator, runs, allocs) {
var hasReset = !!allocator.reset_memory;
var useSet = !!allocator.set_memory;
console.log("hasReset=" + hasReset + ", useSet=" + useSet);
var ptrs = [];
const useFill = false;
function runner(exports, runs, allocs) {
const alloc = exports["memory.allocate"];
const free = exports["memory.free"];
const fill = exports["memory.fill"];
const reset = exports["memory.reset"];
const ptrs = [];
function randomAlloc(maxSize) {
if (!maxSize) maxSize = 8192;
var size = ((Math.random() * maxSize) >>> 0) + 1;
size = (size + 3) & ~3;
var ptr = allocator.allocate_memory(size);
var ptr = alloc(size);
if (!ptr) throw Error();
if ((ptr & 7) != 0) throw Error("invalid alignment: " + (ptr & 7) + " on " + ptr);
if (ptrs.indexOf(ptr) >= 0) throw Error("duplicate pointer");
if (useSet)
allocator.set_memory(ptr, 0xdc, size);
if (useFill) fill(ptr, 0xdc, size);
ptrs.push(ptr);
return ptr;
}
@ -24,7 +27,7 @@ function runner(allocator, runs, allocs) {
var ptr = ptrs[idx];
ptrs.splice(idx, 1);
if (typeof ptr !== "number") throw Error();
allocator.free_memory(ptr);
free(ptr);
}
function randomFree() {
@ -32,22 +35,22 @@ function runner(allocator, runs, allocs) {
var ptr = ptrs[idx];
if (typeof ptr !== "number") throw Error();
ptrs.splice(idx, 1);
allocator.free_memory(ptr);
free(ptr);
}
// remember the smallest possible memory address
var base = allocator.allocate_memory(64);
var base = alloc(64);
console.log("base: " + base);
if (hasReset) {
allocator.reset_memory();
} else {
allocator.free_memory(base);
try {
reset();
} catch (e) {
free(base);
}
var currentMem = allocator.memory.buffer.byteLength;
var currentMem = exports.memory.buffer.byteLength;
console.log("mem initial: " + currentMem);
function testMemChanged() {
var actualMem = allocator.memory.buffer.byteLength;
var actualMem = exports.memory.buffer.byteLength;
if (actualMem > currentMem) {
console.log("mem changed: " + currentMem + " -> " + actualMem);
currentMem = actualMem;
@ -72,23 +75,20 @@ function runner(allocator, runs, allocs) {
// free the rest, randomly
while (ptrs.length) randomFree();
if (hasReset) {
allocator.reset_memory();
var ptr = allocator.allocate_memory(64);
if (ptr !== base)
throw Error("expected " + base + " but got " + ptr);
allocator.reset_memory();
} else {
try {
reset();
var ptr = alloc(64);
if (ptr !== base) throw Error("expected " + base + " but got " + ptr);
reset();
} catch (e) {
// should now be possible to reuse the entire memory
// just try a large portion of the memory here, for example because of
// SL+1 for allocations in TLSF
var size = ((allocator.memory.buffer.byteLength - base) * 9 / 10) >>> 0;
var ptr = allocator.allocate_memory(size);
if (useSet)
allocator.set_memory(ptr, 0xac, size);
if (ptr !== base)
throw Error("expected " + base + " but got " + ptr);
allocator.free_memory(ptr);
var size = ((exports.memory.buffer.byteLength - base) * 9 / 10) >>> 0;
var ptr = alloc(size);
if (useFill) fill(ptr, 0xac, size);
if (ptr !== base) throw Error("expected " + base + " but got " + ptr);
free(ptr);
}
testMemChanged();
}
@ -116,5 +116,4 @@ function mem(memory, offset, count) {
console.log(hex.join(" ") + " ...");
}
if (typeof module === "object" && typeof exports === "object")
module.exports = runner;
if (typeof module === "object" && typeof exports === "object") module.exports = runner;

View File

@ -1,3 +1,2 @@
import "allocator/tlsf";
export { allocate_memory, free_memory };
// export { set_memory };
export { memory };

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
var binaryen = require("binaryen");
var mod = new binaryen.Module();
var addType = mod.addFunctionType("iii", binaryen.i32, [ binaryen.i32, binaryen.i32 ]);
mod.addFunction("add", addType, [],
mod.i32.add(
mod.get_local(0, binaryen.i32),
mod.get_local(1, binaryen.i32)
)
);
mod.addFunctionExport("add", "add");
var testType = mod.addFunctionType("i", binaryen.i32, []);
mod.addFunction("test", testType, [],
mod.call("add", [
mod.i32.const(1),
mod.i32.const(2)
], binaryen.i32)
);
mod.addFunctionExport("test", "test");
binaryen.setOptimizeLevel(4);
binaryen.setShrinkLevel(0);
binaryen.setDebugInfo(false);
mod.runPasses(["precompute"]);
if (!mod.validate())
console.log("-> does not validate");
console.log(mod.emitText());

View File

@ -0,0 +1,19 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(type $i (func (result i32)))
(export "add" (func $add))
(export "test" (func $test))
(func $add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.add
(get_local $0)
(get_local $1)
)
)
(func $test (; 1 ;) (type $i) (result i32)
(call $add
(i32.const 1)
(i32.const 2)
)
)
)

View File

@ -0,0 +1,18 @@
var binaryen = require("binaryen");
var mod = new binaryen.Module();
mod.addGlobal("testGlobal", binaryen.i32, false, mod.i32.const(2));
var funcType = mod.addFunctionType("i", binaryen.i32, []);
mod.addFunction("test", funcType, [],
mod.get_global("testGlobal", binaryen.i32)
);
mod.addFunctionExport("test", "test");
binaryen.setOptimizeLevel(4);
binaryen.setShrinkLevel(0);
binaryen.setDebugInfo(false);
mod.optimize();
if (!mod.validate())
console.log("-> does not validate");
console.log(mod.emitText());

View File

@ -0,0 +1,8 @@
(module
(type $i (func (result i32)))
(global $testGlobal i32 (i32.const 2))
(export "test" (func $test))
(func $test (; 0 ;) (type $i) (result i32)
(get_global $testGlobal)
)
)

View File

@ -0,0 +1,18 @@
var binaryen = require("binaryen");
var mod = new binaryen.Module();
var funcType = mod.addFunctionType("i", binaryen.i32, [ binaryen.i32 ]);
mod.addFunction("test", funcType, [], mod.block("", [
mod.setLocal(0, mod.i32.const(2)),
mod.getLocal(0, binaryen.i32)
], binaryen.i32));
mod.addFunctionExport("test", "test");
binaryen.setOptimizeLevel(4);
binaryen.setShrinkLevel(0);
binaryen.setDebugInfo(false);
mod.optimize();
if (!mod.validate())
console.log("-> does not validate");
console.log(mod.emitText());

View File

@ -153,16 +153,40 @@ tests.forEach(filename => {
// Instantiate
try {
let memory = new WebAssembly.Memory({ initial: 10 });
let exports = {};
function getString(ptr) {
if (!ptr) return "null";
var U32 = new Uint32Array(exports.memory ? exports.memory.buffer : memory.buffer);
var U16 = new Uint16Array(exports.memory ? exports.memory.buffer : memory.buffer);
var dataLength = U32[ptr >>> 2];
var dataOffset = (ptr + 4) >>> 1;
var dataRemain = dataLength;
var parts = [];
const chunkSize = 1024;
while (dataRemain > chunkSize) {
let last = U16[dataOffset + chunkSize - 1];
let size = last >= 0xD800 && last < 0xDC00 ? chunkSize - 1 : chunkSize;
let part = U16.subarray(dataOffset, dataOffset += size);
parts.push(String.fromCharCode.apply(String, part));
dataRemain -= size;
}
return parts.join("") + String.fromCharCode.apply(String, U16.subarray(dataOffset, dataOffset + dataRemain));
}
let runTime = asc.measure(() => {
let exports = new WebAssembly.Instance(new WebAssembly.Module(stdout.toBuffer()), {
exports = new WebAssembly.Instance(new WebAssembly.Module(stdout.toBuffer()), {
env: {
memory,
abort: function(msg, file, line, column) {
console.log("abort called at " + line + ":" + column);
console.log(colorsUtil.red(" abort: " + getString(msg) + " at " + getString(file) + ":" + line + ":" + column));
},
trace: function(msg, n) {
console.log(" " + getString(msg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", "));
},
externalFunction: function() { },
externalConstant: 1,
logi: function(i) { console.log("logi: " + i); },
logf: function(f) { console.log("logf: " + f); }
externalConstant: 1
},
JSOp: {
mod: function(a, b) { return a % b; }
@ -188,10 +212,16 @@ tests.forEach(filename => {
bar: function() {},
baz: function() {},
"var": 3
},
});
}
}).exports;
if (exports.main) {
console.log(colorsUtil.white(" [main]"));
var code = exports.main();
console.log(colorsUtil.white(" [exit " + code + "]\n"));
}
});
console.log("- " + colorsUtil.green("instantiate OK") + " (" + asc.formatTime(runTime) + ")");
console.log("\n " + Object.keys(exports).map(key => "[" + (typeof exports[key]).substring(0, 3) + "] " + key).join("\n "));
} catch (e) {
console.log("- " + colorsUtil.red("instantiate ERROR: ") + e.stack);
failed = true;

View File

@ -7,10 +7,10 @@
(global $abi/y (mut i32) (i32.const 0))
(memory $0 1)
(data (i32.const 8) "\06\00\00\00a\00b\00i\00.\00t\00s")
(export "memory" (memory $0))
(export "exported" (func $abi/exported))
(export "exportedExported" (func $abi/exportedExported))
(export "exportedInternal" (func $abi/exportedInternal))
(export "memory" (memory $0))
(start $start)
(func $abi/exported (; 1 ;) (type $i) (result i32)
(i32.const -128)

View File

@ -8,10 +8,10 @@
(global $HEAP_BASE i32 (i32.const 24))
(memory $0 1)
(data (i32.const 8) "\06\00\00\00a\00b\00i\00.\00t\00s\00")
(export "memory" (memory $0))
(export "exported" (func $abi/exported))
(export "exportedExported" (func $abi/exportedExported))
(export "exportedInternal" (func $abi/exportedInternal))
(export "memory" (memory $0))
(start $start)
(func $abi/exported (; 1 ;) (type $i) (result i32)
(i32.shr_s

View File

@ -1,5 +1,13 @@
(module
(type $v (func))
(global $ASC_TARGET i32 (i32.const 0))
(global $ASC_NO_TREESHAKING i32 (i32.const 0))
(global $ASC_NO_ASSERT i32 (i32.const 0))
(global $ASC_MEMORY_BASE i32 (i32.const 0))
(global $ASC_OPTIMIZE_LEVEL i32 (i32.const 0))
(global $ASC_SHRINK_LEVEL i32 (i32.const 0))
(global $ASC_FEATURE_MUTABLE_GLOBAL i32 (i32.const 0))
(global $ASC_FEATURE_SIGN_EXTENSION i32 (i32.const 0))
(global $HEAP_BASE i32 (i32.const 8))
(memory $0 0)
(export "memory" (memory $0))

View File

@ -2,9 +2,7 @@
(type $FFF (func (param f64 f64) (result f64)))
(type $FiF (func (param f64 i32) (result f64)))
(type $fff (func (param f32 f32) (result f32)))
(type $fi (func (param f32) (result i32)))
(type $fif (func (param f32 i32) (result f32)))
(type $Fi (func (param f64) (result i32)))
(type $v (func))
(global $binary/b (mut i32) (i32.const 0))
(global $binary/i (mut i32) (i32.const 0))
@ -1555,13 +1553,7 @@
(f64.const 1.e+300)
)
)
(func $isNaN<f32> (; 2 ;) (type $fi) (param $0 f32) (result i32)
(f32.ne
(get_local $0)
(get_local $0)
)
)
(func $~lib/math/NativeMathf.mod (; 3 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(func $~lib/math/NativeMathf.mod (; 2 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
@ -1613,14 +1605,18 @@
)
)
(set_local $3
(call $isNaN<f32>
(f32.ne
(get_local $1)
(get_local $1)
)
)
)
(if
(i32.eqz
(get_local $3)
(i32.and
(get_local $3)
(i32.const 1)
)
)
(set_local $3
(i32.eq
@ -1630,7 +1626,10 @@
)
)
(if
(get_local $3)
(i32.and
(get_local $3)
(i32.const 1)
)
(return
(f32.div
(tee_local $0
@ -1902,7 +1901,7 @@
(get_local $0)
)
)
(func $~lib/math/NativeMathf.scalbn (; 4 ;) (type $fif) (param $0 f32) (param $1 i32) (result f32)
(func $~lib/math/NativeMathf.scalbn (; 3 ;) (type $fif) (param $0 f32) (param $1 i32) (result f32)
(local $2 f32)
(set_local $2
(get_local $0)
@ -2014,7 +2013,7 @@
)
)
)
(func $~lib/math/NativeMathf.pow (; 5 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(func $~lib/math/NativeMathf.pow (; 4 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(local $2 f32)
(local $3 f32)
(local $4 i32)
@ -3201,13 +3200,7 @@
(f32.const 1.0000000031710769e-30)
)
)
(func $isNaN<f64> (; 6 ;) (type $Fi) (param $0 f64) (result i32)
(f64.ne
(get_local $0)
(get_local $0)
)
)
(func $~lib/math/NativeMath.mod (; 7 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64)
(func $~lib/math/NativeMath.mod (; 5 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64)
(local $2 i64)
(local $3 i32)
(local $4 i64)
@ -3267,7 +3260,8 @@
)
)
(set_local $7
(call $isNaN<f64>
(f64.ne
(get_local $1)
(get_local $1)
)
)
@ -3581,7 +3575,7 @@
(get_local $0)
)
)
(func $start (; 8 ;) (type $v)
(func $start (; 6 ;) (type $v)
(drop
(i32.rem_s
(get_global $binary/i)

View File

@ -2,9 +2,7 @@
(type $FFF (func (param f64 f64) (result f64)))
(type $FiF (func (param f64 i32) (result f64)))
(type $fff (func (param f32 f32) (result f32)))
(type $fi (func (param f32) (result i32)))
(type $fif (func (param f32 i32) (result f32)))
(type $Fi (func (param f64) (result i32)))
(type $v (func))
(global $binary/b (mut i32) (i32.const 0))
(global $binary/i (mut i32) (i32.const 0))
@ -1692,13 +1690,7 @@
(get_local $13)
)
)
(func $isNaN<f32> (; 2 ;) (type $fi) (param $0 f32) (result i32)
(f32.ne
(get_local $0)
(get_local $0)
)
)
(func $~lib/math/NativeMathf.mod (; 3 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(func $~lib/math/NativeMathf.mod (; 2 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
@ -1741,29 +1733,38 @@
)
)
(if
(if (result i32)
(tee_local $7
(if (result i32)
(i32.and
(if (result i32)
(i32.and
(tee_local $7
(i32.eq
(i32.shl
(get_local $3)
(i32.const 1)
(if (result i32)
(tee_local $7
(i32.eq
(i32.shl
(get_local $3)
(i32.const 1)
)
(i32.const 0)
)
)
(get_local $7)
(block $~lib/builtins/isNaN<f32>|inlined.1 (result i32)
(f32.ne
(get_local $1)
(get_local $1)
)
)
(i32.const 0)
)
)
(get_local $7)
(call $isNaN<f32>
(get_local $1)
)
(i32.const 1)
)
(get_local $7)
(i32.eq
(get_local $4)
(i32.const 255)
)
)
(get_local $7)
(i32.eq
(get_local $4)
(i32.const 255)
)
(i32.const 1)
)
(return
(f32.div
@ -2119,7 +2120,7 @@
(get_local $2)
)
)
(func $~lib/math/NativeMathf.scalbn (; 4 ;) (type $fif) (param $0 f32) (param $1 i32) (result f32)
(func $~lib/math/NativeMathf.scalbn (; 3 ;) (type $fif) (param $0 f32) (param $1 i32) (result f32)
(local $2 f32)
(set_local $2
(get_local $0)
@ -2235,7 +2236,7 @@
)
)
)
(func $~lib/math/NativeMathf.pow (; 5 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(func $~lib/math/NativeMathf.pow (; 4 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
@ -3550,13 +3551,7 @@
(get_local $11)
)
)
(func $isNaN<f64> (; 6 ;) (type $Fi) (param $0 f64) (result i32)
(f64.ne
(get_local $0)
(get_local $0)
)
)
(func $~lib/math/NativeMath.mod (; 7 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64)
(func $~lib/math/NativeMath.mod (; 5 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64)
(local $2 i64)
(local $3 i64)
(local $4 i32)
@ -3605,29 +3600,38 @@
)
)
(if
(if (result i32)
(tee_local $7
(if (result i32)
(i32.and
(if (result i32)
(i32.and
(tee_local $7
(i64.eq
(i64.shl
(get_local $3)
(i64.const 1)
(if (result i32)
(tee_local $7
(i64.eq
(i64.shl
(get_local $3)
(i64.const 1)
)
(i64.const 0)
)
)
(get_local $7)
(block $~lib/builtins/isNaN<f64>|inlined.1 (result i32)
(f64.ne
(get_local $1)
(get_local $1)
)
)
(i64.const 0)
)
)
(get_local $7)
(call $isNaN<f64>
(get_local $1)
)
(i32.const 1)
)
(get_local $7)
(i32.eq
(get_local $4)
(i32.const 2047)
)
)
(get_local $7)
(i32.eq
(get_local $4)
(i32.const 2047)
)
(i32.const 1)
)
(return
(f64.div
@ -3996,7 +4000,7 @@
(get_local $2)
)
)
(func $start (; 8 ;) (type $v)
(func $start (; 6 ;) (type $v)
(drop
(i32.lt_s
(get_global $binary/i)

View File

@ -1,7 +1,5 @@
(module
(type $iiiiv (func (param i32 i32 i32 i32)))
(type $fi (func (param f32) (result i32)))
(type $Fi (func (param f64) (result i32)))
(type $iiv (func (param i32 i32)))
(type $v (func))
(import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32)))
@ -19,47 +17,17 @@
(memory $0 1)
(data (i32.const 8) "\0b\00\00\00b\00u\00i\00l\00t\00i\00n\00s\00.\00t\00s")
(data (i32.const 36) "\01\00\00\001")
(export "test" (func $builtins/test))
(export "memory" (memory $0))
(export "table" (table $0))
(export "test" (func $builtins/test))
(start $start)
(func $isNaN<f32> (; 1 ;) (type $fi) (param $0 f32) (result i32)
(f32.ne
(get_local $0)
(get_local $0)
)
)
(func $isFinite<f32> (; 2 ;) (type $fi) (param $0 f32) (result i32)
(f32.eq
(f32.sub
(get_local $0)
(get_local $0)
)
(f32.const 0)
)
)
(func $isNaN<f64> (; 3 ;) (type $Fi) (param $0 f64) (result i32)
(f64.ne
(get_local $0)
(get_local $0)
)
)
(func $isFinite<f64> (; 4 ;) (type $Fi) (param $0 f64) (result i32)
(f64.eq
(f64.sub
(get_local $0)
(get_local $0)
)
(f64.const 0)
)
)
(func $start~anonymous|0 (; 5 ;) (type $iiv) (param $0 i32) (param $1 i32)
(func $start~anonymous|0 (; 1 ;) (type $iiv) (param $0 i32) (param $1 i32)
(nop)
)
(func $builtins/test (; 6 ;) (type $v)
(func $builtins/test (; 2 ;) (type $v)
(nop)
)
(func $start (; 7 ;) (type $v)
(func $start (; 3 ;) (type $v)
(set_global $builtins/i
(i32.const 31)
)
@ -198,96 +166,6 @@
(unreachable)
)
)
(if
(call $isNaN<f32>
(f32.const 1.25)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 87)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.ne
(call $isNaN<f32>
(f32.const nan:0x400000)
)
(i32.const 1)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 88)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.ne
(call $isFinite<f32>
(f32.const 1.25)
)
(i32.const 1)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 89)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f32>
(f32.const inf)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 90)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f32>
(f32.const -inf)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 91)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f32>
(f32.const nan:0x400000)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 92)
(i32.const 0)
)
(unreachable)
)
)
(set_global $builtins/f
(f32.const nan:0x400000)
)
@ -322,104 +200,10 @@
(f32.const 1)
)
(set_global $builtins/b
(call $isNaN<f32>
(f32.const 1.25)
)
(i32.const 0)
)
(set_global $builtins/b
(call $isFinite<f32>
(f32.const 1.25)
)
)
(if
(call $isNaN<f64>
(f64.const 1.25)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 123)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.ne
(call $isNaN<f64>
(f64.const nan:0x8000000000000)
)
(i32.const 1)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 124)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.ne
(call $isFinite<f64>
(f64.const 1.25)
)
(i32.const 1)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 125)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f64>
(f64.const inf)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 126)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f64>
(f64.const -inf)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 127)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f64>
(f64.const nan:0x8000000000000)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 128)
(i32.const 0)
)
(unreachable)
)
(i32.const 1)
)
(set_global $builtins/F
(f64.const nan:0x8000000000000)
@ -455,14 +239,10 @@
(f64.const 1)
)
(set_global $builtins/b
(call $isNaN<f64>
(f64.const 1.25)
)
(i32.const 0)
)
(set_global $builtins/b
(call $isFinite<f64>
(f64.const 1.25)
)
(i32.const 1)
)
(set_global $builtins/i
(i32.load
@ -767,126 +547,6 @@
(i32.const 2)
(get_global $builtins/fn)
)
(if
(i32.eqz
(call $isNaN<f32>
(f32.const nan:0x400000)
)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 271)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $isNaN<f64>
(f64.const nan:0x8000000000000)
)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 272)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f32>
(f32.const nan:0x400000)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 273)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f32>
(f32.const inf)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 274)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f64>
(f64.const nan:0x8000000000000)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 275)
(i32.const 0)
)
(unreachable)
)
)
(if
(call $isFinite<f64>
(f64.const inf)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 276)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $isFinite<f32>
(f32.const 0)
)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 277)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(call $isFinite<f64>
(f64.const 0)
)
)
(block
(call $~lib/env/abort
(i32.const 0)
(i32.const 8)
(i32.const 278)
(i32.const 0)
)
(unreachable)
)
)
(drop
(i32.load8_s
(i32.const 8)
@ -993,10 +653,5 @@
(i32.const 8)
(f64.const 1)
)
(drop
(call $isNaN<f64>
(f64.const 1)
)
)
)
)

View File

@ -219,11 +219,11 @@ F = reinterpret<f64>(25);
var s: usize;
current_memory();
grow_memory(1); // also necessary for load/store tests below
memory.size();
memory.grow(1); // also necessary for load/store tests below
s = current_memory();
s = grow_memory(1);
s = memory.size();
s = memory.grow(1);
// other

View File

@ -1,7 +1,5 @@
(module
(type $iiiiv (func (param i32 i32 i32 i32)))
(type $fi (func (param f32) (result i32)))
(type $Fi (func (param f64) (result i32)))
(type $iiv (func (param i32 i32)))
(type $v (func))
(import "env" "abort" (func $~lib/env/abort (param i32 i32 i32 i32)))
@ -17,57 +15,57 @@
(global $builtins/U (mut i64) (i64.const 0))
(global $builtins/s (mut i32) (i32.const 0))
(global $builtins/fn (mut i32) (i32.const 0))
(global $~lib/builtins/i8.MIN_VALUE i32 (i32.const -128))
(global $~lib/builtins/i8.MAX_VALUE i32 (i32.const 127))
(global $~lib/builtins/i16.MIN_VALUE i32 (i32.const -32768))
(global $~lib/builtins/i16.MAX_VALUE i32 (i32.const 32767))
(global $~lib/builtins/i32.MIN_VALUE i32 (i32.const -2147483648))
(global $~lib/builtins/i32.MAX_VALUE i32 (i32.const 2147483647))
(global $~lib/builtins/i64.MIN_VALUE i64 (i64.const -9223372036854775808))
(global $~lib/builtins/i64.MAX_VALUE i64 (i64.const 9223372036854775807))
(global $~lib/builtins/u8.MIN_VALUE i32 (i32.const 0))
(global $~lib/builtins/u8.MAX_VALUE i32 (i32.const 255))
(global $~lib/builtins/u16.MIN_VALUE i32 (i32.const 0))
(global $~lib/builtins/u16.MAX_VALUE i32 (i32.const 65535))
(global $~lib/builtins/u32.MIN_VALUE i32 (i32.const 0))
(global $~lib/builtins/u32.MAX_VALUE i32 (i32.const -1))
(global $~lib/builtins/u64.MIN_VALUE i64 (i64.const 0))
(global $~lib/builtins/u64.MAX_VALUE i64 (i64.const -1))
(global $~lib/builtins/bool.MIN_VALUE i32 (i32.const 0))
(global $~lib/builtins/bool.MAX_VALUE i32 (i32.const 1))
(global $~lib/builtins/f32.MIN_VALUE f32 (f32.const -3402823466385288598117041e14))
(global $~lib/builtins/f32.MAX_VALUE f32 (f32.const 3402823466385288598117041e14))
(global $~lib/builtins/f32.MIN_SAFE_INTEGER f32 (f32.const -16777215))
(global $~lib/builtins/f32.MAX_SAFE_INTEGER f32 (f32.const 16777215))
(global $~lib/builtins/f32.EPSILON f32 (f32.const 1.1920928955078125e-07))
(global $~lib/builtins/f64.MIN_VALUE f64 (f64.const -1797693134862315708145274e284))
(global $~lib/builtins/f64.MAX_VALUE f64 (f64.const 1797693134862315708145274e284))
(global $~lib/builtins/f64.MIN_SAFE_INTEGER f64 (f64.const -9007199254740991))
(global $~lib/builtins/f64.MAX_SAFE_INTEGER f64 (f64.const 9007199254740991))
(global $~lib/builtins/f64.EPSILON f64 (f64.const 2.220446049250313e-16))
(global $HEAP_BASE i32 (i32.const 44))
(table 1 1 anyfunc)
(elem (i32.const 0) $start~anonymous|0)
(memory $0 1)
(data (i32.const 8) "\0b\00\00\00b\00u\00i\00l\00t\00i\00n\00s\00.\00t\00s\00")
(data (i32.const 36) "\01\00\00\001\00")
(export "test" (func $builtins/test))
(export "memory" (memory $0))
(export "table" (table $0))
(export "test" (func $builtins/test))
(start $start)
(func $isNaN<f32> (; 1 ;) (type $fi) (param $0 f32) (result i32)
(f32.ne
(get_local $0)
(get_local $0)
)
)
(func $isFinite<f32> (; 2 ;) (type $fi) (param $0 f32) (result i32)
(f32.eq
(f32.sub
(get_local $0)
(get_local $0)
)
(f32.const 0)
)
)
(func $isNaN<f64> (; 3 ;) (type $Fi) (param $0 f64) (result i32)
(f64.ne
(get_local $0)
(get_local $0)
)
)
(func $isFinite<f64> (; 4 ;) (type $Fi) (param $0 f64) (result i32)
(f64.eq
(f64.sub
(get_local $0)
(get_local $0)
)
(f64.const 0)
)
)
(func $start~anonymous|0 (; 5 ;) (type $iiv) (param $0 i32) (param $1 i32)
(func $start~anonymous|0 (; 1 ;) (type $iiv) (param $0 i32) (param $1 i32)
(nop)
)
(func $builtins/test (; 6 ;) (type $v)
(func $builtins/test (; 2 ;) (type $v)
(nop)
)
(func $start (; 7 ;) (type $v)
(func $start (; 3 ;) (type $v)
(local $0 i32)
(local $1 i32)
(local $2 i64)
(local $3 i64)
(local $4 f32)
(local $5 f64)
(if
(i32.eqz
(i32.const 1)
@ -809,8 +807,17 @@
(if
(i32.eqz
(i32.eq
(call $isNaN<f32>
(f32.const 1.25)
(i32.and
(block $~lib/builtins/isNaN<f32>|inlined.0 (result i32)
(set_local $4
(f32.const 1.25)
)
(f32.ne
(get_local $4)
(get_local $4)
)
)
(i32.const 1)
)
(i32.const 0)
)
@ -828,8 +835,17 @@
(if
(i32.eqz
(i32.eq
(call $isNaN<f32>
(f32.const nan:0x400000)
(i32.and
(block $~lib/builtins/isNaN<f32>|inlined.1 (result i32)
(set_local $4
(f32.const nan:0x400000)
)
(f32.ne
(get_local $4)
(get_local $4)
)
)
(i32.const 1)
)
(i32.const 1)
)
@ -847,8 +863,20 @@
(if
(i32.eqz
(i32.eq
(call $isFinite<f32>
(f32.const 1.25)
(i32.and
(block $~lib/builtins/isFinite<f32>|inlined.0 (result i32)
(set_local $4
(f32.const 1.25)
)
(f32.eq
(f32.sub
(get_local $4)
(get_local $4)
)
(f32.const 0)
)
)
(i32.const 1)
)
(i32.const 1)
)
@ -866,8 +894,20 @@
(if
(i32.eqz
(i32.eq
(call $isFinite<f32>
(f32.const inf)
(i32.and
(block $~lib/builtins/isFinite<f32>|inlined.1 (result i32)
(set_local $4
(f32.const inf)
)
(f32.eq
(f32.sub
(get_local $4)
(get_local $4)
)
(f32.const 0)
)
)
(i32.const 1)
)
(i32.const 0)
)
@ -885,10 +925,22 @@
(if
(i32.eqz
(i32.eq
(call $isFinite<f32>
(f32.neg
(f32.const inf)
(i32.and
(block $~lib/builtins/isFinite<f32>|inlined.2 (result i32)
(set_local $4
(f32.neg
(f32.const inf)
)
)
(f32.eq
(f32.sub
(get_local $4)
(get_local $4)
)
(f32.const 0)
)
)
(i32.const 1)
)
(i32.const 0)
)
@ -906,8 +958,20 @@
(if
(i32.eqz
(i32.eq
(call $isFinite<f32>
(f32.const nan:0x400000)
(i32.and
(block $~lib/builtins/isFinite<f32>|inlined.3 (result i32)
(set_local $4
(f32.const nan:0x400000)
)
(f32.eq
(f32.sub
(get_local $4)
(get_local $4)
)
(f32.const 0)
)
)
(i32.const 1)
)
(i32.const 0)
)
@ -977,13 +1041,34 @@
)
)
(set_global $builtins/b
(call $isNaN<f32>
(f32.const 1.25)
(i32.and
(block $~lib/builtins/isNaN<f32>|inlined.2 (result i32)
(set_local $4
(f32.const 1.25)
)
(f32.ne
(get_local $4)
(get_local $4)
)
)
(i32.const 1)
)
)
(set_global $builtins/b
(call $isFinite<f32>
(f32.const 1.25)
(i32.and
(block $~lib/builtins/isFinite<f32>|inlined.4 (result i32)
(set_local $4
(f32.const 1.25)
)
(f32.eq
(f32.sub
(get_local $4)
(get_local $4)
)
(f32.const 0)
)
)
(i32.const 1)
)
)
(drop
@ -1049,8 +1134,17 @@
(if
(i32.eqz
(i32.eq
(call $isNaN<f64>
(f64.const 1.25)
(i32.and
(block $~lib/builtins/isNaN<f64>|inlined.0 (result i32)
(set_local $5
(f64.const 1.25)
)
(f64.ne
(get_local $5)
(get_local $5)
)
)
(i32.const 1)
)
(i32.const 0)
)
@ -1068,8 +1162,17 @@
(if
(i32.eqz
(i32.eq
(call $isNaN<f64>
(f64.const nan:0x8000000000000)
(i32.and
(block $~lib/builtins/isNaN<f64>|inlined.1 (result i32)
(set_local $5
(f64.const nan:0x8000000000000)
)
(f64.ne
(get_local $5)
(get_local $5)
)
)
(i32.const 1)
)
(i32.const 1)
)
@ -1087,8 +1190,20 @@
(if
(i32.eqz
(i32.eq
(call $isFinite<f64>
(f64.const 1.25)
(i32.and
(block $~lib/builtins/isFinite<f64>|inlined.0 (result i32)
(set_local $5
(f64.const 1.25)
)
(f64.eq
(f64.sub
(get_local $5)
(get_local $5)
)
(f64.const 0)
)
)
(i32.const 1)
)
(i32.const 1)
)
@ -1106,8 +1221,20 @@
(if
(i32.eqz
(i32.eq
(call $isFinite<f64>
(f64.const inf)
(i32.and
(block $~lib/builtins/isFinite<f64>|inlined.1 (result i32)
(set_local $5
(f64.const inf)
)
(f64.eq
(f64.sub
(get_local $5)
(get_local $5)
)
(f64.const 0)
)
)
(i32.const 1)
)
(i32.const 0)
)
@ -1125,10 +1252,22 @@
(if
(i32.eqz
(i32.eq
(call $isFinite<f64>
(f64.neg
(f64.const inf)
(i32.and
(block $~lib/builtins/isFinite<f64>|inlined.2 (result i32)
(set_local $5
(f64.neg
(f64.const inf)
)
)
(f64.eq
(f64.sub
(get_local $5)
(get_local $5)
)
(f64.const 0)
)
)
(i32.const 1)
)
(i32.const 0)
)
@ -1146,8 +1285,20 @@
(if
(i32.eqz
(i32.eq
(call $isFinite<f64>
(f64.const nan:0x8000000000000)
(i32.and
(block $~lib/builtins/isFinite<f64>|inlined.3 (result i32)
(set_local $5
(f64.const nan:0x8000000000000)
)
(f64.eq
(f64.sub
(get_local $5)
(get_local $5)
)
(f64.const 0)
)
)
(i32.const 1)
)
(i32.const 0)
)
@ -1217,13 +1368,34 @@
)
)
(set_global $builtins/b
(call $isNaN<f64>
(f64.const 1.25)
(i32.and
(block $~lib/builtins/isNaN<f64>|inlined.2 (result i32)
(set_local $5
(f64.const 1.25)
)
(f64.ne
(get_local $5)
(get_local $5)
)
)
(i32.const 1)
)
)
(set_global $builtins/b
(call $isFinite<f64>
(f64.const 1.25)
(i32.and
(block $~lib/builtins/isFinite<f64>|inlined.4 (result i32)
(set_local $5
(f64.const 1.25)
)
(f64.eq
(f64.sub
(get_local $5)
(get_local $5)
)
(f64.const 0)
)
)
(i32.const 1)
)
)
(set_global $builtins/i
@ -1917,8 +2089,17 @@
)
(if
(i32.eqz
(call $isNaN<f32>
(f32.const nan:0x400000)
(i32.and
(block $~lib/builtins/isNaN<f32>|inlined.3 (result i32)
(set_local $4
(f32.const nan:0x400000)
)
(f32.ne
(get_local $4)
(get_local $4)
)
)
(i32.const 1)
)
)
(block
@ -1933,8 +2114,17 @@
)
(if
(i32.eqz
(call $isNaN<f64>
(f64.const nan:0x8000000000000)
(i32.and
(block $~lib/builtins/isNaN<f64>|inlined.3 (result i32)
(set_local $5
(f64.const nan:0x8000000000000)
)
(f64.ne
(get_local $5)
(get_local $5)
)
)
(i32.const 1)
)
)
(block
@ -1950,8 +2140,20 @@
(if
(i32.eqz
(i32.eqz
(call $isFinite<f32>
(f32.const nan:0x400000)
(i32.and
(block $~lib/builtins/isFinite<f32>|inlined.5 (result i32)
(set_local $4
(f32.const nan:0x400000)
)
(f32.eq
(f32.sub
(get_local $4)
(get_local $4)
)
(f32.const 0)
)
)
(i32.const 1)
)
)
)
@ -1968,8 +2170,20 @@
(if
(i32.eqz
(i32.eqz
(call $isFinite<f32>
(f32.const inf)
(i32.and
(block $~lib/builtins/isFinite<f32>|inlined.6 (result i32)
(set_local $4
(f32.const inf)
)
(f32.eq
(f32.sub
(get_local $4)
(get_local $4)
)
(f32.const 0)
)
)
(i32.const 1)
)
)
)
@ -1986,8 +2200,20 @@
(if
(i32.eqz
(i32.eqz
(call $isFinite<f64>
(f64.const nan:0x8000000000000)
(i32.and
(block $~lib/builtins/isFinite<f64>|inlined.5 (result i32)
(set_local $5
(f64.const nan:0x8000000000000)
)
(f64.eq
(f64.sub
(get_local $5)
(get_local $5)
)
(f64.const 0)
)
)
(i32.const 1)
)
)
)
@ -2004,8 +2230,20 @@
(if
(i32.eqz
(i32.eqz
(call $isFinite<f64>
(f64.const inf)
(i32.and
(block $~lib/builtins/isFinite<f64>|inlined.6 (result i32)
(set_local $5
(f64.const inf)
)
(f64.eq
(f64.sub
(get_local $5)
(get_local $5)
)
(f64.const 0)
)
)
(i32.const 1)
)
)
)
@ -2021,8 +2259,20 @@
)
(if
(i32.eqz
(call $isFinite<f32>
(f32.const 0)
(i32.and
(block $~lib/builtins/isFinite<f32>|inlined.7 (result i32)
(set_local $4
(f32.const 0)
)
(f32.eq
(f32.sub
(get_local $4)
(get_local $4)
)
(f32.const 0)
)
)
(i32.const 1)
)
)
(block
@ -2037,8 +2287,20 @@
)
(if
(i32.eqz
(call $isFinite<f64>
(f64.const 0)
(i32.and
(block $~lib/builtins/isFinite<f64>|inlined.7 (result i32)
(set_local $5
(f64.const 0)
)
(f64.eq
(f64.sub
(get_local $5)
(get_local $5)
)
(f64.const 0)
)
)
(i32.const 1)
)
)
(block
@ -2054,7 +2316,7 @@
(if
(i32.eqz
(i32.eq
(i32.const -128)
(get_global $~lib/builtins/i8.MIN_VALUE)
(i32.shr_s
(i32.shl
(i32.const 128)
@ -2077,7 +2339,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 127)
(get_global $~lib/builtins/i8.MAX_VALUE)
(i32.const 127)
)
)
@ -2094,7 +2356,7 @@
(if
(i32.eqz
(i32.eq
(i32.const -32768)
(get_global $~lib/builtins/i16.MIN_VALUE)
(i32.shr_s
(i32.shl
(i32.const 32768)
@ -2117,7 +2379,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 32767)
(get_global $~lib/builtins/i16.MAX_VALUE)
(i32.const 32767)
)
)
@ -2134,7 +2396,7 @@
(if
(i32.eqz
(i32.eq
(i32.const -2147483648)
(get_global $~lib/builtins/i32.MIN_VALUE)
(i32.const -2147483648)
)
)
@ -2151,7 +2413,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 2147483647)
(get_global $~lib/builtins/i32.MAX_VALUE)
(i32.const 2147483647)
)
)
@ -2168,7 +2430,7 @@
(if
(i32.eqz
(i64.eq
(i64.const -9223372036854775808)
(get_global $~lib/builtins/i64.MIN_VALUE)
(i64.const -9223372036854775808)
)
)
@ -2185,7 +2447,7 @@
(if
(i32.eqz
(i64.eq
(i64.const 9223372036854775807)
(get_global $~lib/builtins/i64.MAX_VALUE)
(i64.const 9223372036854775807)
)
)
@ -2202,7 +2464,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 0)
(get_global $~lib/builtins/u8.MIN_VALUE)
(i32.const 0)
)
)
@ -2219,7 +2481,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 255)
(get_global $~lib/builtins/u8.MAX_VALUE)
(i32.const 255)
)
)
@ -2236,7 +2498,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 0)
(get_global $~lib/builtins/u16.MIN_VALUE)
(i32.const 0)
)
)
@ -2253,7 +2515,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 65535)
(get_global $~lib/builtins/u16.MAX_VALUE)
(i32.const 65535)
)
)
@ -2270,7 +2532,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 0)
(get_global $~lib/builtins/u32.MIN_VALUE)
(i32.const 0)
)
)
@ -2287,7 +2549,7 @@
(if
(i32.eqz
(i32.eq
(i32.const -1)
(get_global $~lib/builtins/u32.MAX_VALUE)
(i32.const -1)
)
)
@ -2304,7 +2566,7 @@
(if
(i32.eqz
(i64.eq
(i64.const 0)
(get_global $~lib/builtins/u64.MIN_VALUE)
(i64.const 0)
)
)
@ -2321,7 +2583,7 @@
(if
(i32.eqz
(i64.eq
(i64.const -1)
(get_global $~lib/builtins/u64.MAX_VALUE)
(i64.const -1)
)
)
@ -2338,7 +2600,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 0)
(get_global $~lib/builtins/bool.MIN_VALUE)
(i32.const 0)
)
)
@ -2355,7 +2617,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 0)
(get_global $~lib/builtins/bool.MIN_VALUE)
(i32.const 0)
)
)
@ -2372,7 +2634,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 1)
(get_global $~lib/builtins/bool.MAX_VALUE)
(i32.const 1)
)
)
@ -2389,7 +2651,7 @@
(if
(i32.eqz
(i32.eq
(i32.const 1)
(get_global $~lib/builtins/bool.MAX_VALUE)
(i32.const 1)
)
)
@ -2406,7 +2668,7 @@
(if
(i32.eqz
(f32.eq
(f32.const -3402823466385288598117041e14)
(get_global $~lib/builtins/f32.MIN_VALUE)
(f32.const -3402823466385288598117041e14)
)
)
@ -2423,7 +2685,7 @@
(if
(i32.eqz
(f32.eq
(f32.const 3402823466385288598117041e14)
(get_global $~lib/builtins/f32.MAX_VALUE)
(f32.const 3402823466385288598117041e14)
)
)
@ -2440,7 +2702,7 @@
(if
(i32.eqz
(f32.eq
(f32.const -16777215)
(get_global $~lib/builtins/f32.MIN_SAFE_INTEGER)
(f32.const -16777215)
)
)
@ -2457,7 +2719,7 @@
(if
(i32.eqz
(f32.eq
(f32.const 16777215)
(get_global $~lib/builtins/f32.MAX_SAFE_INTEGER)
(f32.const 16777215)
)
)
@ -2474,7 +2736,7 @@
(if
(i32.eqz
(f32.eq
(f32.const 1.1920928955078125e-07)
(get_global $~lib/builtins/f32.EPSILON)
(f32.const 1.1920928955078125e-07)
)
)
@ -2491,7 +2753,7 @@
(if
(i32.eqz
(f64.eq
(f64.const -1797693134862315708145274e284)
(get_global $~lib/builtins/f64.MIN_VALUE)
(f64.const -1797693134862315708145274e284)
)
)
@ -2508,7 +2770,7 @@
(if
(i32.eqz
(f64.eq
(f64.const 1797693134862315708145274e284)
(get_global $~lib/builtins/f64.MAX_VALUE)
(f64.const 1797693134862315708145274e284)
)
)
@ -2525,7 +2787,7 @@
(if
(i32.eqz
(f64.eq
(f64.const -9007199254740991)
(get_global $~lib/builtins/f64.MIN_SAFE_INTEGER)
(f64.const -9007199254740991)
)
)
@ -2542,7 +2804,7 @@
(if
(i32.eqz
(f64.eq
(f64.const 9007199254740991)
(get_global $~lib/builtins/f64.MAX_SAFE_INTEGER)
(f64.const 9007199254740991)
)
)
@ -2559,7 +2821,7 @@
(if
(i32.eqz
(f64.eq
(f64.const 2.220446049250313e-16)
(get_global $~lib/builtins/f64.EPSILON)
(f64.const 2.220446049250313e-16)
)
)
@ -2850,8 +3112,14 @@
)
)
(drop
(call $isNaN<f64>
(f64.const 1)
(block $~lib/builtins/isNaN<f64>|inlined.4 (result i32)
(set_local $5
(f64.const 1)
)
(f64.ne
(get_local $5)
(get_local $5)
)
)
)
)

View File

@ -25,8 +25,8 @@
(block $2of2
(block $1of2
(block $0of2
(block $oob
(br_table $0of2 $1of2 $2of2 $oob
(block $outOfRange
(br_table $0of2 $1of2 $2of2 $outOfRange
(i32.sub
(get_global $~argc)
(i32.const 1)

View File

@ -26,8 +26,8 @@
(block $2of2
(block $1of2
(block $0of2
(block $oob
(br_table $0of2 $1of2 $2of2 $oob
(block $outOfRange
(br_table $0of2 $1of2 $2of2 $outOfRange
(i32.sub
(get_global $~argc)
(i32.const 1)

View File

@ -1,8 +1,8 @@
(module
(type $iv (func (param i32)))
(memory $0 0)
(export "test" (func $class-extends/test))
(export "memory" (memory $0))
(export "test" (func $class-extends/test))
(func $class-extends/test (; 0 ;) (type $iv) (param $0 i32)
(drop
(i32.load

View File

@ -2,8 +2,8 @@
(type $iv (func (param i32)))
(global $HEAP_BASE i32 (i32.const 8))
(memory $0 0)
(export "test" (func $class-extends/test))
(export "memory" (memory $0))
(export "test" (func $class-extends/test))
(func $class-extends/test (; 0 ;) (type $iv) (param $0 i32)
(drop
(i32.load

View File

@ -2,8 +2,8 @@
(type $iv (func (param i32)))
(type $v (func))
(memory $0 0)
(export "test" (func $class-overloading/test))
(export "memory" (memory $0))
(export "test" (func $class-overloading/test))
(start $start)
(func $class-overloading/Foo#baz (; 0 ;) (type $iv) (param $0 i32)
(nop)

View File

@ -3,8 +3,8 @@
(type $v (func))
(global $HEAP_BASE i32 (i32.const 8))
(memory $0 0)
(export "test" (func $class-overloading/test))
(export "memory" (memory $0))
(export "test" (func $class-overloading/test))
(start $start)
(func $class-overloading/Foo#baz (; 0 ;) (type $iv) (param $0 i32)
(nop)

View File

@ -1,8 +1,8 @@
(module
(type $i (func (result i32)))
(memory $0 0)
(export "test" (func $class-with-boolean-field/test))
(export "memory" (memory $0))
(export "test" (func $class-with-boolean-field/test))
(func $class-with-boolean-field/test (; 0 ;) (type $i) (result i32)
(i32.store8
(i32.const 0)

View File

@ -2,8 +2,8 @@
(type $i (func (result i32)))
(global $HEAP_BASE i32 (i32.const 8))
(memory $0 0)
(export "test" (func $class-with-boolean-field/test))
(export "memory" (memory $0))
(export "test" (func $class-with-boolean-field/test))
(func $class-with-boolean-field/test (; 0 ;) (type $i) (result i32)
(local $0 i32)
(i32.store8

View File

@ -8,8 +8,8 @@
(global $class/Animal.ONE (mut i32) (i32.const 1))
(memory $0 1)
(data (i32.const 8) "\08\00\00\00c\00l\00a\00s\00s\00.\00t\00s")
(export "test" (func $class/test))
(export "memory" (memory $0))
(export "test" (func $class/test))
(start $start)
(func $class/Animal.add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.add

View File

@ -11,8 +11,8 @@
(global $HEAP_BASE i32 (i32.const 28))
(memory $0 1)
(data (i32.const 8) "\08\00\00\00c\00l\00a\00s\00s\00.\00t\00s\00")
(export "test" (func $class/test))
(export "memory" (memory $0))
(export "test" (func $class/test))
(start $start)
(func $class/Animal.add (; 1 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.add

View File

@ -18,6 +18,7 @@
(global $enum/SelfReference.ZERO i32 (i32.const 0))
(global $enum/SelfReference.ONE i32 (i32.const 1))
(memory $0 0)
(export "memory" (memory $0))
(export "Implicit.ZERO" (global $enum/Implicit.ZERO))
(export "Implicit.ONE" (global $enum/Implicit.ONE))
(export "Implicit.TWO" (global $enum/Implicit.TWO))
@ -32,7 +33,6 @@
(export "Mixed.FOUR" (global $enum/Mixed.FOUR))
(export "SelfReference.ZERO" (global $enum/SelfReference.ZERO))
(export "SelfReference.ONE" (global $enum/SelfReference.ONE))
(export "memory" (memory $0))
(start $start)
(func $enum/getZero (; 0 ;) (type $i) (result i32)
(i32.const 0)
@ -43,7 +43,7 @@
)
(set_global $enum/NonConstant.ONE
(i32.add
(get_global $enum/NonConstant.ZERO)
(call $enum/getZero)
(i32.const 1)
)
)

View File

@ -1,18 +1,18 @@
export enum Implicit {
export const enum Implicit {
ZERO,
ONE,
TWO,
THREE
}
export enum Explicit {
export const enum Explicit {
ZERO = 0,
ONE = 0 + 1,
TWO = 1 + 1,
THREE = 3
}
export enum Mixed {
export const enum Mixed {
ZERO,
ONE,
THREE = 3,
@ -23,12 +23,15 @@ function getZero(): i32 {
return 0;
}
export enum NonConstant {
ZERO = getZero(), // cannot export a mutable global
ONE // cannot export a mutable global (tsc doesn't allow this)
enum NonConstant {
ZERO = getZero(),
ONE = getZero() + 1
}
export enum SelfReference {
NonConstant.ZERO;
NonConstant.ONE;
export const enum SelfReference {
ZERO,
ONE = ZERO + 1
}

View File

@ -20,6 +20,7 @@
(global $enum/enumType (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 8))
(memory $0 0)
(export "memory" (memory $0))
(export "Implicit.ZERO" (global $enum/Implicit.ZERO))
(export "Implicit.ONE" (global $enum/Implicit.ONE))
(export "Implicit.TWO" (global $enum/Implicit.TWO))
@ -34,7 +35,6 @@
(export "Mixed.FOUR" (global $enum/Mixed.FOUR))
(export "SelfReference.ZERO" (global $enum/SelfReference.ZERO))
(export "SelfReference.ONE" (global $enum/SelfReference.ONE))
(export "memory" (memory $0))
(start $start)
(func $enum/getZero (; 0 ;) (type $i) (result i32)
(i32.const 0)
@ -45,9 +45,15 @@
)
(set_global $enum/NonConstant.ONE
(i32.add
(get_global $enum/NonConstant.ZERO)
(call $enum/getZero)
(i32.const 1)
)
)
(drop
(get_global $enum/NonConstant.ZERO)
)
(drop
(get_global $enum/NonConstant.ONE)
)
)
)

View File

@ -5,6 +5,7 @@
(global $export/b i32 (i32.const 2))
(global $export/c i32 (i32.const 3))
(memory $0 0)
(export "memory" (memory $0))
(export "add" (func $export/add))
(export "sub" (func $export/sub))
(export "renamed_mul" (func $export/mul))
@ -12,7 +13,6 @@
(export "b" (global $export/b))
(export "renamed_c" (global $export/c))
(export "ns.two" (func $export/ns.two))
(export "memory" (memory $0))
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.add
(get_local $0)

View File

@ -6,6 +6,7 @@
(global $export/c i32 (i32.const 3))
(global $HEAP_BASE i32 (i32.const 8))
(memory $0 0)
(export "memory" (memory $0))
(export "add" (func $export/add))
(export "sub" (func $export/sub))
(export "renamed_mul" (func $export/mul))
@ -13,7 +14,6 @@
(export "b" (global $export/b))
(export "renamed_c" (global $export/c))
(export "ns.two" (func $export/ns.two))
(export "memory" (memory $0))
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.add
(get_local $0)

View File

@ -1,13 +1,12 @@
(module
(type $iii (func (param i32 i32) (result i32)))
(type $iv (func (param i32)))
(type $i (func (result i32)))
(type $ii (func (param i32) (result i32)))
(type $iiv (func (param i32 i32)))
(type $iv (func (param i32)))
(type $v (func))
(global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0))
(global $~lib/allocator/arena/offset (mut i32) (i32.const 0))
(global $~argc (mut i32) (i32.const 0))
(global $exports/Animal.CAT i32 (i32.const 0))
(global $exports/Animal.DOG i32 (i32.const 1))
(global $exports/animals.Animal.CAT i32 (i32.const 0))
@ -15,8 +14,9 @@
(global $exports/Car.TIRES i32 (i32.const 4))
(global $exports/vehicles.Car.TIRES i32 (i32.const 4))
(global $exports/outer.inner.a i32 (i32.const 42))
(global $HEAP_BASE i32 (i32.const 8))
(global $~argc (mut i32) (i32.const 0))
(memory $0 0)
(export "memory" (memory $0))
(export "add" (func $exports/add))
(export "_setargc" (func $~setargc))
(export "subOpt" (func $exports/subOpt|trampoline))
@ -28,21 +28,20 @@
(export "Car.TIRES" (global $exports/Car.TIRES))
(export "Car.getNumTires" (func $exports/Car.getNumTires))
(export "Car#constructor" (func $exports/Car#constructor|trampoline))
(export "Car#get:doors" (func $Car#get:doors))
(export "Car#set:doors" (func $Car#set:doors))
(export "Car#get:numDoors" (func $Car#get:doors))
(export "Car#set:numDoors" (func $Car#set:doors))
(export "Car#get:doors" (func $exports/Car#get:numDoors))
(export "Car#set:doors" (func $exports/Car#set:numDoors))
(export "Car#get:numDoors" (func $exports/Car#get:numDoors))
(export "Car#set:numDoors" (func $exports/Car#set:numDoors))
(export "Car#openDoors" (func $exports/Car#openDoors))
(export "vehicles.Car.TIRES" (global $exports/vehicles.Car.TIRES))
(export "vehicles.Car.getNumTires" (func $exports/Car.getNumTires))
(export "vehicles.Car#constructor" (func $exports/Car#constructor|trampoline))
(export "vehicles.Car#get:doors" (func $Car#get:doors))
(export "vehicles.Car#set:doors" (func $Car#set:doors))
(export "vehicles.Car#get:numDoors" (func $Car#get:doors))
(export "vehicles.Car#set:numDoors" (func $Car#set:doors))
(export "vehicles.Car#get:doors" (func $exports/Car#get:numDoors))
(export "vehicles.Car#set:doors" (func $exports/Car#set:numDoors))
(export "vehicles.Car#get:numDoors" (func $exports/Car#get:numDoors))
(export "vehicles.Car#set:numDoors" (func $exports/Car#set:numDoors))
(export "vehicles.Car#openDoors" (func $exports/Car#openDoors))
(export "outer.inner.a" (global $exports/outer.inner.a))
(export "memory" (memory $0))
(start $start)
(func $exports/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(i32.add
@ -56,37 +55,10 @@
(get_local $1)
)
)
(func $exports/subOpt|trampoline (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(block $1of1
(block $0of1
(block $oob
(br_table $0of1 $1of1 $oob
(i32.sub
(get_global $~argc)
(i32.const 1)
)
)
)
(unreachable)
)
(set_local $1
(i32.const 0)
)
)
(call $exports/subOpt
(get_local $0)
(get_local $1)
)
)
(func $~setargc (; 3 ;) (type $iv) (param $0 i32)
(set_global $~argc
(get_local $0)
)
)
(func $exports/Car.getNumTires (; 4 ;) (type $i) (result i32)
(func $exports/Car.getNumTires (; 2 ;) (type $i) (result i32)
(i32.const 4)
)
(func $~lib/allocator/arena/allocate_memory (; 5 ;) (type $ii) (param $0 i32) (result i32)
(func $~lib/allocator/arena/__memory_allocate (; 3 ;) (type $ii) (param $0 i32) (result i32)
(local $1 i32)
(local $2 i32)
(local $3 i32)
@ -172,14 +144,19 @@
)
(i32.const 0)
)
(func $exports/Car#constructor (; 6 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(func $~lib/memory/memory.allocate (; 4 ;) (type $ii) (param $0 i32) (result i32)
(call $~lib/allocator/arena/__memory_allocate
(get_local $0)
)
)
(func $exports/Car#constructor (; 5 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(if
(i32.eqz
(get_local $0)
)
(i32.store
(tee_local $0
(call $~lib/allocator/arena/allocate_memory
(call $~lib/memory/memory.allocate
(i32.const 4)
)
)
@ -192,11 +169,60 @@
)
(get_local $0)
)
(func $exports/Car#constructor|trampoline (; 7 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(func $exports/Car#get:numDoors (; 6 ;) (type $ii) (param $0 i32) (result i32)
(i32.load
(get_local $0)
)
)
(func $exports/Car#set:numDoors (; 7 ;) (type $iiv) (param $0 i32) (param $1 i32)
(i32.store
(get_local $0)
(get_local $1)
)
)
(func $exports/Car#openDoors (; 8 ;) (type $iv) (param $0 i32)
(nop)
)
(func $start (; 9 ;) (type $v)
(set_global $~lib/allocator/arena/startOffset
(i32.const 8)
)
(set_global $~lib/allocator/arena/offset
(get_global $~lib/allocator/arena/startOffset)
)
)
(func $exports/subOpt|trampoline (; 10 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(block $1of1
(block $0of1
(block $oob
(br_table $0of1 $1of1 $oob
(block $outOfRange
(br_table $0of1 $1of1 $outOfRange
(i32.sub
(get_global $~argc)
(i32.const 1)
)
)
)
(unreachable)
)
(set_local $1
(i32.const 0)
)
)
(call $exports/subOpt
(get_local $0)
(get_local $1)
)
)
(func $~setargc (; 11 ;) (type $iv) (param $0 i32)
(set_global $~argc
(get_local $0)
)
)
(func $exports/Car#constructor|trampoline (; 12 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
(block $1of1
(block $0of1
(block $outOfRange
(br_table $0of1 $1of1 $outOfRange
(get_global $~argc)
)
)
@ -211,32 +237,4 @@
(get_local $1)
)
)
(func $Car#get:doors (; 8 ;) (type $ii) (param $0 i32) (result i32)
(i32.load
(get_local $0)
)
)
(func $Car#set:doors (; 9 ;) (type $iiv) (param $0 i32) (param $1 i32)
(i32.store
(get_local $0)
(get_local $1)
)
)
(func $exports/Car#openDoors (; 10 ;) (type $iv) (param $0 i32)
(nop)
)
(func $start (; 11 ;) (type $v)
(set_global $~lib/allocator/arena/startOffset
(i32.and
(i32.add
(get_global $HEAP_BASE)
(i32.const 7)
)
(i32.const -8)
)
)
(set_global $~lib/allocator/arena/offset
(get_global $~lib/allocator/arena/startOffset)
)
)
)

View File

@ -17,14 +17,14 @@ export namespace math {
}
// top-level enum
export enum Animal {
export const enum Animal {
CAT,
DOG
}
// namespaced enum
export namespace animals {
export enum Animal {
export const enum Animal {
CAT,
DOG
}

Some files were not shown because too many files have changed in this diff Show More