porffor 0.16.0-688a50c13 → 0.16.0-7143cc59c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/compiler/2c.js +75 -15
- package/compiler/allocators.js +128 -0
- package/compiler/assemble.js +7 -1
- package/compiler/builtins/array.ts +3 -4
- package/compiler/builtins/date.ts +1 -2
- package/compiler/builtins/set.ts +1 -2
- package/compiler/codegen.js +87 -38
- package/compiler/generated_builtins.js +28 -28
- package/compiler/index.js +15 -1
- package/compiler/parse.js +0 -6
- package/compiler/pgo.js +5 -0
- package/compiler/precompile.js +1 -1
- package/compiler/prefs.js +1 -2
- package/compiler/wasmSpec.js +2 -2
- package/compiler/wrap.js +16 -15
- package/package.json +1 -1
- package/runner/index.js +5 -0
- package/a.txt +0 -457
- package/b.txt +0 -457
- package/compiler/allocators/grow.js +0 -26
- package/compiler/allocators/index.js +0 -10
- package/compiler/allocators/static.js +0 -42
- package/no_pgo.txt +0 -923
- package/pgo.txt +0 -916
package/README.md
CHANGED
@@ -29,7 +29,7 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
|
|
29
29
|
> [!WARNING]
|
30
30
|
> Compiling to native binaries uses [2c](#2c), Porffor's own Wasm -> C compiler, which is experimental.
|
31
31
|
|
32
|
-
**`porf native path/to/script.js out(.exe)`**. You can specify the compiler with `--compiler=clang/zig
|
32
|
+
**`porf native path/to/script.js out(.exe)`**. You can specify the compiler with `--compiler=clang/gcc/zig` (`clang` by default), and which optimization level to use with `--cO=Ofast/O3/O2/O1/O0` (`Ofast` by default). Output binaries are also stripped by default.
|
33
33
|
|
34
34
|
### Compiling to C
|
35
35
|
> [!WARNING]
|
@@ -49,7 +49,7 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
|
|
49
49
|
|
50
50
|
**`porf debug path/to/script.js`**
|
51
51
|
|
52
|
-
###
|
52
|
+
### Debugging the compiled Wasm of a JS file
|
53
53
|
> [!WARNING]
|
54
54
|
> Very experimental WIP feature!
|
55
55
|
|
@@ -82,7 +82,6 @@ Expect nothing to work! Only very limited JS is currently supported. See files i
|
|
82
82
|
- Little built-ins/prototype
|
83
83
|
- No async/promise/await
|
84
84
|
- No variables between scopes (except args and globals)
|
85
|
-
- Literal callees only in calls (eg `print()` works, `a = print; a()` does not)
|
86
85
|
- No `eval()` etc (since it is AOT)
|
87
86
|
|
88
87
|
## Sub-engines
|
@@ -112,7 +111,7 @@ These include some early (stage 1/0) and/or dead (last commit years ago) proposa
|
|
112
111
|
|
113
112
|
- Number literals
|
114
113
|
- Declaring functions
|
115
|
-
- Calling functions
|
114
|
+
- Calling functions
|
116
115
|
- `return`
|
117
116
|
- `let`/`const`/`var` basic declarations
|
118
117
|
- Some basic integer operators (`+-/*%`)
|
package/compiler/2c.js
CHANGED
@@ -2,10 +2,11 @@ import { read_ieee754_binary64, read_signedLEB128, read_unsignedLEB128 } from '.
|
|
2
2
|
import { Blocktype, Opcodes, Valtype } from './wasmSpec.js';
|
3
3
|
import { operatorOpcode } from './expression.js';
|
4
4
|
import { log } from './log.js';
|
5
|
+
import Prefs from './prefs.js';
|
5
6
|
|
6
7
|
const CValtype = {
|
7
|
-
i8: '
|
8
|
-
i16: '
|
8
|
+
i8: 'u8',
|
9
|
+
i16: 'u16',
|
9
10
|
i32: 'i32',
|
10
11
|
u32: 'u32',
|
11
12
|
i64: 'i64',
|
@@ -17,8 +18,8 @@ const CValtype = {
|
|
17
18
|
undefined: 'void'
|
18
19
|
};
|
19
20
|
|
20
|
-
const alwaysPreface = `typedef uint8_t
|
21
|
-
typedef uint16_t
|
21
|
+
const alwaysPreface = `typedef uint8_t u8;
|
22
|
+
typedef uint16_t u16;
|
22
23
|
typedef int32_t i32;
|
23
24
|
typedef uint32_t u32;
|
24
25
|
typedef int64_t i64;
|
@@ -33,11 +34,11 @@ struct ReturnValue {
|
|
33
34
|
i32 type;
|
34
35
|
};\n\n`;
|
35
36
|
|
36
|
-
// todo:
|
37
|
+
// todo: review whether 2cMemcpy should be default or not
|
37
38
|
|
38
39
|
// all:
|
39
40
|
// immediates: ['align', 'offset']
|
40
|
-
const CMemFuncs = {
|
41
|
+
const CMemFuncs = Prefs['2cMemcpy'] ? {
|
41
42
|
[Opcodes.i32_store]: {
|
42
43
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
43
44
|
args: ['pointer', 'value'],
|
@@ -47,13 +48,13 @@ const CMemFuncs = {
|
|
47
48
|
[Opcodes.i32_store16]: {
|
48
49
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
49
50
|
args: ['pointer', 'value'],
|
50
|
-
argTypes: ['i32', '
|
51
|
+
argTypes: ['i32', 'u16'],
|
51
52
|
returns: false
|
52
53
|
},
|
53
54
|
[Opcodes.i32_store8]: {
|
54
55
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
55
56
|
args: ['pointer', 'value'],
|
56
|
-
argTypes: ['i32', '
|
57
|
+
argTypes: ['i32', 'u8'],
|
57
58
|
returns: false
|
58
59
|
},
|
59
60
|
|
@@ -66,7 +67,7 @@ return out;`,
|
|
66
67
|
returns: 'i32'
|
67
68
|
},
|
68
69
|
[Opcodes.i32_load16_u]: {
|
69
|
-
c: `
|
70
|
+
c: `u16 out;
|
70
71
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
71
72
|
return out;`,
|
72
73
|
args: ['pointer'],
|
@@ -74,7 +75,7 @@ return out;`,
|
|
74
75
|
returns: 'i32'
|
75
76
|
},
|
76
77
|
[Opcodes.i32_load8_u]: {
|
77
|
-
c: `
|
78
|
+
c: `u8 out;
|
78
79
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
79
80
|
return out;`,
|
80
81
|
args: ['pointer'],
|
@@ -96,6 +97,57 @@ return out;`,
|
|
96
97
|
argTypes: ['i32'],
|
97
98
|
returns: 'f64'
|
98
99
|
},
|
100
|
+
} : {
|
101
|
+
[Opcodes.i32_store]: {
|
102
|
+
c: `*((i32*)(_memory + offset + pointer)) = value;`,
|
103
|
+
args: ['pointer', 'value'],
|
104
|
+
argTypes: ['i32', 'i32'],
|
105
|
+
returns: false
|
106
|
+
},
|
107
|
+
[Opcodes.i32_store16]: {
|
108
|
+
c: `*((u16*)(_memory + offset + pointer)) = value;`,
|
109
|
+
args: ['pointer', 'value'],
|
110
|
+
argTypes: ['i32', 'u16'],
|
111
|
+
returns: false
|
112
|
+
},
|
113
|
+
[Opcodes.i32_store8]: {
|
114
|
+
c: `*((u8*)(_memory + offset + pointer)) = value;`,
|
115
|
+
args: ['pointer', 'value'],
|
116
|
+
argTypes: ['i32', 'u8'],
|
117
|
+
returns: false
|
118
|
+
},
|
119
|
+
|
120
|
+
[Opcodes.i32_load]: {
|
121
|
+
c: `return *((i32*)(_memory + offset + pointer));`,
|
122
|
+
args: ['pointer'],
|
123
|
+
argTypes: ['i32'],
|
124
|
+
returns: 'i32'
|
125
|
+
},
|
126
|
+
[Opcodes.i32_load16_u]: {
|
127
|
+
c: `return *((u16*)(_memory + offset + pointer));`,
|
128
|
+
args: ['pointer'],
|
129
|
+
argTypes: ['i32'],
|
130
|
+
returns: 'i32'
|
131
|
+
},
|
132
|
+
[Opcodes.i32_load8_u]: {
|
133
|
+
c: `return *((u8*)(_memory + offset + pointer));`,
|
134
|
+
args: ['pointer'],
|
135
|
+
argTypes: ['i32'],
|
136
|
+
returns: 'i32'
|
137
|
+
},
|
138
|
+
|
139
|
+
[Opcodes.f64_store]: {
|
140
|
+
c: `*((f64*)(_memory + offset + pointer)) = value;`,
|
141
|
+
args: ['pointer', 'value'],
|
142
|
+
argTypes: ['i32', 'f64'],
|
143
|
+
returns: false
|
144
|
+
},
|
145
|
+
[Opcodes.f64_load]: {
|
146
|
+
c: `return *((f64*)(_memory + offset + pointer));`,
|
147
|
+
args: ['pointer'],
|
148
|
+
argTypes: ['i32'],
|
149
|
+
returns: 'f64'
|
150
|
+
},
|
99
151
|
};
|
100
152
|
|
101
153
|
const inv = (obj, keyMap = x => x) => Object.keys(obj).reduce((acc, x) => { acc[keyMap(obj[x])] = x; return acc; }, {});
|
@@ -152,11 +204,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
152
204
|
|
153
205
|
if (pages.size > 0) {
|
154
206
|
prepend.set('_memory', `char _memory[${pages.size * pageSize}];\n`);
|
155
|
-
includes.set('string.h', true);
|
207
|
+
if (Prefs['2cMemcpy']) includes.set('string.h', true);
|
156
208
|
}
|
157
209
|
|
158
210
|
if (data.length > 0) {
|
159
|
-
|
211
|
+
if (Prefs['2cMemcpy']) {
|
212
|
+
prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n '));
|
213
|
+
includes.set('string.h', true);
|
214
|
+
} else {
|
215
|
+
prependMain.set('_data', data.map(x => x.bytes.reduce((acc, y, i) => acc + `_memory[${x.offset + i}]=(u8)${y};`, '')).join('\n '));
|
216
|
+
}
|
160
217
|
}
|
161
218
|
|
162
219
|
if (importFuncs.find(x => x.name === '__Porffor_readArgv')) {
|
@@ -362,7 +419,10 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
362
419
|
|
363
420
|
case Opcodes.return:
|
364
421
|
// line(`return${returns ? ` ${removeBrackets(vals.pop())}` : ''}`);
|
365
|
-
|
422
|
+
const b = vals.pop();
|
423
|
+
const a = vals.pop();
|
424
|
+
line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(a)}, ${removeBrackets(b)} }` : ''}`);
|
425
|
+
// line(`return${returns ? ` (struct ReturnValue){ ${removeBrackets(vals.pop())}, ${removeBrackets(vals.pop())} }` : ''}`);
|
366
426
|
break;
|
367
427
|
|
368
428
|
case Opcodes.if: {
|
@@ -474,7 +534,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
474
534
|
out[read++] = ch;
|
475
535
|
}
|
476
536
|
|
477
|
-
|
537
|
+
*((i32*)(_memory + outPtr)) = (i32)read;
|
478
538
|
return read;
|
479
539
|
}`);
|
480
540
|
|
@@ -504,7 +564,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
504
564
|
|
505
565
|
fclose(fp);
|
506
566
|
|
507
|
-
|
567
|
+
*((i32*)(_memory + outPtr)) = (i32)read;
|
508
568
|
return read;
|
509
569
|
}`);
|
510
570
|
const outPtr = vals.pop();
|
@@ -0,0 +1,128 @@
|
|
1
|
+
import { Opcodes, PageSize, Valtype } from './wasmSpec.js';
|
2
|
+
import { number } from './embedding.js';
|
3
|
+
import Prefs from './prefs.js';
|
4
|
+
|
5
|
+
// we currently have 3 allocators:
|
6
|
+
// - static (default): a static/compile-time allocator. fast (no grow/run-time alloc needed) but can break some code
|
7
|
+
// - grow: perform a memory.grow every allocation. simple but maybe slow?
|
8
|
+
// - chunk: perform large memory.grow's in chunks when needed. needs investigation
|
9
|
+
|
10
|
+
export default name => {
|
11
|
+
switch (name) {
|
12
|
+
case 'static': return new StaticAllocator();
|
13
|
+
case 'grow': return new GrowAllocator();
|
14
|
+
case 'chunk': return new ChunkAllocator();
|
15
|
+
default: throw new Error(`unknown allocator: ${name}`);
|
16
|
+
}
|
17
|
+
};
|
18
|
+
|
19
|
+
export class StaticAllocator {
|
20
|
+
constructor() {
|
21
|
+
}
|
22
|
+
|
23
|
+
allocType(itemType) {
|
24
|
+
switch (itemType) {
|
25
|
+
case 'i8': return 'bytestring';
|
26
|
+
case 'i16': return 'string';
|
27
|
+
|
28
|
+
default: return 'array';
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
ptr(ind) {
|
33
|
+
if (ind === 0) return 4;
|
34
|
+
return ind * PageSize;
|
35
|
+
}
|
36
|
+
|
37
|
+
alloc({ scope, pages }, name, { itemType }) {
|
38
|
+
const reason = `${this.allocType(itemType)}: ${Prefs.scopedPageNames ? (scope.name + '/') : ''}${name}`;
|
39
|
+
|
40
|
+
if (pages.has(reason)) return number(this.ptr(pages.get(reason).ind), Valtype.i32);
|
41
|
+
|
42
|
+
if (reason.startsWith('array:')) pages.hasArray = true;
|
43
|
+
if (reason.startsWith('string:')) pages.hasString = true;
|
44
|
+
if (reason.startsWith('bytestring:')) pages.hasByteString = true;
|
45
|
+
if (reason.includes('string:')) pages.hasAnyString = true;
|
46
|
+
|
47
|
+
let ind = pages.size;
|
48
|
+
pages.set(reason, { ind, type: itemType });
|
49
|
+
|
50
|
+
scope.pages ??= new Map();
|
51
|
+
scope.pages.set(reason, { ind, type: itemType });
|
52
|
+
|
53
|
+
return number(this.ptr(ind), Valtype.i32);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
export class GrowAllocator {
|
58
|
+
constructor() {
|
59
|
+
Prefs.rmUnusedTypes = false;
|
60
|
+
}
|
61
|
+
|
62
|
+
alloc() {
|
63
|
+
return [
|
64
|
+
// grow by 1 page
|
65
|
+
[ Opcodes.i32_const, 1 ],
|
66
|
+
[ Opcodes.memory_grow, 0 ], // returns old page count
|
67
|
+
|
68
|
+
// get ptr (page count * page size)
|
69
|
+
number(65536, Valtype.i32)[0],
|
70
|
+
[ Opcodes.i32_mul ]
|
71
|
+
];
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
export class ChunkAllocator {
|
76
|
+
constructor(chunkSize) {
|
77
|
+
Prefs.rmUnusedTypes = false;
|
78
|
+
|
79
|
+
// 64KiB * chunk size each growth
|
80
|
+
// 16: 1MiB chunks
|
81
|
+
this.chunkSize = chunkSize ?? Prefs.chunkAllocatorSize ?? 16;
|
82
|
+
}
|
83
|
+
|
84
|
+
alloc({ asmFunc, funcIndex }) {
|
85
|
+
const func = funcIndex['#chunkallocator_alloc'] ?? asmFunc('#chunkallocator_alloc', {
|
86
|
+
wasm: [
|
87
|
+
[ Opcodes.global_get, 0 ],
|
88
|
+
[ Opcodes.global_get, 1 ],
|
89
|
+
[ Opcodes.i32_ge_s ],
|
90
|
+
[ Opcodes.if, Valtype.i32 ], // ptr >= next
|
91
|
+
// grow by chunk size pages
|
92
|
+
[ Opcodes.i32_const, this.chunkSize ],
|
93
|
+
[ Opcodes.memory_grow, 0 ],
|
94
|
+
|
95
|
+
// ptr = prev memory size * PageSize
|
96
|
+
number(65536, Valtype.i32)[0],
|
97
|
+
[ Opcodes.i32_mul ],
|
98
|
+
[ Opcodes.global_set, 0 ],
|
99
|
+
|
100
|
+
// next = ptr + ((chunkSize - 1) * PageSize)
|
101
|
+
[ Opcodes.global_get, 0 ],
|
102
|
+
number(65536 * (this.chunkSize - 1), Valtype.i32)[0],
|
103
|
+
[ Opcodes.i32_add ],
|
104
|
+
[ Opcodes.global_set, 1 ],
|
105
|
+
|
106
|
+
// return ptr
|
107
|
+
[ Opcodes.global_get, 0 ],
|
108
|
+
[ Opcodes.else ],
|
109
|
+
// return ptr = ptr + PageSize
|
110
|
+
[ Opcodes.global_get, 0 ],
|
111
|
+
number(65536, Valtype.i32)[0],
|
112
|
+
[ Opcodes.i32_add ],
|
113
|
+
[ Opcodes.global_set, 0 ],
|
114
|
+
[ Opcodes.global_get, 0 ],
|
115
|
+
[ Opcodes.end ],
|
116
|
+
],
|
117
|
+
params: [],
|
118
|
+
locals: [],
|
119
|
+
globals: [ Valtype.i32, Valtype.i32 ],
|
120
|
+
globalNames: ['#chunkallocator_ptr', '#chunkallocator_next'],
|
121
|
+
returns: [ Valtype.i32 ],
|
122
|
+
}).index;
|
123
|
+
|
124
|
+
return [
|
125
|
+
[ Opcodes.call, func ]
|
126
|
+
];
|
127
|
+
}
|
128
|
+
}
|
package/compiler/assemble.js
CHANGED
@@ -240,7 +240,13 @@ export default (funcs, globals, tags, pages, data, flags, noTreeshake = false) =
|
|
240
240
|
|
241
241
|
const dataSection = data.length === 0 ? [] : createSection(
|
242
242
|
Section.data,
|
243
|
-
encodeVector(data.map(x =>
|
243
|
+
encodeVector(data.map(x => {
|
244
|
+
// type: active
|
245
|
+
if (x.offset != null) return [ 0x00, Opcodes.i32_const, ...signedLEB128(x.offset), Opcodes.end, ...encodeVector(x.bytes) ];
|
246
|
+
|
247
|
+
// type: passive
|
248
|
+
return [ 0x01, ...encodeVector(x.bytes) ];
|
249
|
+
}))
|
244
250
|
);
|
245
251
|
|
246
252
|
const dataCountSection = data.length === 0 ? [] : createSection(
|
@@ -171,12 +171,11 @@ export const __Array_prototype_filter = (_this: any[], callbackFn: any) => {
|
|
171
171
|
};
|
172
172
|
|
173
173
|
export const __Array_prototype_map = (_this: any[], callbackFn: any) => {
|
174
|
-
const out: any[] = [];
|
175
|
-
|
176
|
-
const len: i32 = _this.length;
|
177
174
|
let i: i32 = 0;
|
175
|
+
const len: i32 = _this.length;
|
176
|
+
const out: any[] = new Array(len);
|
178
177
|
while (i < len) {
|
179
|
-
out
|
178
|
+
out[i] = callbackFn(_this[i], i++, _this);
|
180
179
|
}
|
181
180
|
|
182
181
|
return out;
|
@@ -722,10 +722,9 @@ export const __Porffor_date_allocate = (): Date => {
|
|
722
722
|
const hack: bytestring = '';
|
723
723
|
|
724
724
|
if (hack.length == 0) {
|
725
|
-
hack.length = Porffor.wasm`
|
725
|
+
hack.length = Porffor.wasm`
|
726
726
|
i32.const 1
|
727
727
|
memory.grow 0
|
728
|
-
drop
|
729
728
|
i32.const 65536
|
730
729
|
i32.mul
|
731
730
|
i32.from_u`;
|
package/compiler/builtins/set.ts
CHANGED
@@ -2,10 +2,9 @@ import type {} from './porffor.d.ts';
|
|
2
2
|
|
3
3
|
// dark wasm magic for dealing with memory, sorry.
|
4
4
|
export const __Porffor_allocate = (): number => {
|
5
|
-
Porffor.wasm`
|
5
|
+
Porffor.wasm`
|
6
6
|
i32.const 1
|
7
7
|
memory.grow 0
|
8
|
-
drop
|
9
8
|
i32.const 65536
|
10
9
|
i32.mul
|
11
10
|
i32.from_u
|
package/compiler/codegen.js
CHANGED
@@ -9,7 +9,7 @@ import * as Rhemyn from '../rhemyn/compile.js';
|
|
9
9
|
import parse from './parse.js';
|
10
10
|
import { log } from './log.js';
|
11
11
|
import Prefs from './prefs.js';
|
12
|
-
import
|
12
|
+
import makeAllocator from './allocators.js';
|
13
13
|
|
14
14
|
let globals = {};
|
15
15
|
let tags = [];
|
@@ -33,11 +33,6 @@ const todo = (scope, msg, expectsValue = undefined) => {
|
|
33
33
|
|
34
34
|
case 'runtime':
|
35
35
|
return internalThrow(scope, 'TodoError', msg, expectsValue);
|
36
|
-
|
37
|
-
// return [
|
38
|
-
// ...debug(`todo! ${msg}`),
|
39
|
-
// [ Opcodes.unreachable ]
|
40
|
-
// ];
|
41
36
|
}
|
42
37
|
};
|
43
38
|
|
@@ -431,7 +426,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
431
426
|
const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
|
432
427
|
|
433
428
|
// alloc/assign array
|
434
|
-
const [ , pointer ] = makeArray(scope, {
|
429
|
+
const [ out, pointer ] = makeArray(scope, {
|
435
430
|
rawElements: new Array(0)
|
436
431
|
}, global, name, true, 'i16', true);
|
437
432
|
|
@@ -447,7 +442,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
447
442
|
[ Opcodes.local_set, rightPointer ],
|
448
443
|
|
449
444
|
// calculate length
|
450
|
-
...
|
445
|
+
...out,
|
451
446
|
|
452
447
|
[ Opcodes.local_get, leftPointer ],
|
453
448
|
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
@@ -1003,7 +998,7 @@ const asmFuncToAsm = (func, scope) => {
|
|
1003
998
|
});
|
1004
999
|
};
|
1005
1000
|
|
1006
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
|
1001
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits = [], returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
|
1007
1002
|
const existing = funcs.find(x => x.name === name);
|
1008
1003
|
if (existing) return existing;
|
1009
1004
|
|
@@ -1017,7 +1012,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1017
1012
|
|
1018
1013
|
for (const x of _data) {
|
1019
1014
|
const copy = { ...x };
|
1020
|
-
copy.offset += pages.size * pageSize;
|
1015
|
+
if (copy.offset != null) copy.offset += pages.size * pageSize;
|
1021
1016
|
data.push(copy);
|
1022
1017
|
}
|
1023
1018
|
|
@@ -1403,10 +1398,10 @@ const countLeftover = wasm => {
|
|
1403
1398
|
|
1404
1399
|
if (depth === 0)
|
1405
1400
|
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1406
|
-
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] <
|
1401
|
+
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x04)) {}
|
1407
1402
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
|
1408
1403
|
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1409
|
-
else if (Opcodes.memory_copy[0]
|
1404
|
+
else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
|
1410
1405
|
else if (inst[0] === Opcodes.return) count = 0;
|
1411
1406
|
else if (inst[0] === Opcodes.call) {
|
1412
1407
|
let func = funcs.find(x => x.index === inst[1]);
|
@@ -3117,8 +3112,6 @@ const allocPage = (scope, reason, type) => {
|
|
3117
3112
|
scope.pages ??= new Map();
|
3118
3113
|
scope.pages.set(reason, { ind, type });
|
3119
3114
|
|
3120
|
-
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
3121
|
-
|
3122
3115
|
return ind;
|
3123
3116
|
};
|
3124
3117
|
|
@@ -3156,6 +3149,46 @@ const compileBytes = (val, itemType) => {
|
|
3156
3149
|
}
|
3157
3150
|
};
|
3158
3151
|
|
3152
|
+
const makeData = (scope, elements, offset = null, itemType, initEmpty) => {
|
3153
|
+
const length = elements.length;
|
3154
|
+
|
3155
|
+
// if length is 0 memory/data will just be 0000... anyway
|
3156
|
+
if (length === 0) return false;
|
3157
|
+
|
3158
|
+
let bytes = compileBytes(length, 'i32');
|
3159
|
+
|
3160
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3161
|
+
if (elements[i] == null) continue;
|
3162
|
+
|
3163
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
3164
|
+
}
|
3165
|
+
|
3166
|
+
const obj = { bytes };
|
3167
|
+
if (offset != null) obj.offset = offset;
|
3168
|
+
|
3169
|
+
const idx = data.push(obj) - 1;
|
3170
|
+
|
3171
|
+
scope.data ??= [];
|
3172
|
+
scope.data.push(idx);
|
3173
|
+
|
3174
|
+
return { idx, size: bytes.length };
|
3175
|
+
};
|
3176
|
+
|
3177
|
+
const printStaticStr = str => {
|
3178
|
+
const out = [];
|
3179
|
+
|
3180
|
+
for (let i = 0; i < str.length; i++) {
|
3181
|
+
out.push(
|
3182
|
+
// ...number(str.charCodeAt(i)),
|
3183
|
+
...number(str.charCodeAt(i), Valtype.i32),
|
3184
|
+
Opcodes.i32_from_u,
|
3185
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3186
|
+
);
|
3187
|
+
}
|
3188
|
+
|
3189
|
+
return out;
|
3190
|
+
};
|
3191
|
+
|
3159
3192
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
|
3160
3193
|
if (itemType !== 'i16' && itemType !== 'i8') {
|
3161
3194
|
pages.hasArray = true;
|
@@ -3175,17 +3208,50 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3175
3208
|
const valtype = itemTypeToValtype[itemType];
|
3176
3209
|
const length = elements.length;
|
3177
3210
|
|
3178
|
-
const allocated = allocator.alloc({ scope, pages, globals }, uniqueName, { itemType });
|
3211
|
+
const allocated = allocator.alloc({ scope, pages, globals, asmFunc, funcIndex }, uniqueName, { itemType });
|
3179
3212
|
|
3180
3213
|
let pointer = allocated;
|
3181
3214
|
if (allocator.constructor.name !== 'StaticAllocator') {
|
3182
|
-
const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
|
3215
|
+
// const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
|
3216
|
+
const tmp = localTmp(scope, '#makearray_pointer' + name, Valtype.i32);
|
3183
3217
|
out.push(
|
3184
3218
|
...allocated,
|
3185
3219
|
[ Opcodes.local_set, tmp ]
|
3186
3220
|
);
|
3187
3221
|
|
3222
|
+
if (Prefs.runtimeAllocLog) out.push(
|
3223
|
+
...printStaticStr(`${name}: `),
|
3224
|
+
|
3225
|
+
[ Opcodes.local_get, tmp ],
|
3226
|
+
Opcodes.i32_from_u,
|
3227
|
+
[ Opcodes.call, 0 ],
|
3228
|
+
|
3229
|
+
...number(10),
|
3230
|
+
[ Opcodes.call, 1 ]
|
3231
|
+
);
|
3232
|
+
|
3188
3233
|
pointer = [ [ Opcodes.local_get, tmp ] ];
|
3234
|
+
|
3235
|
+
if (Prefs.data && useRawElements) {
|
3236
|
+
const data = makeData(scope, elements, null, itemType, initEmpty);
|
3237
|
+
if (data) {
|
3238
|
+
// init data
|
3239
|
+
out.push(
|
3240
|
+
...pointer,
|
3241
|
+
...number(0, Valtype.i32),
|
3242
|
+
...number(data.size, Valtype.i32),
|
3243
|
+
[ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
|
3244
|
+
);
|
3245
|
+
}
|
3246
|
+
|
3247
|
+
// return pointer in out
|
3248
|
+
out.push(
|
3249
|
+
...pointer,
|
3250
|
+
...(!intOut ? [ Opcodes.i32_from_u ] : [])
|
3251
|
+
);
|
3252
|
+
|
3253
|
+
return [ out, pointer ];
|
3254
|
+
}
|
3189
3255
|
} else {
|
3190
3256
|
const rawPtr = read_signedLEB128(pointer[0].slice(1));
|
3191
3257
|
|
@@ -3194,24 +3260,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3194
3260
|
if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
|
3195
3261
|
|
3196
3262
|
if (Prefs.data && firstAssign && useRawElements) {
|
3197
|
-
|
3198
|
-
if (length !== 0) {
|
3199
|
-
let bytes = compileBytes(length, 'i32');
|
3200
|
-
|
3201
|
-
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3202
|
-
if (elements[i] == null) continue;
|
3203
|
-
|
3204
|
-
bytes.push(...compileBytes(elements[i], itemType));
|
3205
|
-
}
|
3206
|
-
|
3207
|
-
const ind = data.push({
|
3208
|
-
offset: rawPtr,
|
3209
|
-
bytes
|
3210
|
-
}) - 1;
|
3211
|
-
|
3212
|
-
scope.data ??= [];
|
3213
|
-
scope.data.push(ind);
|
3214
|
-
}
|
3263
|
+
makeData(scope, elements, rawPtr, itemType, initEmpty);
|
3215
3264
|
|
3216
3265
|
// local value as pointer
|
3217
3266
|
return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
|
@@ -3694,9 +3743,9 @@ const internalConstrs = {
|
|
3694
3743
|
|
3695
3744
|
// new Array(n)
|
3696
3745
|
|
3697
|
-
const [ , pointer ] = makeArray(scope, {
|
3746
|
+
const [ out, pointer ] = makeArray(scope, {
|
3698
3747
|
rawElements: new Array(0)
|
3699
|
-
}, global, name, true);
|
3748
|
+
}, global, name, true, undefined, true);
|
3700
3749
|
|
3701
3750
|
const arg = decl.arguments[0] ?? DEFAULT_VALUE;
|
3702
3751
|
|
@@ -3705,7 +3754,7 @@ const internalConstrs = {
|
|
3705
3754
|
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
|
3706
3755
|
|
3707
3756
|
return [
|
3708
|
-
...
|
3757
|
+
...out,
|
3709
3758
|
...generate(scope, arg, global, name),
|
3710
3759
|
Opcodes.i32_to_u,
|
3711
3760
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
@@ -3895,7 +3944,7 @@ export default program => {
|
|
3895
3944
|
builtinFuncs = new BuiltinFuncs();
|
3896
3945
|
builtinVars = new BuiltinVars();
|
3897
3946
|
prototypeFuncs = new PrototypeFuncs();
|
3898
|
-
allocator =
|
3947
|
+
allocator = makeAllocator(Prefs.allocator ?? 'static');
|
3899
3948
|
|
3900
3949
|
program.id = { name: 'main' };
|
3901
3950
|
|