porffor 0.0.0-ba812f2 → 0.0.0-beff13f
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 +5 -5
- package/c.exe +0 -0
- package/compiler/2c.js +112 -19
- package/compiler/builtins.js +6 -0
- package/compiler/codeGen.js +295 -81
- package/compiler/embedding.js +9 -5
- package/compiler/index.js +5 -5
- package/compiler/opt.js +14 -18
- package/compiler/parse.js +2 -2
- package/compiler/prototype.js +90 -28
- package/compiler/sections.js +17 -4
- package/compiler/wrap.js +9 -2
- package/g.exe +0 -0
- package/out.exe +0 -0
- package/package.json +1 -1
- package/r.js +39 -1
- package/rhemyn/README.md +1 -1
- package/rhemyn/compile.js +1 -1
- package/rhemyn/parse.js +12 -10
- package/runner/index.js +5 -3
- package/runner/info.js +37 -2
- package/tmp.c +46 -25
package/compiler/codeGen.js
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from "./wasmSpec.js";
|
2
|
-
import { signedLEB128, unsignedLEB128 } from "./encoding.js";
|
2
|
+
import { ieee754_binary64, signedLEB128, unsignedLEB128 } from "./encoding.js";
|
3
3
|
import { operatorOpcode } from "./expression.js";
|
4
4
|
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from "./builtins.js";
|
5
5
|
import { PrototypeFuncs } from "./prototype.js";
|
6
|
-
import { number, i32x4 } from "./embedding.js";
|
6
|
+
import { number, i32x4, enforceOneByte, enforceTwoBytes, enforceFourBytes, enforceEightBytes } from "./embedding.js";
|
7
7
|
import parse from "./parse.js";
|
8
8
|
import * as Rhemyn from "../rhemyn/compile.js";
|
9
9
|
|
@@ -109,8 +109,8 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
109
109
|
case 'WhileStatement':
|
110
110
|
return generateWhile(scope, decl);
|
111
111
|
|
112
|
-
|
113
|
-
return generateForOf(scope, decl);
|
112
|
+
case 'ForOfStatement':
|
113
|
+
return generateForOf(scope, decl);
|
114
114
|
|
115
115
|
case 'BreakStatement':
|
116
116
|
return generateBreak(scope, decl);
|
@@ -152,44 +152,65 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
152
152
|
|
153
153
|
return [];
|
154
154
|
|
155
|
-
case 'TaggedTemplateExpression':
|
156
|
-
|
157
|
-
|
155
|
+
case 'TaggedTemplateExpression': {
|
156
|
+
const funcs = {
|
157
|
+
asm: str => {
|
158
|
+
let out = [];
|
158
159
|
|
159
|
-
|
160
|
-
|
160
|
+
for (const line of str.split('\n')) {
|
161
|
+
const asm = line.trim().split(';;')[0].split(' ');
|
162
|
+
if (asm[0] === '') continue; // blank
|
161
163
|
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
+
}
|
165
169
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
}
|
170
|
+
if (asm[0] === 'returns') {
|
171
|
+
scope.returns = asm.slice(1).map(x => Valtype[x]);
|
172
|
+
continue;
|
173
|
+
}
|
171
174
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
175
|
+
if (asm[0] === 'memory') {
|
176
|
+
allocPage('asm instrinsic');
|
177
|
+
// todo: add to store/load offset insts
|
178
|
+
continue;
|
179
|
+
}
|
176
180
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
+
}
|
189
|
+
|
190
|
+
return out;
|
191
|
+
},
|
182
192
|
|
183
|
-
|
184
|
-
|
193
|
+
__internal_print_type: str => {
|
194
|
+
const type = getType(scope, str) - TYPES.number;
|
185
195
|
|
186
|
-
|
187
|
-
|
196
|
+
return [
|
197
|
+
...number(type),
|
198
|
+
[ Opcodes.call, importedFuncs.print ],
|
188
199
|
|
189
|
-
|
200
|
+
// newline
|
201
|
+
...number(10),
|
202
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
203
|
+
];
|
204
|
+
}
|
190
205
|
}
|
191
206
|
|
192
|
-
|
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
|
+
}
|
193
214
|
|
194
215
|
default:
|
195
216
|
return todo(`no generation for ${decl.type}!`);
|
@@ -665,29 +686,44 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
665
686
|
|
666
687
|
if (codeLog && (!leftType || !rightType)) log('codegen', 'untracked type in op', op, _name, '\n' + new Error().stack.split('\n').slice(1).join('\n'));
|
667
688
|
|
668
|
-
|
669
|
-
|
689
|
+
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
690
|
+
|
691
|
+
if (leftType && rightType && (
|
692
|
+
// if strict (in)equal and known types mismatch, return false (===)/true (!==)
|
693
|
+
((op === '===' || op === '!==') && leftType !== rightType) ||
|
694
|
+
|
695
|
+
// if equality op and an operand is undefined, return false
|
696
|
+
(eqOp && leftType === TYPES.undefined ^ rightType === TYPES.undefined)
|
697
|
+
)) {
|
698
|
+
// undefined == null
|
699
|
+
if (((leftType === TYPES.undefined && rightType === TYPES.object) || (leftType === TYPES.object && rightType === TYPES.undefined)) && (op === '==' || op === '!=')) return [
|
700
|
+
...(leftType === TYPES.object ? left : right),
|
701
|
+
...Opcodes.eqz,
|
702
|
+
...(op === '!=' ? [ [ Opcodes.i32_eqz ] ] : [])
|
703
|
+
];
|
704
|
+
|
670
705
|
return [
|
671
706
|
...left,
|
672
|
-
...right,
|
673
|
-
|
674
|
-
// drop values
|
675
707
|
[ Opcodes.drop ],
|
708
|
+
|
709
|
+
...right,
|
676
710
|
[ Opcodes.drop ],
|
677
711
|
|
678
|
-
// return
|
679
|
-
...number(op === '===' ?
|
712
|
+
// return true (!=/!==) or false (else)
|
713
|
+
...number(op === '!=' || op === '!==' ? 1 : 0, Valtype.i32)
|
680
714
|
];
|
681
715
|
}
|
682
716
|
|
717
|
+
// todo: niche null hell with 0
|
718
|
+
|
683
719
|
if (leftType === TYPES.string || rightType === TYPES.string) {
|
684
720
|
if (op === '+') {
|
685
721
|
// string concat (a + b)
|
686
722
|
return concatStrings(scope, left, right, _global, _name, assign);
|
687
723
|
}
|
688
724
|
|
689
|
-
//
|
690
|
-
if (!
|
725
|
+
// not an equality op, NaN
|
726
|
+
if (!eqOp) return number(NaN);
|
691
727
|
|
692
728
|
// else leave bool ops
|
693
729
|
// todo: convert string to number if string and number/bool
|
@@ -732,17 +768,11 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
732
768
|
];
|
733
769
|
};
|
734
770
|
|
735
|
-
let binaryExpDepth = 0;
|
736
771
|
const generateBinaryExp = (scope, decl, _global, _name) => {
|
737
|
-
|
738
|
-
|
739
|
-
const out = [
|
740
|
-
...performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name)
|
741
|
-
];
|
772
|
+
const out = performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name);
|
742
773
|
|
743
774
|
if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
|
744
775
|
|
745
|
-
binaryExpDepth--;
|
746
776
|
return out;
|
747
777
|
};
|
748
778
|
|
@@ -999,6 +1029,8 @@ const countLeftover = wasm => {
|
|
999
1029
|
} else count--;
|
1000
1030
|
if (func) count += func.returns.length;
|
1001
1031
|
} else count--;
|
1032
|
+
|
1033
|
+
// console.log(count, decompile([ inst ]).slice(0, -1));
|
1002
1034
|
}
|
1003
1035
|
|
1004
1036
|
return count;
|
@@ -1185,24 +1217,34 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1185
1217
|
// use local for cached i32 length as commonly used
|
1186
1218
|
let lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1187
1219
|
|
1220
|
+
let lengthI32CacheUsed = false;
|
1221
|
+
|
1222
|
+
const protoOut = protoFunc(pointer, {
|
1223
|
+
getCachedI32: () => {
|
1224
|
+
lengthI32CacheUsed = true;
|
1225
|
+
return [ [ Opcodes.local_get, lengthLocal ] ]
|
1226
|
+
},
|
1227
|
+
setCachedI32: () => [ [ Opcodes.local_set, lengthLocal ] ],
|
1228
|
+
get: () => arrayUtil.getLength(pointer),
|
1229
|
+
getI32: () => arrayUtil.getLengthI32(pointer),
|
1230
|
+
set: value => arrayUtil.setLength(pointer, value),
|
1231
|
+
setI32: value => arrayUtil.setLengthI32(pointer, value)
|
1232
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, (length, itemType) => {
|
1233
|
+
return makeArray(scope, {
|
1234
|
+
rawElements: new Array(length)
|
1235
|
+
}, _global, _name, true, itemType);
|
1236
|
+
});
|
1237
|
+
|
1188
1238
|
return [
|
1189
1239
|
...out,
|
1190
1240
|
|
1191
|
-
...
|
1192
|
-
|
1241
|
+
...(!lengthI32CacheUsed ? [] : [
|
1242
|
+
...arrayUtil.getLengthI32(pointer),
|
1243
|
+
[ Opcodes.local_set, lengthLocal ],
|
1244
|
+
]),
|
1193
1245
|
|
1194
1246
|
[ Opcodes.block, valtypeBinary ],
|
1195
|
-
...
|
1196
|
-
cachedI32: [ [ Opcodes.local_get, lengthLocal ] ],
|
1197
|
-
get: arrayUtil.getLength(pointer),
|
1198
|
-
getI32: arrayUtil.getLengthI32(pointer),
|
1199
|
-
set: value => arrayUtil.setLength(pointer, value),
|
1200
|
-
setI32: value => arrayUtil.setLengthI32(pointer, value)
|
1201
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, (length, itemType) => {
|
1202
|
-
return makeArray(scope, {
|
1203
|
-
rawElements: new Array(length)
|
1204
|
-
}, _global, _name, true, itemType);
|
1205
|
-
}),
|
1247
|
+
...protoOut,
|
1206
1248
|
[ Opcodes.end ]
|
1207
1249
|
];
|
1208
1250
|
}
|
@@ -1262,7 +1304,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1262
1304
|
if (func && func.throws) scope.throws = true;
|
1263
1305
|
|
1264
1306
|
for (const arg of args) {
|
1265
|
-
out.
|
1307
|
+
out = out.concat(generate(scope, arg));
|
1266
1308
|
}
|
1267
1309
|
|
1268
1310
|
out.push([ Opcodes.call, idx ]);
|
@@ -1402,13 +1444,60 @@ const generateAssign = (scope, decl) => {
|
|
1402
1444
|
];
|
1403
1445
|
}
|
1404
1446
|
|
1447
|
+
const op = decl.operator.slice(0, -1) || '=';
|
1448
|
+
|
1449
|
+
// arr[i] | str[i]
|
1450
|
+
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
1451
|
+
const name = decl.left.object.name;
|
1452
|
+
const pointer = arrays.get(name);
|
1453
|
+
|
1454
|
+
const aotPointer = pointer != null;
|
1455
|
+
|
1456
|
+
const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
|
1457
|
+
const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
|
1458
|
+
|
1459
|
+
const parentType = getNodeType(scope, decl.left.object);
|
1460
|
+
|
1461
|
+
return [
|
1462
|
+
...(aotPointer ? [] : [
|
1463
|
+
...generate(scope, decl.left.object),
|
1464
|
+
Opcodes.i32_to_u
|
1465
|
+
]),
|
1466
|
+
|
1467
|
+
// get index as valtype
|
1468
|
+
...generate(scope, decl.left.property),
|
1469
|
+
|
1470
|
+
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
1471
|
+
Opcodes.i32_to_u,
|
1472
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
1473
|
+
[ Opcodes.i32_mul ],
|
1474
|
+
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
1475
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
1476
|
+
|
1477
|
+
...(op === '=' ? generate(scope, decl.right, false, name) : performOp(scope, op, [
|
1478
|
+
[ Opcodes.local_get, pointerTmp ],
|
1479
|
+
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1480
|
+
], generate(scope, decl.right), parentType === TYPES._array ? TYPES.number : TYPES.string, getNodeType(scope, decl.right), false, name, true)),
|
1481
|
+
[ Opcodes.local_tee, newValueTmp ],
|
1482
|
+
|
1483
|
+
...(parentType === TYPES._array ? [
|
1484
|
+
[ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1485
|
+
] : [
|
1486
|
+
Opcodes.i32_to_u,
|
1487
|
+
[ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1488
|
+
]),
|
1489
|
+
|
1490
|
+
[ Opcodes.local_get, newValueTmp ]
|
1491
|
+
];
|
1492
|
+
}
|
1493
|
+
|
1405
1494
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1406
1495
|
|
1407
1496
|
if (local === undefined) {
|
1408
1497
|
// todo: this should be a devtools/repl/??? only thing
|
1409
1498
|
|
1410
1499
|
// only allow = for this
|
1411
|
-
if (
|
1500
|
+
if (op !== '=') return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
|
1412
1501
|
|
1413
1502
|
if (builtinVars[name]) {
|
1414
1503
|
// just return rhs (eg `NaN = 2`)
|
@@ -1422,8 +1511,10 @@ const generateAssign = (scope, decl) => {
|
|
1422
1511
|
];
|
1423
1512
|
}
|
1424
1513
|
|
1425
|
-
|
1426
|
-
|
1514
|
+
typeStates[name] = getNodeType(scope, decl.right);
|
1515
|
+
|
1516
|
+
if (op === '=') {
|
1517
|
+
// typeStates[name] = getNodeType(scope, decl.right);
|
1427
1518
|
|
1428
1519
|
return [
|
1429
1520
|
...generate(scope, decl.right, isGlobal, name),
|
@@ -1432,7 +1523,6 @@ const generateAssign = (scope, decl) => {
|
|
1432
1523
|
];
|
1433
1524
|
}
|
1434
1525
|
|
1435
|
-
const op = decl.operator.slice(0, -1);
|
1436
1526
|
if (op === '||' || op === '&&' || op === '??') {
|
1437
1527
|
// todo: is this needed?
|
1438
1528
|
// for logical assignment ops, it is not left @= right ~= left = left @ right
|
@@ -1567,7 +1657,7 @@ const generateUpdate = (scope, decl) => {
|
|
1567
1657
|
};
|
1568
1658
|
|
1569
1659
|
const generateIf = (scope, decl) => {
|
1570
|
-
const out = truthy(scope, generate(scope, decl.test), decl.test);
|
1660
|
+
const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test));
|
1571
1661
|
|
1572
1662
|
out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
|
1573
1663
|
depth.push('if');
|
@@ -1660,18 +1750,106 @@ const generateWhile = (scope, decl) => {
|
|
1660
1750
|
const generateForOf = (scope, decl) => {
|
1661
1751
|
const out = [];
|
1662
1752
|
|
1753
|
+
const rightType = getNodeType(scope, decl.right);
|
1754
|
+
const valtypeSize = rightType === TYPES._array ? ValtypeSize[valtype] : ValtypeSize.i16; // presume array (:()
|
1755
|
+
|
1756
|
+
// todo: for of inside for of might fuck up?
|
1757
|
+
const pointer = localTmp(scope, 'forof_base_pointer', Valtype.i32);
|
1758
|
+
const length = localTmp(scope, 'forof_length', Valtype.i32);
|
1759
|
+
const counter = localTmp(scope, 'forof_counter', Valtype.i32);
|
1760
|
+
|
1761
|
+
out.push(
|
1762
|
+
// set pointer as right
|
1763
|
+
...generate(scope, decl.right),
|
1764
|
+
Opcodes.i32_to_u,
|
1765
|
+
[ Opcodes.local_set, pointer ],
|
1766
|
+
|
1767
|
+
// get length
|
1768
|
+
[ Opcodes.local_get, pointer ],
|
1769
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
1770
|
+
[ Opcodes.local_set, length ]
|
1771
|
+
);
|
1772
|
+
|
1663
1773
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
1664
|
-
depth.push('
|
1774
|
+
depth.push('forof');
|
1665
1775
|
|
1666
|
-
|
1667
|
-
|
1668
|
-
depth.push('if');
|
1776
|
+
// setup local for left
|
1777
|
+
generate(scope, decl.left);
|
1669
1778
|
|
1670
|
-
|
1779
|
+
const leftName = decl.left.declarations[0].id.name;
|
1671
1780
|
|
1672
|
-
|
1673
|
-
|
1674
|
-
|
1781
|
+
// set type for local
|
1782
|
+
typeStates[leftName] = rightType === TYPES._array ? TYPES.number : TYPES.string;
|
1783
|
+
|
1784
|
+
const [ local, isGlobal ] = lookupName(scope, leftName);
|
1785
|
+
|
1786
|
+
if (rightType === TYPES._array) { // array
|
1787
|
+
out.push(
|
1788
|
+
[ Opcodes.local_get, pointer ],
|
1789
|
+
[ Opcodes.load, Math.log2(valtypeSize) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
|
1790
|
+
);
|
1791
|
+
} else { // string
|
1792
|
+
const [ newOut, newPointer ] = makeArray(scope, {
|
1793
|
+
rawElements: new Array(1)
|
1794
|
+
}, isGlobal, leftName, true, 'i16');
|
1795
|
+
|
1796
|
+
out.push(
|
1797
|
+
// setup new/out array
|
1798
|
+
...newOut,
|
1799
|
+
[ Opcodes.drop ],
|
1800
|
+
|
1801
|
+
...number(0, Valtype.i32), // base 0 for store after
|
1802
|
+
|
1803
|
+
// load current string ind {arg}
|
1804
|
+
[ Opcodes.local_get, pointer ],
|
1805
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
1806
|
+
|
1807
|
+
// store to new string ind 0
|
1808
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
1809
|
+
|
1810
|
+
// return new string (page)
|
1811
|
+
...number(newPointer)
|
1812
|
+
);
|
1813
|
+
}
|
1814
|
+
|
1815
|
+
// set left value
|
1816
|
+
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ]);
|
1817
|
+
|
1818
|
+
out.push(
|
1819
|
+
[ Opcodes.block, Blocktype.void ],
|
1820
|
+
[ Opcodes.block, Blocktype.void ]
|
1821
|
+
);
|
1822
|
+
depth.push('block');
|
1823
|
+
depth.push('block');
|
1824
|
+
|
1825
|
+
out.push(
|
1826
|
+
...generate(scope, decl.body),
|
1827
|
+
[ Opcodes.end ]
|
1828
|
+
);
|
1829
|
+
depth.pop();
|
1830
|
+
|
1831
|
+
out.push(
|
1832
|
+
// increment iter pointer by valtype size
|
1833
|
+
[ Opcodes.local_get, pointer ],
|
1834
|
+
...number(valtypeSize, Valtype.i32),
|
1835
|
+
[ Opcodes.i32_add ],
|
1836
|
+
[ Opcodes.local_set, pointer ],
|
1837
|
+
|
1838
|
+
// increment counter by 1
|
1839
|
+
[ Opcodes.local_get, counter ],
|
1840
|
+
...number(1, Valtype.i32),
|
1841
|
+
[ Opcodes.i32_add ],
|
1842
|
+
[ Opcodes.local_tee, counter ],
|
1843
|
+
|
1844
|
+
// loop if counter != length
|
1845
|
+
[ Opcodes.local_get, length ],
|
1846
|
+
[ Opcodes.i32_ne ],
|
1847
|
+
[ Opcodes.br_if, 1 ],
|
1848
|
+
|
1849
|
+
[ Opcodes.end ], [ Opcodes.end ]
|
1850
|
+
);
|
1851
|
+
depth.pop();
|
1852
|
+
depth.pop();
|
1675
1853
|
|
1676
1854
|
return out;
|
1677
1855
|
};
|
@@ -1791,7 +1969,7 @@ const itemTypeToValtype = {
|
|
1791
1969
|
i16: 'i32'
|
1792
1970
|
};
|
1793
1971
|
|
1794
|
-
const
|
1972
|
+
const StoreOps = {
|
1795
1973
|
i32: Opcodes.i32_store,
|
1796
1974
|
i64: Opcodes.i64_store,
|
1797
1975
|
f64: Opcodes.f64_store,
|
@@ -1800,10 +1978,25 @@ const storeOps = {
|
|
1800
1978
|
i16: Opcodes.i32_store16
|
1801
1979
|
};
|
1802
1980
|
|
1981
|
+
let data = [];
|
1982
|
+
|
1983
|
+
const compileBytes = (val, itemType, signed = true) => {
|
1984
|
+
switch (itemType) {
|
1985
|
+
case 'i8': return enforceOneByte(unsignedLEB128(val));
|
1986
|
+
case 'i16': return enforceTwoBytes(unsignedLEB128(val));
|
1987
|
+
case 'i32': return enforceFourBytes(signedLEB128(val));
|
1988
|
+
case 'i64': return enforceEightBytes(signedLEB128(val));
|
1989
|
+
case 'f64': return enforceEightBytes(ieee754_binary64(val));
|
1990
|
+
}
|
1991
|
+
};
|
1992
|
+
|
1803
1993
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
1804
1994
|
const out = [];
|
1805
1995
|
|
1996
|
+
let firstAssign = false;
|
1806
1997
|
if (!arrays.has(name) || name === '$undeclared') {
|
1998
|
+
firstAssign = true;
|
1999
|
+
|
1807
2000
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
1808
2001
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
1809
2002
|
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
|
@@ -1814,8 +2007,29 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1814
2007
|
const useRawElements = !!decl.rawElements;
|
1815
2008
|
const elements = useRawElements ? decl.rawElements : decl.elements;
|
1816
2009
|
|
2010
|
+
const valtype = itemTypeToValtype[itemType];
|
1817
2011
|
const length = elements.length;
|
1818
2012
|
|
2013
|
+
if (firstAssign && useRawElements) {
|
2014
|
+
let bytes = compileBytes(length, 'i32');
|
2015
|
+
|
2016
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
2017
|
+
if (elements[i] == null) continue;
|
2018
|
+
|
2019
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
2020
|
+
}
|
2021
|
+
|
2022
|
+
data.push({
|
2023
|
+
offset: pointer,
|
2024
|
+
bytes
|
2025
|
+
});
|
2026
|
+
|
2027
|
+
// local value as pointer
|
2028
|
+
out.push(...number(pointer));
|
2029
|
+
|
2030
|
+
return [ out, pointer ];
|
2031
|
+
}
|
2032
|
+
|
1819
2033
|
// store length as 0th array
|
1820
2034
|
out.push(
|
1821
2035
|
...number(0, Valtype.i32),
|
@@ -1823,8 +2037,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1823
2037
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
|
1824
2038
|
);
|
1825
2039
|
|
1826
|
-
const storeOp =
|
1827
|
-
const valtype = itemTypeToValtype[itemType];
|
2040
|
+
const storeOp = StoreOps[itemType];
|
1828
2041
|
|
1829
2042
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
1830
2043
|
if (elements[i] == null) continue;
|
@@ -2148,10 +2361,10 @@ const generateFunc = (scope, decl) => {
|
|
2148
2361
|
};
|
2149
2362
|
|
2150
2363
|
const generateCode = (scope, decl) => {
|
2151
|
-
|
2364
|
+
let out = [];
|
2152
2365
|
|
2153
2366
|
for (const x of decl.body) {
|
2154
|
-
out.
|
2367
|
+
out = out.concat(generate(scope, x));
|
2155
2368
|
}
|
2156
2369
|
|
2157
2370
|
return out;
|
@@ -2201,6 +2414,7 @@ export default program => {
|
|
2201
2414
|
typeStates = {};
|
2202
2415
|
arrays = new Map();
|
2203
2416
|
pages = new Map();
|
2417
|
+
data = [];
|
2204
2418
|
currentFuncIndex = importedFuncs.length;
|
2205
2419
|
|
2206
2420
|
globalThis.valtype = 'f64';
|
@@ -2277,5 +2491,5 @@ export default program => {
|
|
2277
2491
|
// if blank main func and other exports, remove it
|
2278
2492
|
if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(funcs.length - 1, 1);
|
2279
2493
|
|
2280
|
-
return { funcs, globals, tags, exceptions, pages };
|
2494
|
+
return { funcs, globals, tags, exceptions, pages, data };
|
2281
2495
|
};
|
package/compiler/embedding.js
CHANGED
@@ -9,11 +9,15 @@ export const number = (n, valtype = valtypeBinary) => {
|
|
9
9
|
}
|
10
10
|
};
|
11
11
|
|
12
|
-
const
|
12
|
+
export const enforceOneByte = arr => [ arr[0] ?? 0 ];
|
13
|
+
export const enforceTwoBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0 ];
|
14
|
+
export const enforceFourBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0 ];
|
15
|
+
export const enforceEightBytes = arr => [ arr[0] ?? 0, arr[1] ?? 0, arr[2] ?? 0, arr[3] ?? 0, arr[4] ?? 0, arr[5] ?? 0, arr[6] ?? 0, arr[7] ?? 0 ];
|
16
|
+
|
13
17
|
export const i32x4 = (a, b, c, d) => [ [
|
14
18
|
...Opcodes.v128_const,
|
15
|
-
...
|
16
|
-
...
|
17
|
-
...
|
18
|
-
...
|
19
|
+
...enforceFourBytes(signedLEB128(a)),
|
20
|
+
...enforceFourBytes(signedLEB128(b)),
|
21
|
+
...enforceFourBytes(signedLEB128(c)),
|
22
|
+
...enforceFourBytes(signedLEB128(d))
|
19
23
|
] ];
|
package/compiler/index.js
CHANGED
@@ -59,7 +59,7 @@ export default (code, flags) => {
|
|
59
59
|
if (flags.includes('info')) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
|
60
60
|
|
61
61
|
const t1 = performance.now();
|
62
|
-
const { funcs, globals, tags, exceptions, pages } = codeGen(program);
|
62
|
+
const { funcs, globals, tags, exceptions, pages, data } = codeGen(program);
|
63
63
|
if (flags.includes('info')) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
|
64
64
|
|
65
65
|
if (process.argv.includes('-funcs')) logFuncs(funcs, globals, exceptions);
|
@@ -71,14 +71,14 @@ export default (code, flags) => {
|
|
71
71
|
if (process.argv.includes('-opt-funcs')) logFuncs(funcs, globals, exceptions);
|
72
72
|
|
73
73
|
const t3 = performance.now();
|
74
|
-
const sections = produceSections(funcs, globals, tags, pages, flags);
|
74
|
+
const sections = produceSections(funcs, globals, tags, pages, data, flags);
|
75
75
|
if (flags.includes('info')) console.log(`4. produced sections in ${(performance.now() - t3).toFixed(2)}ms`);
|
76
76
|
|
77
77
|
if (allocLog) {
|
78
78
|
const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
|
79
79
|
const bytes = wasmPages * 65536;
|
80
80
|
log('alloc', `\x1B[1mallocated ${bytes / 1024}KiB\x1B[0m for ${pages.size} things using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}`);
|
81
|
-
|
81
|
+
console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n') + '\n');
|
82
82
|
}
|
83
83
|
|
84
84
|
const out = { wasm: sections, funcs, globals, tags, exceptions, pages };
|
@@ -100,10 +100,10 @@ export default (code, flags) => {
|
|
100
100
|
|
101
101
|
if (target === 'native') {
|
102
102
|
const compiler = getArg('compiler') ?? 'clang';
|
103
|
-
const cO = getArg('cO') ?? '
|
103
|
+
const cO = getArg('cO') ?? 'Ofast';
|
104
104
|
|
105
105
|
const tmpfile = 'tmp.c';
|
106
|
-
const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO ];
|
106
|
+
const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native' ];
|
107
107
|
|
108
108
|
const c = toc(out);
|
109
109
|
writeFileSync(tmpfile, c);
|
package/compiler/opt.js
CHANGED
@@ -317,28 +317,24 @@ export default (funcs, globals) => {
|
|
317
317
|
continue;
|
318
318
|
}
|
319
319
|
|
320
|
-
|
321
|
-
const
|
320
|
+
// remove unneeded before get with update exprs (n++, etc) when value is unused
|
321
|
+
if (i < wasm.length - 4 && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get && wasm[i + 1][0] === Opcodes.const && [Opcodes.add, Opcodes.sub].includes(wasm[i + 2][0]) && wasm[i + 3][0] === Opcodes.local_set && wasm[i + 3][1] === inst[1] && (wasm[i + 4][0] === Opcodes.drop || wasm[i + 4][0] === Opcodes.br)) {
|
322
|
+
// local.get 1
|
323
|
+
// local.get 1
|
324
|
+
// -->
|
325
|
+
// local.get 1
|
322
326
|
|
323
|
-
|
324
|
-
|
325
|
-
if (lastLastInst[0] === Opcodes.end && lastInst[1] === inst[1] && lastInst[0] === Opcodes.local_get && inst[0] === Opcodes.local_get) {
|
326
|
-
// local.get 1
|
327
|
-
// local.get 1
|
328
|
-
// -->
|
329
|
-
// local.get 1
|
330
|
-
|
331
|
-
// remove drop at the end as well
|
332
|
-
if (wasm[i + 4][0] === Opcodes.drop) {
|
333
|
-
wasm.splice(i + 4, 1);
|
334
|
-
}
|
327
|
+
// remove drop at the end as well
|
328
|
+
if (wasm[i + 4][0] === Opcodes.drop) wasm.splice(i + 4, 1);
|
335
329
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
}
|
330
|
+
wasm.splice(i, 1); // remove this inst (second get)
|
331
|
+
i--;
|
332
|
+
continue;
|
340
333
|
}
|
341
334
|
|
335
|
+
if (i < 2) continue;
|
336
|
+
const lastLastInst = wasm[i - 2];
|
337
|
+
|
342
338
|
if (lastLastInst[1] === inst[1] && inst[0] === Opcodes.local_get && lastInst[0] === Opcodes.local_tee && lastLastInst[0] === Opcodes.local_set) {
|
343
339
|
// local.set x
|
344
340
|
// local.tee y
|
package/compiler/parse.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { parse } from 'acorn';
|
2
|
-
|
1
|
+
// import { parse } from 'acorn';
|
2
|
+
const { parse } = (await import(globalThis.document ? 'https://esm.sh/acorn' : 'acorn'));
|
3
3
|
|
4
4
|
export default (input, flags) => {
|
5
5
|
return parse(input, {
|