porffor 0.0.0-425ea20 → 0.0.0-5594e9d
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 +31 -12
- package/c +0 -0
- package/c.exe +0 -0
- package/compiler/2c.js +350 -0
- package/compiler/builtins.js +6 -1
- package/compiler/codeGen.js +264 -60
- package/compiler/decompile.js +1 -1
- package/compiler/index.js +44 -2
- package/compiler/opt.js +1 -1
- package/compiler/parse.js +1 -0
- package/compiler/prototype.js +2 -1
- package/compiler/sections.js +25 -0
- package/compiler/wrap.js +12 -3
- package/cool.exe +0 -0
- package/g +0 -0
- package/g.exe +0 -0
- package/hi.c +37 -0
- package/out +0 -0
- package/out.exe +0 -0
- package/package.json +1 -1
- package/r.js +1 -0
- package/rhemyn/README.md +37 -0
- package/rhemyn/compile.js +214 -0
- package/rhemyn/parse.js +321 -0
- package/rhemyn/test/parse.js +59 -0
- package/runner/index.js +52 -40
- package/runner/transform.js +2 -1
- package/tmp.c +58 -0
package/compiler/codeGen.js
CHANGED
@@ -5,6 +5,7 @@ import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from "./bui
|
|
5
5
|
import { PrototypeFuncs } from "./prototype.js";
|
6
6
|
import { number, i32x4 } from "./embedding.js";
|
7
7
|
import parse from "./parse.js";
|
8
|
+
import * as Rhemyn from "../rhemyn/compile.js";
|
8
9
|
|
9
10
|
let globals = {};
|
10
11
|
let globalInd = 0;
|
@@ -108,8 +109,8 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
108
109
|
case 'WhileStatement':
|
109
110
|
return generateWhile(scope, decl);
|
110
111
|
|
111
|
-
|
112
|
-
return generateForOf(scope, decl);
|
112
|
+
case 'ForOfStatement':
|
113
|
+
return generateForOf(scope, decl);
|
113
114
|
|
114
115
|
case 'BreakStatement':
|
115
116
|
return generateBreak(scope, decl);
|
@@ -151,44 +152,65 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
151
152
|
|
152
153
|
return [];
|
153
154
|
|
154
|
-
case 'TaggedTemplateExpression':
|
155
|
-
|
156
|
-
|
155
|
+
case 'TaggedTemplateExpression': {
|
156
|
+
const funcs = {
|
157
|
+
asm: str => {
|
158
|
+
let out = [];
|
157
159
|
|
158
|
-
|
159
|
-
|
160
|
+
for (const line of str.split('\n')) {
|
161
|
+
const asm = line.trim().split(';;')[0].split(' ');
|
162
|
+
if (asm[0] === '') continue; // blank
|
160
163
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
+
if (asm[0] === 'local') {
|
165
|
+
const [ name, idx, type ] = asm.slice(1);
|
166
|
+
scope.locals[name] = { idx: parseInt(idx), type: Valtype[type] };
|
167
|
+
continue;
|
168
|
+
}
|
164
169
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
}
|
170
|
+
if (asm[0] === 'returns') {
|
171
|
+
scope.returns = asm.slice(1).map(x => Valtype[x]);
|
172
|
+
continue;
|
173
|
+
}
|
170
174
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
+
if (asm[0] === 'memory') {
|
176
|
+
allocPage('asm instrinsic');
|
177
|
+
// todo: add to store/load offset insts
|
178
|
+
continue;
|
179
|
+
}
|
175
180
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
+
let inst = Opcodes[asm[0].replace('.', '_')];
|
182
|
+
if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
183
|
+
|
184
|
+
if (!Array.isArray(inst)) inst = [ inst ];
|
185
|
+
const immediates = asm.slice(1).map(x => parseInt(x));
|
186
|
+
|
187
|
+
out.push([ ...inst, ...immediates ]);
|
188
|
+
}
|
181
189
|
|
182
|
-
|
183
|
-
|
190
|
+
return out;
|
191
|
+
},
|
184
192
|
|
185
|
-
|
186
|
-
|
193
|
+
__internal_print_type: str => {
|
194
|
+
const type = getType(scope, str) - TYPES.number;
|
187
195
|
|
188
|
-
|
196
|
+
return [
|
197
|
+
...number(type),
|
198
|
+
[ Opcodes.call, importedFuncs.print ],
|
199
|
+
|
200
|
+
// newline
|
201
|
+
...number(10),
|
202
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
203
|
+
];
|
204
|
+
}
|
189
205
|
}
|
190
206
|
|
191
|
-
|
207
|
+
const name = decl.tag.name;
|
208
|
+
// hack for inline asm
|
209
|
+
if (!funcs[name]) return todo('tagged template expressions not implemented');
|
210
|
+
|
211
|
+
const str = decl.quasi.quasis[0].value.raw;
|
212
|
+
return funcs[name](str);
|
213
|
+
}
|
192
214
|
|
193
215
|
default:
|
194
216
|
return todo(`no generation for ${decl.type}!`);
|
@@ -731,21 +753,15 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
731
753
|
];
|
732
754
|
};
|
733
755
|
|
734
|
-
let binaryExpDepth = 0;
|
735
756
|
const generateBinaryExp = (scope, decl, _global, _name) => {
|
736
|
-
|
737
|
-
|
738
|
-
const out = [
|
739
|
-
...performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name)
|
740
|
-
];
|
757
|
+
const out = performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name);
|
741
758
|
|
742
759
|
if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
|
743
760
|
|
744
|
-
binaryExpDepth--;
|
745
761
|
return out;
|
746
762
|
};
|
747
763
|
|
748
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType,
|
764
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
|
749
765
|
const existing = funcs.find(x => x.name === name);
|
750
766
|
if (existing) return existing;
|
751
767
|
|
@@ -781,7 +797,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
781
797
|
returns,
|
782
798
|
returnType: TYPES[returnType ?? 'number'],
|
783
799
|
wasm,
|
784
|
-
memory,
|
785
800
|
internal: true,
|
786
801
|
index: currentFuncIndex++
|
787
802
|
};
|
@@ -814,7 +829,8 @@ const TYPES = {
|
|
814
829
|
bigint: 0xffffffffffff7,
|
815
830
|
|
816
831
|
// these are not "typeof" types but tracked internally
|
817
|
-
_array:
|
832
|
+
_array: 0xfffffffffff0f,
|
833
|
+
_regexp: 0xfffffffffff1f
|
818
834
|
};
|
819
835
|
|
820
836
|
const TYPE_NAMES = {
|
@@ -849,6 +865,8 @@ const getType = (scope, _name) => {
|
|
849
865
|
const getNodeType = (scope, node) => {
|
850
866
|
if (node.type === 'Literal') {
|
851
867
|
if (['number', 'boolean', 'string', 'undefined', 'object', 'function', 'symbol', 'bigint'].includes(node.value)) return TYPES.number;
|
868
|
+
if (node.regex) return TYPES._regexp;
|
869
|
+
|
852
870
|
return TYPES[typeof node.value];
|
853
871
|
}
|
854
872
|
|
@@ -882,6 +900,11 @@ const getNodeType = (scope, node) => {
|
|
882
900
|
|
883
901
|
// literal.func()
|
884
902
|
if (!name && node.callee.type === 'MemberExpression') {
|
903
|
+
if (node.callee.object.regex) {
|
904
|
+
const funcName = node.callee.property.name;
|
905
|
+
return Rhemyn[funcName] ? TYPES.boolean : TYPES.undefined;
|
906
|
+
}
|
907
|
+
|
885
908
|
const baseType = getNodeType(scope, node.callee.object);
|
886
909
|
|
887
910
|
const func = node.callee.property.name;
|
@@ -925,6 +948,11 @@ const getNodeType = (scope, node) => {
|
|
925
948
|
const generateLiteral = (scope, decl, global, name) => {
|
926
949
|
if (decl.value === null) return number(NULL);
|
927
950
|
|
951
|
+
if (decl.regex) {
|
952
|
+
scope.regex[name] = decl.regex;
|
953
|
+
return number(1);
|
954
|
+
}
|
955
|
+
|
928
956
|
switch (typeof decl.value) {
|
929
957
|
case 'number':
|
930
958
|
return number(decl.value);
|
@@ -986,6 +1014,8 @@ const countLeftover = wasm => {
|
|
986
1014
|
} else count--;
|
987
1015
|
if (func) count += func.returns.length;
|
988
1016
|
} else count--;
|
1017
|
+
|
1018
|
+
// console.log(count, decompile([ inst ]).slice(0, -1));
|
989
1019
|
}
|
990
1020
|
|
991
1021
|
return count;
|
@@ -1084,6 +1114,25 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1084
1114
|
|
1085
1115
|
// literal.func()
|
1086
1116
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1117
|
+
// megahack for /regex/.func()
|
1118
|
+
if (decl.callee.object.regex) {
|
1119
|
+
const funcName = decl.callee.property.name;
|
1120
|
+
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1121
|
+
|
1122
|
+
funcIndex[func.name] = func.index;
|
1123
|
+
funcs.push(func);
|
1124
|
+
|
1125
|
+
return [
|
1126
|
+
// make string arg
|
1127
|
+
...generate(scope, decl.arguments[0]),
|
1128
|
+
|
1129
|
+
// call regex func
|
1130
|
+
Opcodes.i32_to_u,
|
1131
|
+
[ Opcodes.call, func.index ],
|
1132
|
+
Opcodes.i32_from
|
1133
|
+
];
|
1134
|
+
}
|
1135
|
+
|
1087
1136
|
baseType = getNodeType(scope, decl.callee.object);
|
1088
1137
|
|
1089
1138
|
const func = decl.callee.property.name;
|
@@ -1096,6 +1145,31 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1096
1145
|
baseName = [...arrays.keys()].pop();
|
1097
1146
|
}
|
1098
1147
|
|
1148
|
+
if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
|
1149
|
+
const func = Rhemyn[protoName](decl.arguments[0].regex.pattern, currentFuncIndex++);
|
1150
|
+
|
1151
|
+
funcIndex[func.name] = func.index;
|
1152
|
+
funcs.push(func);
|
1153
|
+
|
1154
|
+
const pointer = arrays.get(baseName);
|
1155
|
+
const [ local, isGlobal ] = lookupName(scope, baseName);
|
1156
|
+
|
1157
|
+
return [
|
1158
|
+
...out,
|
1159
|
+
|
1160
|
+
...(pointer == null ? [
|
1161
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1162
|
+
Opcodes.i32_to_u,
|
1163
|
+
] : [
|
1164
|
+
...number(pointer, Valtype.i32)
|
1165
|
+
]),
|
1166
|
+
|
1167
|
+
// call regex func
|
1168
|
+
[ Opcodes.call, func.index ],
|
1169
|
+
Opcodes.i32_from
|
1170
|
+
];
|
1171
|
+
}
|
1172
|
+
|
1099
1173
|
if (protoFunc) {
|
1100
1174
|
let pointer = arrays.get(baseName);
|
1101
1175
|
|
@@ -1345,6 +1419,47 @@ const generateAssign = (scope, decl) => {
|
|
1345
1419
|
];
|
1346
1420
|
}
|
1347
1421
|
|
1422
|
+
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
1423
|
+
// arr[i] | str[i]
|
1424
|
+
const name = decl.left.object.name;
|
1425
|
+
const pointer = arrays.get(name);
|
1426
|
+
|
1427
|
+
const aotPointer = pointer != null;
|
1428
|
+
|
1429
|
+
const newValueTmp = localTmp(scope, '__member_setter_tmp');
|
1430
|
+
|
1431
|
+
const parentType = getNodeType(scope, decl.left.object);
|
1432
|
+
|
1433
|
+
const op = decl.operator.slice(0, -1);
|
1434
|
+
return [
|
1435
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
1436
|
+
...generate(scope, decl.left.object),
|
1437
|
+
Opcodes.i32_to_u
|
1438
|
+
]),
|
1439
|
+
|
1440
|
+
// get index as valtype
|
1441
|
+
...generate(scope, decl.left.property),
|
1442
|
+
|
1443
|
+
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
1444
|
+
Opcodes.i32_to_u,
|
1445
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
1446
|
+
[ Opcodes.i32_mul ],
|
1447
|
+
[ Opcodes.i32_add ],
|
1448
|
+
|
1449
|
+
...(op === '' ? generate(scope, decl.right, false, name) : performOp(scope, op, generate(scope, decl.left), generate(scope, decl.right), parentType === TYPES._array ? TYPES.number : TYPES.string, getNodeType(scope, decl.right), false, name, true)),
|
1450
|
+
[ Opcodes.local_tee, newValueTmp ],
|
1451
|
+
|
1452
|
+
...(parentType === TYPES._array ? [
|
1453
|
+
[ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1454
|
+
] : [
|
1455
|
+
Opcodes.i32_to_u,
|
1456
|
+
[ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1457
|
+
]),
|
1458
|
+
|
1459
|
+
[ Opcodes.local_get, newValueTmp ]
|
1460
|
+
];
|
1461
|
+
}
|
1462
|
+
|
1348
1463
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1349
1464
|
|
1350
1465
|
if (local === undefined) {
|
@@ -1365,8 +1480,10 @@ const generateAssign = (scope, decl) => {
|
|
1365
1480
|
];
|
1366
1481
|
}
|
1367
1482
|
|
1483
|
+
typeStates[name] = getNodeType(scope, decl.right);
|
1484
|
+
|
1368
1485
|
if (decl.operator === '=') {
|
1369
|
-
typeStates[name] = getNodeType(scope, decl.right);
|
1486
|
+
// typeStates[name] = getNodeType(scope, decl.right);
|
1370
1487
|
|
1371
1488
|
return [
|
1372
1489
|
...generate(scope, decl.right, isGlobal, name),
|
@@ -1510,7 +1627,7 @@ const generateUpdate = (scope, decl) => {
|
|
1510
1627
|
};
|
1511
1628
|
|
1512
1629
|
const generateIf = (scope, decl) => {
|
1513
|
-
const out = truthy(scope, generate(scope, decl.test), decl.test);
|
1630
|
+
const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test));
|
1514
1631
|
|
1515
1632
|
out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
|
1516
1633
|
depth.push('if');
|
@@ -1603,18 +1720,106 @@ const generateWhile = (scope, decl) => {
|
|
1603
1720
|
const generateForOf = (scope, decl) => {
|
1604
1721
|
const out = [];
|
1605
1722
|
|
1723
|
+
const rightType = getNodeType(scope, decl.right);
|
1724
|
+
const valtypeSize = rightType === TYPES._array ? ValtypeSize[valtype] : ValtypeSize.i16; // presume array (:()
|
1725
|
+
|
1726
|
+
// todo: for of inside for of might fuck up?
|
1727
|
+
const pointer = localTmp(scope, 'forof_base_pointer', Valtype.i32);
|
1728
|
+
const length = localTmp(scope, 'forof_length', Valtype.i32);
|
1729
|
+
const counter = localTmp(scope, 'forof_counter', Valtype.i32);
|
1730
|
+
|
1731
|
+
out.push(
|
1732
|
+
// set pointer as right
|
1733
|
+
...generate(scope, decl.right),
|
1734
|
+
Opcodes.i32_to_u,
|
1735
|
+
[ Opcodes.local_set, pointer ],
|
1736
|
+
|
1737
|
+
// get length
|
1738
|
+
[ Opcodes.local_get, pointer ],
|
1739
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
1740
|
+
[ Opcodes.local_set, length ]
|
1741
|
+
);
|
1742
|
+
|
1606
1743
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
1607
|
-
depth.push('
|
1744
|
+
depth.push('forof');
|
1608
1745
|
|
1609
|
-
|
1610
|
-
|
1611
|
-
depth.push('if');
|
1746
|
+
// setup local for left
|
1747
|
+
generate(scope, decl.left);
|
1612
1748
|
|
1613
|
-
|
1749
|
+
const leftName = decl.left.declarations[0].id.name;
|
1614
1750
|
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1751
|
+
// set type for local
|
1752
|
+
typeStates[leftName] = rightType === TYPES._array ? TYPES.number : TYPES.string;
|
1753
|
+
|
1754
|
+
const [ local, isGlobal ] = lookupName(scope, leftName);
|
1755
|
+
|
1756
|
+
if (rightType === TYPES._array) { // array
|
1757
|
+
out.push(
|
1758
|
+
[ Opcodes.local_get, pointer ],
|
1759
|
+
[ Opcodes.load, Math.log2(valtypeSize) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
|
1760
|
+
);
|
1761
|
+
} else { // string
|
1762
|
+
const [ newOut, newPointer ] = makeArray(scope, {
|
1763
|
+
rawElements: new Array(1)
|
1764
|
+
}, isGlobal, leftName, true, 'i16');
|
1765
|
+
|
1766
|
+
out.push(
|
1767
|
+
// setup new/out array
|
1768
|
+
...newOut,
|
1769
|
+
[ Opcodes.drop ],
|
1770
|
+
|
1771
|
+
...number(0, Valtype.i32), // base 0 for store after
|
1772
|
+
|
1773
|
+
// load current string ind {arg}
|
1774
|
+
[ Opcodes.local_get, pointer ],
|
1775
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
1776
|
+
|
1777
|
+
// store to new string ind 0
|
1778
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
1779
|
+
|
1780
|
+
// return new string (page)
|
1781
|
+
...number(newPointer)
|
1782
|
+
);
|
1783
|
+
}
|
1784
|
+
|
1785
|
+
// set left value
|
1786
|
+
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ]);
|
1787
|
+
|
1788
|
+
out.push(
|
1789
|
+
[ Opcodes.block, Blocktype.void ],
|
1790
|
+
[ Opcodes.block, Blocktype.void ]
|
1791
|
+
);
|
1792
|
+
depth.push('block');
|
1793
|
+
depth.push('block');
|
1794
|
+
|
1795
|
+
out.push(
|
1796
|
+
...generate(scope, decl.body),
|
1797
|
+
[ Opcodes.end ]
|
1798
|
+
);
|
1799
|
+
depth.pop();
|
1800
|
+
|
1801
|
+
out.push(
|
1802
|
+
// increment iter pointer by valtype size
|
1803
|
+
[ Opcodes.local_get, pointer ],
|
1804
|
+
...number(valtypeSize, Valtype.i32),
|
1805
|
+
[ Opcodes.i32_add ],
|
1806
|
+
[ Opcodes.local_set, pointer ],
|
1807
|
+
|
1808
|
+
// increment counter by 1
|
1809
|
+
[ Opcodes.local_get, counter ],
|
1810
|
+
...number(1, Valtype.i32),
|
1811
|
+
[ Opcodes.i32_add ],
|
1812
|
+
[ Opcodes.local_tee, counter ],
|
1813
|
+
|
1814
|
+
// loop if counter != length
|
1815
|
+
[ Opcodes.local_get, length ],
|
1816
|
+
[ Opcodes.i32_ne ],
|
1817
|
+
[ Opcodes.br_if, 1 ],
|
1818
|
+
|
1819
|
+
[ Opcodes.end ], [ Opcodes.end ]
|
1820
|
+
);
|
1821
|
+
depth.pop();
|
1822
|
+
depth.pop();
|
1618
1823
|
|
1619
1824
|
return out;
|
1620
1825
|
};
|
@@ -1705,19 +1910,19 @@ const generateAssignPat = (scope, decl) => {
|
|
1705
1910
|
};
|
1706
1911
|
|
1707
1912
|
let pages = new Map();
|
1708
|
-
const allocPage = reason => {
|
1709
|
-
if (pages.has(reason)) return pages.get(reason);
|
1913
|
+
const allocPage = (reason, type) => {
|
1914
|
+
if (pages.has(reason)) return pages.get(reason).ind;
|
1710
1915
|
|
1711
|
-
|
1712
|
-
pages.set(reason, ind);
|
1916
|
+
const ind = pages.size;
|
1917
|
+
pages.set(reason, { ind, type });
|
1713
1918
|
|
1714
|
-
if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason}`);
|
1919
|
+
if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
1715
1920
|
|
1716
1921
|
return ind;
|
1717
1922
|
};
|
1718
1923
|
|
1719
1924
|
const freePage = reason => {
|
1720
|
-
|
1925
|
+
const { ind } = pages.get(reason);
|
1721
1926
|
pages.delete(reason);
|
1722
1927
|
|
1723
1928
|
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
@@ -1734,7 +1939,7 @@ const itemTypeToValtype = {
|
|
1734
1939
|
i16: 'i32'
|
1735
1940
|
};
|
1736
1941
|
|
1737
|
-
const
|
1942
|
+
const StoreOps = {
|
1738
1943
|
i32: Opcodes.i32_store,
|
1739
1944
|
i64: Opcodes.i64_store,
|
1740
1945
|
f64: Opcodes.f64_store,
|
@@ -1749,7 +1954,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1749
1954
|
if (!arrays.has(name) || name === '$undeclared') {
|
1750
1955
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
1751
1956
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
1752
|
-
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}
|
1957
|
+
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
|
1753
1958
|
}
|
1754
1959
|
|
1755
1960
|
const pointer = arrays.get(name);
|
@@ -1766,7 +1971,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1766
1971
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
|
1767
1972
|
);
|
1768
1973
|
|
1769
|
-
const storeOp =
|
1974
|
+
const storeOp = StoreOps[itemType];
|
1770
1975
|
const valtype = itemTypeToValtype[itemType];
|
1771
1976
|
|
1772
1977
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
@@ -1929,7 +2134,6 @@ const generateFunc = (scope, decl) => {
|
|
1929
2134
|
localInd: 0,
|
1930
2135
|
returns: [ valtypeBinary ],
|
1931
2136
|
returnType: null,
|
1932
|
-
memory: false,
|
1933
2137
|
throws: false,
|
1934
2138
|
name
|
1935
2139
|
};
|
package/compiler/decompile.js
CHANGED
@@ -57,7 +57,7 @@ export default (wasm, name = '', ind = 0, locals = {}, params = [], returns = []
|
|
57
57
|
out += ` ;; label @${depth}`;
|
58
58
|
}
|
59
59
|
|
60
|
-
if (inst[0] === Opcodes.br) {
|
60
|
+
if (inst[0] === Opcodes.br || inst[0] === Opcodes.br_if) {
|
61
61
|
out += ` ;; goto @${depth - inst[1]}`;
|
62
62
|
}
|
63
63
|
|
package/compiler/index.js
CHANGED
@@ -4,6 +4,8 @@ import opt from './opt.js';
|
|
4
4
|
import produceSections from './sections.js';
|
5
5
|
import decompile from './decompile.js';
|
6
6
|
import { BuiltinPreludes } from './builtins.js';
|
7
|
+
import toc from './2c.js';
|
8
|
+
|
7
9
|
|
8
10
|
globalThis.decompile = decompile;
|
9
11
|
|
@@ -15,7 +17,8 @@ const areaColors = {
|
|
15
17
|
codegen: [ 20, 80, 250 ],
|
16
18
|
opt: [ 250, 20, 80 ],
|
17
19
|
sections: [ 20, 250, 80 ],
|
18
|
-
alloc: [ 250, 250, 20 ]
|
20
|
+
alloc: [ 250, 250, 20 ],
|
21
|
+
'2c': [ 20, 250, 250 ]
|
19
22
|
};
|
20
23
|
|
21
24
|
globalThis.log = (area, ...args) => console.log(`\u001b[90m[\u001b[0m${rgb(...areaColors[area], area)}\u001b[90m]\u001b[0m`, ...args);
|
@@ -36,10 +39,16 @@ const logFuncs = (funcs, globals, exceptions) => {
|
|
36
39
|
console.log();
|
37
40
|
};
|
38
41
|
|
42
|
+
const getArg = name => process.argv.find(x => x.startsWith(`-${name}=`))?.slice(name.length + 2);
|
43
|
+
|
44
|
+
const writeFileSync = (typeof process !== 'undefined' ? (await import('node:fs')).writeFileSync : undefined);
|
45
|
+
const execSync = (typeof process !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
|
46
|
+
|
39
47
|
export default (code, flags) => {
|
40
48
|
globalThis.optLog = process.argv.includes('-opt-log');
|
41
49
|
globalThis.codeLog = process.argv.includes('-code-log');
|
42
50
|
globalThis.allocLog = process.argv.includes('-alloc-log');
|
51
|
+
globalThis.regexLog = process.argv.includes('-regex-log');
|
43
52
|
|
44
53
|
for (const x in BuiltinPreludes) {
|
45
54
|
if (code.indexOf(x + '(') !== -1) code = BuiltinPreludes[x] + code;
|
@@ -72,5 +81,38 @@ export default (code, flags) => {
|
|
72
81
|
// console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n'));
|
73
82
|
}
|
74
83
|
|
75
|
-
|
84
|
+
const out = { wasm: sections, funcs, globals, tags, exceptions, pages };
|
85
|
+
|
86
|
+
const target = getArg('target') ?? getArg('t') ?? 'wasm';
|
87
|
+
const outFile = getArg('o');
|
88
|
+
|
89
|
+
if (target === 'c') {
|
90
|
+
const c = toc(out);
|
91
|
+
|
92
|
+
if (outFile) {
|
93
|
+
writeFileSync(outFile, c);
|
94
|
+
} else {
|
95
|
+
console.log(c);
|
96
|
+
}
|
97
|
+
|
98
|
+
process.exit();
|
99
|
+
}
|
100
|
+
|
101
|
+
if (target === 'native') {
|
102
|
+
const compiler = getArg('compiler') ?? 'clang';
|
103
|
+
const cO = getArg('cO') ?? 'Ofast';
|
104
|
+
|
105
|
+
const tmpfile = 'tmp.c';
|
106
|
+
const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
|
107
|
+
|
108
|
+
const c = toc(out);
|
109
|
+
writeFileSync(tmpfile, c);
|
110
|
+
|
111
|
+
// obvious command escape is obvious
|
112
|
+
execSync(args.join(' '), { stdio: 'inherit' });
|
113
|
+
|
114
|
+
process.exit();
|
115
|
+
}
|
116
|
+
|
117
|
+
return out;
|
76
118
|
};
|
package/compiler/opt.js
CHANGED
package/compiler/parse.js
CHANGED
package/compiler/prototype.js
CHANGED
@@ -15,7 +15,8 @@ const TYPES = {
|
|
15
15
|
bigint: 0xffffffffffff7,
|
16
16
|
|
17
17
|
// these are not "typeof" types but tracked internally
|
18
|
-
_array:
|
18
|
+
_array: 0xfffffffffff0f,
|
19
|
+
_regexp: 0xfffffffffff1f
|
19
20
|
};
|
20
21
|
|
21
22
|
// todo: turn these into built-ins once arrays and these become less hacky
|
package/compiler/sections.js
CHANGED
@@ -8,11 +8,26 @@ const createSection = (type, data) => [
|
|
8
8
|
...encodeVector(data)
|
9
9
|
];
|
10
10
|
|
11
|
+
const customSection = (name, data) => [
|
12
|
+
Section.custom,
|
13
|
+
...encodeVector([...encodeString(name), ...data])
|
14
|
+
];
|
15
|
+
|
16
|
+
const chHint = (topTier, baselineTier, strategy) => {
|
17
|
+
// 1 byte of 4 2 bit components: spare, top tier, baseline tier, compilation strategy
|
18
|
+
// tiers: 0x00 = default, 0x01 = baseline (liftoff), 0x02 = optimized (turbofan)
|
19
|
+
// strategy: 0x00 = default, 0x01 = lazy, 0x02 = eager, 0x03 = lazy baseline, eager top tier
|
20
|
+
return (strategy | (baselineTier << 2) | (topTier << 4));
|
21
|
+
};
|
22
|
+
|
11
23
|
export default (funcs, globals, tags, pages, flags) => {
|
12
24
|
const types = [], typeCache = {};
|
13
25
|
|
14
26
|
const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
|
15
27
|
|
28
|
+
const compileHints = process.argv.includes('-compile-hints');
|
29
|
+
if (compileHints) log('sections', 'warning: compile hints is V8 only w/ experimental arg! (you used -compile-hints)');
|
30
|
+
|
16
31
|
const getType = (params, returns) => {
|
17
32
|
const hash = `${params.join(',')}_${returns.join(',')}`;
|
18
33
|
if (optLog) log('sections', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
|
@@ -61,6 +76,7 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
61
76
|
}
|
62
77
|
}
|
63
78
|
}
|
79
|
+
globalThis.importFuncs = importFuncs;
|
64
80
|
|
65
81
|
if (optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
|
66
82
|
|
@@ -74,6 +90,14 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
74
90
|
encodeVector(funcs.map(x => getType(x.params, x.returns))) // type indexes
|
75
91
|
);
|
76
92
|
|
93
|
+
// compilation hints section - unspec v8 only
|
94
|
+
// https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
|
95
|
+
const chSection = !compileHints ? [] : customSection(
|
96
|
+
'compilationHints',
|
97
|
+
// for now just do everything as optimise eager
|
98
|
+
encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
|
99
|
+
);
|
100
|
+
|
77
101
|
const globalSection = Object.keys(globals).length === 0 ? [] : createSection(
|
78
102
|
Section.global,
|
79
103
|
encodeVector(Object.keys(globals).map(x => [ globals[x].type, 0x01, ...number(globals[x].init ?? 0, globals[x].type).flat(), Opcodes.end ]))
|
@@ -146,6 +170,7 @@ export default (funcs, globals, tags, pages, flags) => {
|
|
146
170
|
...typeSection,
|
147
171
|
...importSection,
|
148
172
|
...funcSection,
|
173
|
+
...chSection,
|
149
174
|
...memorySection,
|
150
175
|
...tagSection,
|
151
176
|
...globalSection,
|