porffor 0.2.0-15592d6 → 0.2.0-181627c
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/compiler/builtins/base64.ts +48 -28
- package/compiler/builtins/porffor.d.ts +5 -0
- package/compiler/builtins.js +78 -1
- package/compiler/codeGen.js +28 -27
- package/compiler/index.js +8 -14
- package/compiler/opt.js +25 -24
- package/compiler/parse.js +4 -2
- package/compiler/precompile.js +19 -9
- package/compiler/prefs.js +22 -0
- package/compiler/prototype.js +8 -6
- package/compiler/sections.js +6 -5
- package/demo.js +2 -2
- package/hello +0 -0
- package/package.json +1 -1
- package/rhemyn/compile.js +2 -1
- package/tmp.c +20 -1116
@@ -2,6 +2,38 @@
|
|
2
2
|
|
3
3
|
import type { i32, bytestring } from './porffor.d.ts';
|
4
4
|
|
5
|
+
// const appendKeyChar = (target: bytestring, ind: i32): bytestring => {
|
6
|
+
// const keyStr: bytestring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
7
|
+
// return target + keyStr.charAt(ind);
|
8
|
+
// };
|
9
|
+
|
10
|
+
// const appendKeyChar = (target: bytestring, ind: i32): void => {
|
11
|
+
// const keyStr: bytestring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
12
|
+
|
13
|
+
// Porffor.bytestring.appendCharAt(target, keyStr, ind);
|
14
|
+
|
15
|
+
// Porffor.wasm`local.get ${ind}
|
16
|
+
// i32.to_u
|
17
|
+
// i32.const 2
|
18
|
+
// i32.mul
|
19
|
+
// local.get ${keyStr}
|
20
|
+
// i32.to_u
|
21
|
+
// i32.add
|
22
|
+
// i32.load8_u 0 4
|
23
|
+
|
24
|
+
// local targetI32 i32
|
25
|
+
// local.get ${target}
|
26
|
+
// i32.to_u
|
27
|
+
// local.tee targetI32
|
28
|
+
// local.get targetI32
|
29
|
+
// i32.load 1 0
|
30
|
+
// i32.const 2
|
31
|
+
// i32.mul
|
32
|
+
// i32.add
|
33
|
+
|
34
|
+
// i32.store8 0 4`;
|
35
|
+
// };
|
36
|
+
|
5
37
|
export const btoa = (input: bytestring): bytestring => {
|
6
38
|
const keyStr: bytestring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
7
39
|
|
@@ -20,37 +52,22 @@ export const btoa = (input: bytestring): bytestring => {
|
|
20
52
|
let enc3: i32 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
21
53
|
let enc4: i32 = chr3 & 63;
|
22
54
|
|
23
|
-
if (isNaN(chr2)) {
|
55
|
+
if (Number.isNaN(chr2)) {
|
24
56
|
enc3 = 64;
|
25
57
|
enc4 = 64;
|
26
|
-
} else if (isNaN(chr3)) {
|
58
|
+
} else if (Number.isNaN(chr3)) {
|
27
59
|
enc4 = 64;
|
28
60
|
}
|
29
61
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
// i32.to_u
|
35
|
-
// i32.const 2
|
36
|
-
// i32.mul
|
37
|
-
// local.get ${keyStr}
|
38
|
-
// i32.to_u
|
39
|
-
// i32.add
|
40
|
-
// i32.load16_u 0 4
|
41
|
-
|
42
|
-
// local.get ${output}
|
43
|
-
// i32.to_u
|
44
|
-
// local.get ${endPtr}
|
45
|
-
// i32.to_u
|
46
|
-
// i32.add
|
47
|
-
|
48
|
-
// i32.store16 0 4`;
|
62
|
+
Porffor.bytestring.appendCharAt(output, keyStr, enc1);
|
63
|
+
Porffor.bytestring.appendCharAt(output, keyStr, enc2);
|
64
|
+
Porffor.bytestring.appendCharAt(output, keyStr, enc3);
|
65
|
+
Porffor.bytestring.appendCharAt(output, keyStr, enc4);
|
49
66
|
|
50
|
-
output += keyStr.charAt(enc1);
|
51
|
-
output += keyStr.charAt(enc2);
|
52
|
-
output += keyStr.charAt(enc3);
|
53
|
-
output += keyStr.charAt(enc4);
|
67
|
+
// output += keyStr.charAt(enc1);
|
68
|
+
// output += keyStr.charAt(enc2);
|
69
|
+
// output += keyStr.charAt(enc3);
|
70
|
+
// output += keyStr.charAt(enc4);
|
54
71
|
}
|
55
72
|
|
56
73
|
return output;
|
@@ -74,13 +91,16 @@ export const btoa = (input: bytestring): bytestring => {
|
|
74
91
|
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
75
92
|
chr3 = ((enc3 & 3) << 6) | enc4;
|
76
93
|
|
77
|
-
output += String.fromCharCode(chr1);
|
94
|
+
// output += String.fromCharCode(chr1);
|
95
|
+
Porffor.bytestring.appendCharCode(output, chr1);
|
78
96
|
|
79
97
|
if (enc3 != 64) {
|
80
|
-
output += String.fromCharCode(chr2);
|
98
|
+
// output += String.fromCharCode(chr2);
|
99
|
+
Porffor.bytestring.appendCharCode(output, chr2);
|
81
100
|
}
|
82
101
|
if (enc4 != 64) {
|
83
|
-
output += String.fromCharCode(chr3);
|
102
|
+
// output += String.fromCharCode(chr3);
|
103
|
+
Porffor.bytestring.appendCharCode(output, chr3);
|
84
104
|
}
|
85
105
|
}
|
86
106
|
|
@@ -3,6 +3,11 @@ export type bytestring = string;
|
|
3
3
|
|
4
4
|
type PorfforGlobal = {
|
5
5
|
wasm: (...args: any[]) => void;
|
6
|
+
|
7
|
+
bytestring: {
|
8
|
+
appendCharAt: (target: bytestring, lookup: bytestring, ind: i32) => void;
|
9
|
+
appendCharCode: (target: bytestring, code: i32) => void;
|
10
|
+
}
|
6
11
|
};
|
7
12
|
|
8
13
|
declare global {
|
package/compiler/builtins.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import { Blocktype, Opcodes, Valtype, ValtypeSize } from "./wasmSpec.js";
|
2
2
|
import { number, i32x4 } from "./embedding.js";
|
3
|
+
import Prefs from './prefs.js';
|
3
4
|
|
4
5
|
export const importedFuncs = [
|
5
6
|
{
|
@@ -685,7 +686,7 @@ export const BuiltinFuncs = function() {
|
|
685
686
|
typedParams: true,
|
686
687
|
locals: [ Valtype.i32, Valtype.i32 ],
|
687
688
|
returns: [ valtypeBinary ],
|
688
|
-
returnType:
|
689
|
+
returnType: Prefs.bytestring ? '_bytestring' : 'string',
|
689
690
|
wasm: (scope, { TYPE_NAMES, typeSwitch, makeString }) => {
|
690
691
|
const bc = {};
|
691
692
|
for (const x in TYPE_NAMES) {
|
@@ -722,6 +723,82 @@ export const BuiltinFuncs = function() {
|
|
722
723
|
]
|
723
724
|
};
|
724
725
|
|
726
|
+
this.__Porffor_bytestring_appendCharAt = {
|
727
|
+
params: [ valtypeBinary, valtypeBinary, valtypeBinary ],
|
728
|
+
locals: [ Valtype.i32 ],
|
729
|
+
localNames: ['target', 'lookup', 'index', 'length'],
|
730
|
+
returns: [],
|
731
|
+
wasm: [
|
732
|
+
// get ptr for target[target.length]
|
733
|
+
[ Opcodes.local_get, 0 ],
|
734
|
+
Opcodes.i32_to_u,
|
735
|
+
[ Opcodes.local_get, 0 ],
|
736
|
+
Opcodes.i32_to_u,
|
737
|
+
[ Opcodes.i32_load, 1, 0 ],
|
738
|
+
[ Opcodes.local_tee, 3 ],
|
739
|
+
[ Opcodes.i32_const, 2 ],
|
740
|
+
[ Opcodes.i32_mul ],
|
741
|
+
[ Opcodes.i32_add ],
|
742
|
+
|
743
|
+
// get lookup[index]
|
744
|
+
[ Opcodes.local_get, 2 ],
|
745
|
+
Opcodes.i32_to_u,
|
746
|
+
[ Opcodes.i32_const, 2 ],
|
747
|
+
[ Opcodes.i32_mul ],
|
748
|
+
[ Opcodes.local_get, 1 ],
|
749
|
+
Opcodes.i32_to_u,
|
750
|
+
[ Opcodes.i32_add ],
|
751
|
+
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
|
752
|
+
|
753
|
+
// store target[target.length] = lookup[index]
|
754
|
+
[ Opcodes.i32_store8, 0, 4 ],
|
755
|
+
|
756
|
+
// store target.length = target.length + 1
|
757
|
+
[ Opcodes.local_get, 0 ],
|
758
|
+
Opcodes.i32_to_u,
|
759
|
+
[ Opcodes.local_get, 3 ],
|
760
|
+
[ Opcodes.i32_const, 1 ],
|
761
|
+
[ Opcodes.i32_add ],
|
762
|
+
[ Opcodes.i32_store, 0, 0 ],
|
763
|
+
]
|
764
|
+
};
|
765
|
+
|
766
|
+
this.__Porffor_bytestring_appendCharCode = {
|
767
|
+
params: [ valtypeBinary, valtypeBinary ],
|
768
|
+
locals: [ Valtype.i32 ],
|
769
|
+
localnames: ['target', 'code', 'length'],
|
770
|
+
returns: [],
|
771
|
+
wasm: [
|
772
|
+
// get ptr for target[target.length]
|
773
|
+
[ Opcodes.local_get, 0 ],
|
774
|
+
Opcodes.i32_to_u,
|
775
|
+
[ Opcodes.local_get, 0 ],
|
776
|
+
Opcodes.i32_to_u,
|
777
|
+
[ Opcodes.i32_load, 1, 0 ],
|
778
|
+
[ Opcodes.local_tee, 3 ],
|
779
|
+
[ Opcodes.i32_const, 2 ],
|
780
|
+
[ Opcodes.i32_mul ],
|
781
|
+
[ Opcodes.i32_add ],
|
782
|
+
|
783
|
+
// get code
|
784
|
+
[ Opcodes.local_get, 1 ],
|
785
|
+
[ Opcodes.i32_to_u ],
|
786
|
+
|
787
|
+
// set target[target.length] = code
|
788
|
+
[ Opcodes.i32_store8, 0, 4 ],
|
789
|
+
|
790
|
+
// store target.length = target.length + 1
|
791
|
+
[ Opcodes.local_get, 0 ],
|
792
|
+
Opcodes.i32_to_u,
|
793
|
+
[ Opcodes.local_get, 3 ],
|
794
|
+
[ Opcodes.i32_const, 1 ],
|
795
|
+
[ Opcodes.i32_add ],
|
796
|
+
[ Opcodes.i32_store, 0, 0 ],
|
797
|
+
]
|
798
|
+
};
|
799
|
+
|
800
|
+
// todo: indexOfCharAt
|
801
|
+
|
725
802
|
|
726
803
|
this.__SIMD_i32x4_load = {
|
727
804
|
params: [ Valtype.i32 ],
|
package/compiler/codeGen.js
CHANGED
@@ -7,6 +7,7 @@ import { number, i32x4, enforceOneByte, enforceTwoBytes, enforceFourBytes, enfor
|
|
7
7
|
import { log } from "./log.js";
|
8
8
|
import parse from "./parse.js";
|
9
9
|
import * as Rhemyn from "../rhemyn/compile.js";
|
10
|
+
import Prefs from './prefs.js';
|
10
11
|
|
11
12
|
let globals = {};
|
12
13
|
let globalInd = 0;
|
@@ -168,8 +169,8 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
168
169
|
if (asm[0] === '') continue; // blank
|
169
170
|
|
170
171
|
if (asm[0] === 'local') {
|
171
|
-
const [ name,
|
172
|
-
scope.locals[name] = { idx:
|
172
|
+
const [ name, type ] = asm.slice(1);
|
173
|
+
scope.locals[name] = { idx: scope.localInd++, type: Valtype[type] };
|
173
174
|
continue;
|
174
175
|
}
|
175
176
|
|
@@ -188,7 +189,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
188
189
|
if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
189
190
|
|
190
191
|
if (!Array.isArray(inst)) inst = [ inst ];
|
191
|
-
const immediates = asm.slice(1).map(x => parseInt(x));
|
192
|
+
const immediates = asm.slice(1).map(x => parseInt(x) || scope.locals[x]?.idx);
|
192
193
|
|
193
194
|
out.push([ ...inst, ...immediates ]);
|
194
195
|
}
|
@@ -419,9 +420,6 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
419
420
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
420
421
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
421
422
|
|
422
|
-
const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
|
423
|
-
if (aotWFA) addVarMeta(name, { wellFormed: undefined });
|
424
|
-
|
425
423
|
if (assign) {
|
426
424
|
const pointer = arrays.get(name ?? '$undeclared');
|
427
425
|
|
@@ -1273,7 +1271,7 @@ const getNodeType = (scope, node) => {
|
|
1273
1271
|
if (node.operator === '!') return TYPES.boolean;
|
1274
1272
|
if (node.operator === 'void') return TYPES.undefined;
|
1275
1273
|
if (node.operator === 'delete') return TYPES.boolean;
|
1276
|
-
if (node.operator === 'typeof') return
|
1274
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES._bytestring : TYPES.string;
|
1277
1275
|
|
1278
1276
|
return TYPES.number;
|
1279
1277
|
}
|
@@ -1836,14 +1834,14 @@ const brTable = (input, bc, returns) => {
|
|
1836
1834
|
};
|
1837
1835
|
|
1838
1836
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1839
|
-
if (!
|
1837
|
+
if (!Prefs.bytestring) delete bc[TYPES._bytestring];
|
1840
1838
|
|
1841
1839
|
const known = knownType(scope, type);
|
1842
1840
|
if (known != null) {
|
1843
1841
|
return bc[known] ?? bc.default;
|
1844
1842
|
}
|
1845
1843
|
|
1846
|
-
if (
|
1844
|
+
if (Prefs.typeswitchUseBrtable)
|
1847
1845
|
return brTable(type, bc, returns);
|
1848
1846
|
|
1849
1847
|
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
@@ -1938,7 +1936,7 @@ const extractTypeAnnotation = decl => {
|
|
1938
1936
|
const typeName = type;
|
1939
1937
|
type = typeAnnoToPorfType(type);
|
1940
1938
|
|
1941
|
-
if (type === TYPES._bytestring && !
|
1939
|
+
if (type === TYPES._bytestring && !Prefs.bytestring) type = TYPES.string;
|
1942
1940
|
|
1943
1941
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1944
1942
|
|
@@ -2631,7 +2629,7 @@ const allocPage = (scope, reason, type) => {
|
|
2631
2629
|
scope.pages ??= new Map();
|
2632
2630
|
scope.pages.set(reason, { ind, type });
|
2633
2631
|
|
2634
|
-
if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2632
|
+
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2635
2633
|
|
2636
2634
|
return ind;
|
2637
2635
|
};
|
@@ -2641,7 +2639,7 @@ const freePage = reason => {
|
|
2641
2639
|
const { ind } = pages.get(reason);
|
2642
2640
|
pages.delete(reason);
|
2643
2641
|
|
2644
|
-
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2642
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2645
2643
|
|
2646
2644
|
return ind;
|
2647
2645
|
};
|
@@ -2710,21 +2708,24 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2710
2708
|
const length = elements.length;
|
2711
2709
|
|
2712
2710
|
if (firstAssign && useRawElements) {
|
2713
|
-
|
2711
|
+
// if length is 0 memory/data will just be 0000... anyway
|
2712
|
+
if (length !== 0) {
|
2713
|
+
let bytes = compileBytes(length, 'i32');
|
2714
2714
|
|
2715
|
-
|
2716
|
-
|
2715
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
2716
|
+
if (elements[i] == null) continue;
|
2717
2717
|
|
2718
|
-
|
2719
|
-
|
2718
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
2719
|
+
}
|
2720
2720
|
|
2721
|
-
|
2722
|
-
|
2723
|
-
|
2724
|
-
|
2721
|
+
const ind = data.push({
|
2722
|
+
offset: pointer,
|
2723
|
+
bytes
|
2724
|
+
}) - 1;
|
2725
2725
|
|
2726
|
-
|
2727
|
-
|
2726
|
+
scope.data ??= [];
|
2727
|
+
scope.data.push(ind);
|
2728
|
+
}
|
2728
2729
|
|
2729
2730
|
// local value as pointer
|
2730
2731
|
out.push(...number(pointer));
|
@@ -2758,7 +2759,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2758
2759
|
};
|
2759
2760
|
|
2760
2761
|
const byteStringable = str => {
|
2761
|
-
if (!
|
2762
|
+
if (!Prefs.bytestring) return false;
|
2762
2763
|
|
2763
2764
|
for (let i = 0; i < str.length; i++) {
|
2764
2765
|
if (str.charCodeAt(i) > 0xFF) return false;
|
@@ -2769,7 +2770,7 @@ const byteStringable = str => {
|
|
2769
2770
|
|
2770
2771
|
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2771
2772
|
const rawElements = new Array(str.length);
|
2772
|
-
let byteStringable =
|
2773
|
+
let byteStringable = Prefs.bytestring;
|
2773
2774
|
for (let i = 0; i < str.length; i++) {
|
2774
2775
|
const c = str.charCodeAt(i);
|
2775
2776
|
rawElements[i] = c;
|
@@ -2930,7 +2931,7 @@ const objectHack = node => {
|
|
2930
2931
|
if (!objectName) return node;
|
2931
2932
|
|
2932
2933
|
const name = '__' + objectName + '_' + node.property.name;
|
2933
|
-
if (codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2934
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2934
2935
|
|
2935
2936
|
return {
|
2936
2937
|
type: 'Identifier',
|
@@ -3157,7 +3158,7 @@ export default program => {
|
|
3157
3158
|
body: program.body
|
3158
3159
|
};
|
3159
3160
|
|
3160
|
-
if (
|
3161
|
+
if (Prefs.astLog) console.log(program.body.body);
|
3161
3162
|
|
3162
3163
|
generateFunc(scope, program);
|
3163
3164
|
|
package/compiler/index.js
CHANGED
@@ -5,6 +5,7 @@ import opt from './opt.js';
|
|
5
5
|
import produceSections from './sections.js';
|
6
6
|
import decompile from './decompile.js';
|
7
7
|
import toc from './2c.js';
|
8
|
+
import Prefs from './prefs.js';
|
8
9
|
|
9
10
|
globalThis.decompile = decompile;
|
10
11
|
|
@@ -25,17 +26,10 @@ const logFuncs = (funcs, globals, exceptions) => {
|
|
25
26
|
console.log();
|
26
27
|
};
|
27
28
|
|
28
|
-
const getArg = name => process.argv.find(x => x.startsWith(`-${name}=`))?.slice(name.length + 2);
|
29
|
-
|
30
29
|
const writeFileSync = (typeof process?.version !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
|
31
30
|
const execSync = (typeof process?.version !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
|
32
31
|
|
33
32
|
export default (code, flags) => {
|
34
|
-
globalThis.optLog = process.argv.includes('-opt-log');
|
35
|
-
globalThis.codeLog = process.argv.includes('-code-log');
|
36
|
-
globalThis.allocLog = process.argv.includes('-alloc-log');
|
37
|
-
globalThis.regexLog = process.argv.includes('-regex-log');
|
38
|
-
|
39
33
|
const t0 = performance.now();
|
40
34
|
const program = parse(code, flags);
|
41
35
|
if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
|
@@ -44,19 +38,19 @@ export default (code, flags) => {
|
|
44
38
|
const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
|
45
39
|
if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
|
46
40
|
|
47
|
-
if (
|
41
|
+
if (Prefs.funcs) logFuncs(funcs, globals, exceptions);
|
48
42
|
|
49
43
|
const t2 = performance.now();
|
50
44
|
opt(funcs, globals, pages, tags, exceptions);
|
51
45
|
if (flags.includes('info')) console.log(`3. optimized code in ${(performance.now() - t2).toFixed(2)}ms`);
|
52
46
|
|
53
|
-
if (
|
47
|
+
if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
|
54
48
|
|
55
49
|
const t3 = performance.now();
|
56
50
|
const sections = produceSections(funcs, globals, tags, pages, data, flags);
|
57
51
|
if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
|
58
52
|
|
59
|
-
if (allocLog) {
|
53
|
+
if (Prefs.allocLog) {
|
60
54
|
const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
|
61
55
|
const bytes = wasmPages * 65536;
|
62
56
|
log('alloc', `\x1B[1mallocated ${bytes / 1024}KiB\x1B[0m for ${pages.size} things using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}`);
|
@@ -65,8 +59,8 @@ export default (code, flags) => {
|
|
65
59
|
|
66
60
|
const out = { wasm: sections, funcs, globals, tags, exceptions, pages, data };
|
67
61
|
|
68
|
-
const target =
|
69
|
-
const outFile =
|
62
|
+
const target = Prefs.target ?? 'wasm';
|
63
|
+
const outFile = Prefs.o;
|
70
64
|
|
71
65
|
if (target === 'wasm' && outFile) {
|
72
66
|
writeFileSync(outFile, Buffer.from(sections));
|
@@ -88,8 +82,8 @@ export default (code, flags) => {
|
|
88
82
|
}
|
89
83
|
|
90
84
|
if (target === 'native') {
|
91
|
-
let compiler =
|
92
|
-
const cO =
|
85
|
+
let compiler = Prefs.compiler ?? 'clang';
|
86
|
+
const cO = Prefs._cO ?? 'Ofast';
|
93
87
|
|
94
88
|
if (compiler === 'zig') compiler = [ 'zig', 'cc' ];
|
95
89
|
else compiler = [ compiler ];
|
package/compiler/opt.js
CHANGED
@@ -2,6 +2,7 @@ import { Opcodes, Valtype } from "./wasmSpec.js";
|
|
2
2
|
import { number } from "./embedding.js";
|
3
3
|
import { read_signedLEB128, read_ieee754_binary64 } from "./encoding.js";
|
4
4
|
import { log } from "./log.js";
|
5
|
+
import Prefs from './prefs.js';
|
5
6
|
|
6
7
|
const performWasmOp = (op, a, b) => {
|
7
8
|
switch (op) {
|
@@ -15,17 +16,17 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
15
16
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
16
17
|
if (optLevel === 0) return;
|
17
18
|
|
18
|
-
const tailCall =
|
19
|
+
const tailCall = Prefs.tailCall;
|
19
20
|
if (tailCall) log.warning('opt', 'tail call proposal is not widely implemented! (you used -tail-call)');
|
20
21
|
|
21
|
-
if (optLevel >= 2 && !
|
22
|
+
if (optLevel >= 2 && !Prefs.optNoInline) {
|
22
23
|
// inline pass (very WIP)
|
23
24
|
// get candidates for inlining
|
24
25
|
// todo: pick smart in future (if func is used <N times? or?)
|
25
26
|
const callsSelf = f => f.wasm.some(x => x[0] === Opcodes.call && x[1] === f.index);
|
26
27
|
const suitableReturns = wasm => wasm.reduce((acc, x) => acc + (x[0] === Opcodes.return), 0) <= 1;
|
27
28
|
const candidates = funcs.filter(x => x.name !== 'main' && Object.keys(x.locals).length === x.params.length && (x.returns.length === 0 || suitableReturns(x.wasm)) && !callsSelf(x) && !x.throws).reverse();
|
28
|
-
if (optLog) {
|
29
|
+
if (Prefs.optLog) {
|
29
30
|
log('opt', `found inline candidates: ${candidates.map(x => x.name).join(', ')} (${candidates.length}/${funcs.length - 1})`);
|
30
31
|
|
31
32
|
let reasons = {};
|
@@ -53,7 +54,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
53
54
|
for (let i = 0; i < tWasm.length; i++) {
|
54
55
|
const inst = tWasm[i];
|
55
56
|
if (inst[0] === Opcodes.call && inst[1] === c.index) {
|
56
|
-
if (optLog) log('opt', `inlining call for ${c.name} (in ${t.name})`);
|
57
|
+
if (Prefs.optLog) log('opt', `inlining call for ${c.name} (in ${t.name})`);
|
57
58
|
tWasm.splice(i, 1); // remove this call
|
58
59
|
|
59
60
|
// add params as locals and set in reverse order
|
@@ -80,7 +81,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
80
81
|
// adjust local operands to go to correct param index
|
81
82
|
for (const inst of iWasm) {
|
82
83
|
if ((inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set) && inst[1] < c.params.length) {
|
83
|
-
if (optLog) log('opt', `replacing local operand in inlined wasm (${inst[1]} -> ${paramIdx[inst[1]]})`);
|
84
|
+
if (Prefs.optLog) log('opt', `replacing local operand in inlined wasm (${inst[1]} -> ${paramIdx[inst[1]]})`);
|
84
85
|
inst[1] = paramIdx[inst[1]];
|
85
86
|
}
|
86
87
|
}
|
@@ -97,7 +98,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
97
98
|
}
|
98
99
|
}
|
99
100
|
|
100
|
-
if (
|
101
|
+
if (Prefs.optInlineOnly) return;
|
101
102
|
|
102
103
|
const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
|
103
104
|
const exceptionUse = exceptions.reduce((acc, _, i) => { acc[i] = 0; return acc; }, {});
|
@@ -166,7 +167,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
166
167
|
|
167
168
|
wasm.splice(j - 1, 1); // remove end of this block
|
168
169
|
|
169
|
-
if (optLog) log('opt', `removed unneeded block in for loop`);
|
170
|
+
if (Prefs.optLog) log('opt', `removed unneeded block in for loop`);
|
170
171
|
}
|
171
172
|
}
|
172
173
|
|
@@ -215,7 +216,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
215
216
|
i -= 4;
|
216
217
|
inst = wasm[i];
|
217
218
|
|
218
|
-
if (optLog) log('opt', `removed unneeded typeswitch check`);
|
219
|
+
if (Prefs.optLog) log('opt', `removed unneeded typeswitch check`);
|
219
220
|
}
|
220
221
|
}
|
221
222
|
|
@@ -238,7 +239,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
238
239
|
wasm.splice(j - 1, 2, [ Opcodes.drop ]); // remove typeswitch start
|
239
240
|
wasm.splice(i - 1, 1); // remove this inst
|
240
241
|
|
241
|
-
if (optLog) log('opt', 'removed unneeded entire typeswitch');
|
242
|
+
if (Prefs.optLog) log('opt', 'removed unneeded entire typeswitch');
|
242
243
|
|
243
244
|
if (i > 0) i--;
|
244
245
|
continue;
|
@@ -267,7 +268,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
267
268
|
|
268
269
|
getCount[inst[1]]--;
|
269
270
|
i--;
|
270
|
-
// if (optLog) log('opt', `consolidated set, get -> tee`);
|
271
|
+
// if (Prefs.optLog) log('opt', `consolidated set, get -> tee`);
|
271
272
|
continue;
|
272
273
|
}
|
273
274
|
|
@@ -335,7 +336,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
335
336
|
|
336
337
|
wasm.splice(i - 1, 2); // remove this inst and last
|
337
338
|
i -= 2;
|
338
|
-
// if (optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
|
339
|
+
// if (Prefs.optLog) log('opt', `removed redundant i32 -> i64 -> i32 conversion ops`);
|
339
340
|
continue;
|
340
341
|
}
|
341
342
|
|
@@ -348,7 +349,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
348
349
|
|
349
350
|
wasm.splice(i - 1, 2); // remove this inst and last
|
350
351
|
i -= 2;
|
351
|
-
// if (optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
|
352
|
+
// if (Prefs.optLog) log('opt', `removed redundant i32 -> f64 -> i32 conversion ops`);
|
352
353
|
continue;
|
353
354
|
}
|
354
355
|
|
@@ -363,7 +364,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
363
364
|
|
364
365
|
wasm.splice(i, 1); // remove this inst
|
365
366
|
i--;
|
366
|
-
if (optLog) log('opt', `converted const -> i32 convert into i32 const`);
|
367
|
+
if (Prefs.optLog) log('opt', `converted const -> i32 convert into i32 const`);
|
367
368
|
continue;
|
368
369
|
}
|
369
370
|
|
@@ -378,7 +379,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
378
379
|
|
379
380
|
wasm.splice(i, 1); // remove this inst
|
380
381
|
i--;
|
381
|
-
if (optLog) log('opt', `converted i32 const -> convert into const`);
|
382
|
+
if (Prefs.optLog) log('opt', `converted i32 const -> convert into const`);
|
382
383
|
continue;
|
383
384
|
}
|
384
385
|
|
@@ -393,7 +394,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
393
394
|
|
394
395
|
wasm.splice(i, 1); // remove this inst (return)
|
395
396
|
i--;
|
396
|
-
if (optLog) log('opt', `tail called return, call`);
|
397
|
+
if (Prefs.optLog) log('opt', `tail called return, call`);
|
397
398
|
continue;
|
398
399
|
}
|
399
400
|
|
@@ -406,7 +407,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
406
407
|
|
407
408
|
wasm.splice(i, 1); // remove this inst (return)
|
408
409
|
i--;
|
409
|
-
// if (optLog) log('opt', `removed redundant return at end`);
|
410
|
+
// if (Prefs.optLog) log('opt', `removed redundant return at end`);
|
410
411
|
continue;
|
411
412
|
}
|
412
413
|
|
@@ -436,7 +437,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
436
437
|
// <nothing>
|
437
438
|
|
438
439
|
wasm.splice(i - 2, 3); // remove this, last, 2nd last insts
|
439
|
-
if (optLog) log('opt', `removed redundant inline param local handling`);
|
440
|
+
if (Prefs.optLog) log('opt', `removed redundant inline param local handling`);
|
440
441
|
i -= 3;
|
441
442
|
continue;
|
442
443
|
}
|
@@ -444,12 +445,12 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
444
445
|
|
445
446
|
if (optLevel < 2) continue;
|
446
447
|
|
447
|
-
if (optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
|
448
|
+
if (Prefs.optLog) log('opt', `get counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${getCount[f.locals[x].idx]}`).join(', ')}`);
|
448
449
|
|
449
450
|
// remove unneeded var: remove pass
|
450
451
|
// locals only got once. we don't need to worry about sets/else as these are only candidates and we will check for matching set + get insts in wasm
|
451
452
|
let unneededCandidates = Object.keys(getCount).filter(x => getCount[x] === 0 || (getCount[x] === 1 && setCount[x] === 0)).map(x => parseInt(x));
|
452
|
-
if (optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
|
453
|
+
if (Prefs.optLog) log('opt', `found unneeded locals candidates: ${unneededCandidates.join(', ')} (${unneededCandidates.length}/${Object.keys(getCount).length})`);
|
453
454
|
|
454
455
|
// note: disabled for now due to instability
|
455
456
|
if (unneededCandidates.length > 0 && false) for (let i = 0; i < wasm.length; i++) {
|
@@ -467,7 +468,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
467
468
|
wasm.splice(i - 1, 2); // remove insts
|
468
469
|
i -= 2;
|
469
470
|
delete f.locals[Object.keys(f.locals)[inst[1]]]; // remove from locals
|
470
|
-
if (optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
|
471
|
+
if (Prefs.optLog) log('opt', `removed redundant local (get set ${inst[1]})`);
|
471
472
|
}
|
472
473
|
|
473
474
|
if (inst[0] === Opcodes.local_tee && unneededCandidates.includes(inst[1])) {
|
@@ -495,7 +496,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
495
496
|
unneededCandidates.splice(unneededCandidates.indexOf(inst[1]), 1);
|
496
497
|
unneededCandidates = unneededCandidates.map(x => x > removedIdx ? (x - 1) : x);
|
497
498
|
|
498
|
-
if (optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
|
499
|
+
if (Prefs.optLog) log('opt', `removed redundant local ${localName} (tee ${inst[1]})`);
|
499
500
|
}
|
500
501
|
}
|
501
502
|
|
@@ -531,7 +532,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
531
532
|
let b = lastInst[1];
|
532
533
|
|
533
534
|
const val = performWasmOp(inst[0], a, b);
|
534
|
-
if (optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
|
535
|
+
if (Prefs.optLog) log('opt', `inlined math op (${a} ${inst[0].toString(16)} ${b} -> ${val})`);
|
535
536
|
|
536
537
|
wasm.splice(i - 2, 3, ...number(val)); // remove consts, math op and add new const
|
537
538
|
i -= 2;
|
@@ -543,12 +544,12 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
543
544
|
for (const x in useCount) {
|
544
545
|
if (useCount[x] === 0) {
|
545
546
|
const name = Object.keys(f.locals)[localIdxs.indexOf(parseInt(x))];
|
546
|
-
if (optLog) log('opt', `removed internal local ${x} (${name})`);
|
547
|
+
if (Prefs.optLog) log('opt', `removed internal local ${x} (${name})`);
|
547
548
|
delete f.locals[name];
|
548
549
|
}
|
549
550
|
}
|
550
551
|
|
551
|
-
if (optLog) log('opt', `final use counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${useCount[f.locals[x].idx]}`).join(', ')}`);
|
552
|
+
if (Prefs.optLog) log('opt', `final use counts: ${Object.keys(f.locals).map(x => `${x} (${f.locals[x].idx}): ${useCount[f.locals[x].idx]}`).join(', ')}`);
|
552
553
|
}
|
553
554
|
}
|
554
555
|
|
package/compiler/parse.js
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
import { log } from "./log.js";
|
2
|
+
import Prefs from './prefs.js';
|
3
|
+
|
2
4
|
// import { parse } from 'acorn';
|
3
5
|
|
4
6
|
// deno compat
|
@@ -8,8 +10,8 @@ if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
|
8
10
|
}
|
9
11
|
|
10
12
|
// should we try to support types (while parsing)
|
11
|
-
const types =
|
12
|
-
globalThis.typedInput = types &&
|
13
|
+
const types = Prefs.parseTypes;
|
14
|
+
globalThis.typedInput = types && Prefs.optTypes;
|
13
15
|
|
14
16
|
// todo: review which to use by default
|
15
17
|
// supported parsers:
|