porffor 0.0.0-c743344 → 0.0.0-d650361
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 +43 -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 +295 -75
- 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 +90 -29
- 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 +39 -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 +54 -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
|
+
}
|
189
|
+
|
190
|
+
return out;
|
191
|
+
},
|
181
192
|
|
182
|
-
|
183
|
-
|
193
|
+
__internal_print_type: str => {
|
194
|
+
const type = getType(scope, str) - TYPES.number;
|
184
195
|
|
185
|
-
|
186
|
-
|
196
|
+
return [
|
197
|
+
...number(type),
|
198
|
+
[ Opcodes.call, importedFuncs.print ],
|
187
199
|
|
188
|
-
|
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}!`);
|
@@ -664,29 +686,44 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
664
686
|
|
665
687
|
if (codeLog && (!leftType || !rightType)) log('codegen', 'untracked type in op', op, _name, '\n' + new Error().stack.split('\n').slice(1).join('\n'));
|
666
688
|
|
667
|
-
|
668
|
-
|
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
|
+
|
669
705
|
return [
|
670
706
|
...left,
|
671
|
-
...right,
|
672
|
-
|
673
|
-
// drop values
|
674
707
|
[ Opcodes.drop ],
|
708
|
+
|
709
|
+
...right,
|
675
710
|
[ Opcodes.drop ],
|
676
711
|
|
677
|
-
// return
|
678
|
-
...number(op === '===' ?
|
712
|
+
// return true (!=/!==) or false (else)
|
713
|
+
...number(op === '!=' || op === '!==' ? 1 : 0, Valtype.i32)
|
679
714
|
];
|
680
715
|
}
|
681
716
|
|
717
|
+
// todo: niche null hell with 0
|
718
|
+
|
682
719
|
if (leftType === TYPES.string || rightType === TYPES.string) {
|
683
720
|
if (op === '+') {
|
684
721
|
// string concat (a + b)
|
685
722
|
return concatStrings(scope, left, right, _global, _name, assign);
|
686
723
|
}
|
687
724
|
|
688
|
-
//
|
689
|
-
if (!
|
725
|
+
// not an equality op, NaN
|
726
|
+
if (!eqOp) return number(NaN);
|
690
727
|
|
691
728
|
// else leave bool ops
|
692
729
|
// todo: convert string to number if string and number/bool
|
@@ -731,21 +768,15 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
731
768
|
];
|
732
769
|
};
|
733
770
|
|
734
|
-
let binaryExpDepth = 0;
|
735
771
|
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
|
-
];
|
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);
|
741
773
|
|
742
774
|
if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
|
743
775
|
|
744
|
-
binaryExpDepth--;
|
745
776
|
return out;
|
746
777
|
};
|
747
778
|
|
748
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType,
|
779
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
|
749
780
|
const existing = funcs.find(x => x.name === name);
|
750
781
|
if (existing) return existing;
|
751
782
|
|
@@ -781,7 +812,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
781
812
|
returns,
|
782
813
|
returnType: TYPES[returnType ?? 'number'],
|
783
814
|
wasm,
|
784
|
-
memory,
|
785
815
|
internal: true,
|
786
816
|
index: currentFuncIndex++
|
787
817
|
};
|
@@ -814,7 +844,8 @@ const TYPES = {
|
|
814
844
|
bigint: 0xffffffffffff7,
|
815
845
|
|
816
846
|
// these are not "typeof" types but tracked internally
|
817
|
-
_array:
|
847
|
+
_array: 0xfffffffffff0f,
|
848
|
+
_regexp: 0xfffffffffff1f
|
818
849
|
};
|
819
850
|
|
820
851
|
const TYPE_NAMES = {
|
@@ -849,6 +880,8 @@ const getType = (scope, _name) => {
|
|
849
880
|
const getNodeType = (scope, node) => {
|
850
881
|
if (node.type === 'Literal') {
|
851
882
|
if (['number', 'boolean', 'string', 'undefined', 'object', 'function', 'symbol', 'bigint'].includes(node.value)) return TYPES.number;
|
883
|
+
if (node.regex) return TYPES._regexp;
|
884
|
+
|
852
885
|
return TYPES[typeof node.value];
|
853
886
|
}
|
854
887
|
|
@@ -882,6 +915,11 @@ const getNodeType = (scope, node) => {
|
|
882
915
|
|
883
916
|
// literal.func()
|
884
917
|
if (!name && node.callee.type === 'MemberExpression') {
|
918
|
+
if (node.callee.object.regex) {
|
919
|
+
const funcName = node.callee.property.name;
|
920
|
+
return Rhemyn[funcName] ? TYPES.boolean : TYPES.undefined;
|
921
|
+
}
|
922
|
+
|
885
923
|
const baseType = getNodeType(scope, node.callee.object);
|
886
924
|
|
887
925
|
const func = node.callee.property.name;
|
@@ -925,6 +963,11 @@ const getNodeType = (scope, node) => {
|
|
925
963
|
const generateLiteral = (scope, decl, global, name) => {
|
926
964
|
if (decl.value === null) return number(NULL);
|
927
965
|
|
966
|
+
if (decl.regex) {
|
967
|
+
scope.regex[name] = decl.regex;
|
968
|
+
return number(1);
|
969
|
+
}
|
970
|
+
|
928
971
|
switch (typeof decl.value) {
|
929
972
|
case 'number':
|
930
973
|
return number(decl.value);
|
@@ -986,6 +1029,8 @@ const countLeftover = wasm => {
|
|
986
1029
|
} else count--;
|
987
1030
|
if (func) count += func.returns.length;
|
988
1031
|
} else count--;
|
1032
|
+
|
1033
|
+
// console.log(count, decompile([ inst ]).slice(0, -1));
|
989
1034
|
}
|
990
1035
|
|
991
1036
|
return count;
|
@@ -1084,6 +1129,25 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1084
1129
|
|
1085
1130
|
// literal.func()
|
1086
1131
|
if (!name && decl.callee.type === 'MemberExpression') {
|
1132
|
+
// megahack for /regex/.func()
|
1133
|
+
if (decl.callee.object.regex) {
|
1134
|
+
const funcName = decl.callee.property.name;
|
1135
|
+
const func = Rhemyn[funcName](decl.callee.object.regex.pattern, currentFuncIndex++);
|
1136
|
+
|
1137
|
+
funcIndex[func.name] = func.index;
|
1138
|
+
funcs.push(func);
|
1139
|
+
|
1140
|
+
return [
|
1141
|
+
// make string arg
|
1142
|
+
...generate(scope, decl.arguments[0]),
|
1143
|
+
|
1144
|
+
// call regex func
|
1145
|
+
Opcodes.i32_to_u,
|
1146
|
+
[ Opcodes.call, func.index ],
|
1147
|
+
Opcodes.i32_from
|
1148
|
+
];
|
1149
|
+
}
|
1150
|
+
|
1087
1151
|
baseType = getNodeType(scope, decl.callee.object);
|
1088
1152
|
|
1089
1153
|
const func = decl.callee.property.name;
|
@@ -1096,6 +1160,31 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1096
1160
|
baseName = [...arrays.keys()].pop();
|
1097
1161
|
}
|
1098
1162
|
|
1163
|
+
if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
|
1164
|
+
const func = Rhemyn[protoName](decl.arguments[0].regex.pattern, currentFuncIndex++);
|
1165
|
+
|
1166
|
+
funcIndex[func.name] = func.index;
|
1167
|
+
funcs.push(func);
|
1168
|
+
|
1169
|
+
const pointer = arrays.get(baseName);
|
1170
|
+
const [ local, isGlobal ] = lookupName(scope, baseName);
|
1171
|
+
|
1172
|
+
return [
|
1173
|
+
...out,
|
1174
|
+
|
1175
|
+
...(pointer == null ? [
|
1176
|
+
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1177
|
+
Opcodes.i32_to_u,
|
1178
|
+
] : [
|
1179
|
+
...number(pointer, Valtype.i32)
|
1180
|
+
]),
|
1181
|
+
|
1182
|
+
// call regex func
|
1183
|
+
[ Opcodes.call, func.index ],
|
1184
|
+
Opcodes.i32_from
|
1185
|
+
];
|
1186
|
+
}
|
1187
|
+
|
1099
1188
|
if (protoFunc) {
|
1100
1189
|
let pointer = arrays.get(baseName);
|
1101
1190
|
|
@@ -1136,9 +1225,10 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1136
1225
|
|
1137
1226
|
[ Opcodes.block, valtypeBinary ],
|
1138
1227
|
...protoFunc(pointer, {
|
1139
|
-
|
1140
|
-
|
1141
|
-
|
1228
|
+
getCachedI32: () => [ [ Opcodes.local_get, lengthLocal ] ],
|
1229
|
+
setCachedI32: () => [ [ Opcodes.local_set, lengthLocal ] ],
|
1230
|
+
get: () => arrayUtil.getLength(pointer),
|
1231
|
+
getI32: () => arrayUtil.getLengthI32(pointer),
|
1142
1232
|
set: value => arrayUtil.setLength(pointer, value),
|
1143
1233
|
setI32: value => arrayUtil.setLengthI32(pointer, value)
|
1144
1234
|
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, (length, itemType) => {
|
@@ -1205,7 +1295,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1205
1295
|
if (func && func.throws) scope.throws = true;
|
1206
1296
|
|
1207
1297
|
for (const arg of args) {
|
1208
|
-
out.
|
1298
|
+
out = out.concat(generate(scope, arg));
|
1209
1299
|
}
|
1210
1300
|
|
1211
1301
|
out.push([ Opcodes.call, idx ]);
|
@@ -1345,6 +1435,47 @@ const generateAssign = (scope, decl) => {
|
|
1345
1435
|
];
|
1346
1436
|
}
|
1347
1437
|
|
1438
|
+
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
1439
|
+
// arr[i] | str[i]
|
1440
|
+
const name = decl.left.object.name;
|
1441
|
+
const pointer = arrays.get(name);
|
1442
|
+
|
1443
|
+
const aotPointer = pointer != null;
|
1444
|
+
|
1445
|
+
const newValueTmp = localTmp(scope, '__member_setter_tmp');
|
1446
|
+
|
1447
|
+
const parentType = getNodeType(scope, decl.left.object);
|
1448
|
+
|
1449
|
+
const op = decl.operator.slice(0, -1);
|
1450
|
+
return [
|
1451
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
1452
|
+
...generate(scope, decl.left.object),
|
1453
|
+
Opcodes.i32_to_u
|
1454
|
+
]),
|
1455
|
+
|
1456
|
+
// get index as valtype
|
1457
|
+
...generate(scope, decl.left.property),
|
1458
|
+
|
1459
|
+
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
1460
|
+
Opcodes.i32_to_u,
|
1461
|
+
...number(ValtypeSize[valtype], Valtype.i32),
|
1462
|
+
[ Opcodes.i32_mul ],
|
1463
|
+
[ Opcodes.i32_add ],
|
1464
|
+
|
1465
|
+
...(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)),
|
1466
|
+
[ Opcodes.local_tee, newValueTmp ],
|
1467
|
+
|
1468
|
+
...(parentType === TYPES._array ? [
|
1469
|
+
[ Opcodes.store, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1470
|
+
] : [
|
1471
|
+
Opcodes.i32_to_u,
|
1472
|
+
[ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
1473
|
+
]),
|
1474
|
+
|
1475
|
+
[ Opcodes.local_get, newValueTmp ]
|
1476
|
+
];
|
1477
|
+
}
|
1478
|
+
|
1348
1479
|
const [ local, isGlobal ] = lookupName(scope, name);
|
1349
1480
|
|
1350
1481
|
if (local === undefined) {
|
@@ -1365,8 +1496,10 @@ const generateAssign = (scope, decl) => {
|
|
1365
1496
|
];
|
1366
1497
|
}
|
1367
1498
|
|
1499
|
+
typeStates[name] = getNodeType(scope, decl.right);
|
1500
|
+
|
1368
1501
|
if (decl.operator === '=') {
|
1369
|
-
typeStates[name] = getNodeType(scope, decl.right);
|
1502
|
+
// typeStates[name] = getNodeType(scope, decl.right);
|
1370
1503
|
|
1371
1504
|
return [
|
1372
1505
|
...generate(scope, decl.right, isGlobal, name),
|
@@ -1510,7 +1643,7 @@ const generateUpdate = (scope, decl) => {
|
|
1510
1643
|
};
|
1511
1644
|
|
1512
1645
|
const generateIf = (scope, decl) => {
|
1513
|
-
const out = truthy(scope, generate(scope, decl.test), decl.test);
|
1646
|
+
const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test));
|
1514
1647
|
|
1515
1648
|
out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
|
1516
1649
|
depth.push('if');
|
@@ -1603,18 +1736,106 @@ const generateWhile = (scope, decl) => {
|
|
1603
1736
|
const generateForOf = (scope, decl) => {
|
1604
1737
|
const out = [];
|
1605
1738
|
|
1739
|
+
const rightType = getNodeType(scope, decl.right);
|
1740
|
+
const valtypeSize = rightType === TYPES._array ? ValtypeSize[valtype] : ValtypeSize.i16; // presume array (:()
|
1741
|
+
|
1742
|
+
// todo: for of inside for of might fuck up?
|
1743
|
+
const pointer = localTmp(scope, 'forof_base_pointer', Valtype.i32);
|
1744
|
+
const length = localTmp(scope, 'forof_length', Valtype.i32);
|
1745
|
+
const counter = localTmp(scope, 'forof_counter', Valtype.i32);
|
1746
|
+
|
1747
|
+
out.push(
|
1748
|
+
// set pointer as right
|
1749
|
+
...generate(scope, decl.right),
|
1750
|
+
Opcodes.i32_to_u,
|
1751
|
+
[ Opcodes.local_set, pointer ],
|
1752
|
+
|
1753
|
+
// get length
|
1754
|
+
[ Opcodes.local_get, pointer ],
|
1755
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
1756
|
+
[ Opcodes.local_set, length ]
|
1757
|
+
);
|
1758
|
+
|
1606
1759
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
1607
|
-
depth.push('
|
1760
|
+
depth.push('forof');
|
1608
1761
|
|
1609
|
-
|
1610
|
-
|
1611
|
-
depth.push('if');
|
1762
|
+
// setup local for left
|
1763
|
+
generate(scope, decl.left);
|
1612
1764
|
|
1613
|
-
|
1765
|
+
const leftName = decl.left.declarations[0].id.name;
|
1614
1766
|
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1767
|
+
// set type for local
|
1768
|
+
typeStates[leftName] = rightType === TYPES._array ? TYPES.number : TYPES.string;
|
1769
|
+
|
1770
|
+
const [ local, isGlobal ] = lookupName(scope, leftName);
|
1771
|
+
|
1772
|
+
if (rightType === TYPES._array) { // array
|
1773
|
+
out.push(
|
1774
|
+
[ Opcodes.local_get, pointer ],
|
1775
|
+
[ Opcodes.load, Math.log2(valtypeSize) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
|
1776
|
+
);
|
1777
|
+
} else { // string
|
1778
|
+
const [ newOut, newPointer ] = makeArray(scope, {
|
1779
|
+
rawElements: new Array(1)
|
1780
|
+
}, isGlobal, leftName, true, 'i16');
|
1781
|
+
|
1782
|
+
out.push(
|
1783
|
+
// setup new/out array
|
1784
|
+
...newOut,
|
1785
|
+
[ Opcodes.drop ],
|
1786
|
+
|
1787
|
+
...number(0, Valtype.i32), // base 0 for store after
|
1788
|
+
|
1789
|
+
// load current string ind {arg}
|
1790
|
+
[ Opcodes.local_get, pointer ],
|
1791
|
+
[ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
|
1792
|
+
|
1793
|
+
// store to new string ind 0
|
1794
|
+
[ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
|
1795
|
+
|
1796
|
+
// return new string (page)
|
1797
|
+
...number(newPointer)
|
1798
|
+
);
|
1799
|
+
}
|
1800
|
+
|
1801
|
+
// set left value
|
1802
|
+
out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ]);
|
1803
|
+
|
1804
|
+
out.push(
|
1805
|
+
[ Opcodes.block, Blocktype.void ],
|
1806
|
+
[ Opcodes.block, Blocktype.void ]
|
1807
|
+
);
|
1808
|
+
depth.push('block');
|
1809
|
+
depth.push('block');
|
1810
|
+
|
1811
|
+
out.push(
|
1812
|
+
...generate(scope, decl.body),
|
1813
|
+
[ Opcodes.end ]
|
1814
|
+
);
|
1815
|
+
depth.pop();
|
1816
|
+
|
1817
|
+
out.push(
|
1818
|
+
// increment iter pointer by valtype size
|
1819
|
+
[ Opcodes.local_get, pointer ],
|
1820
|
+
...number(valtypeSize, Valtype.i32),
|
1821
|
+
[ Opcodes.i32_add ],
|
1822
|
+
[ Opcodes.local_set, pointer ],
|
1823
|
+
|
1824
|
+
// increment counter by 1
|
1825
|
+
[ Opcodes.local_get, counter ],
|
1826
|
+
...number(1, Valtype.i32),
|
1827
|
+
[ Opcodes.i32_add ],
|
1828
|
+
[ Opcodes.local_tee, counter ],
|
1829
|
+
|
1830
|
+
// loop if counter != length
|
1831
|
+
[ Opcodes.local_get, length ],
|
1832
|
+
[ Opcodes.i32_ne ],
|
1833
|
+
[ Opcodes.br_if, 1 ],
|
1834
|
+
|
1835
|
+
[ Opcodes.end ], [ Opcodes.end ]
|
1836
|
+
);
|
1837
|
+
depth.pop();
|
1838
|
+
depth.pop();
|
1618
1839
|
|
1619
1840
|
return out;
|
1620
1841
|
};
|
@@ -1705,19 +1926,19 @@ const generateAssignPat = (scope, decl) => {
|
|
1705
1926
|
};
|
1706
1927
|
|
1707
1928
|
let pages = new Map();
|
1708
|
-
const allocPage = reason => {
|
1709
|
-
if (pages.has(reason)) return pages.get(reason);
|
1929
|
+
const allocPage = (reason, type) => {
|
1930
|
+
if (pages.has(reason)) return pages.get(reason).ind;
|
1710
1931
|
|
1711
|
-
|
1712
|
-
pages.set(reason, ind);
|
1932
|
+
const ind = pages.size;
|
1933
|
+
pages.set(reason, { ind, type });
|
1713
1934
|
|
1714
|
-
if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason}`);
|
1935
|
+
if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
1715
1936
|
|
1716
1937
|
return ind;
|
1717
1938
|
};
|
1718
1939
|
|
1719
1940
|
const freePage = reason => {
|
1720
|
-
|
1941
|
+
const { ind } = pages.get(reason);
|
1721
1942
|
pages.delete(reason);
|
1722
1943
|
|
1723
1944
|
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
@@ -1734,7 +1955,7 @@ const itemTypeToValtype = {
|
|
1734
1955
|
i16: 'i32'
|
1735
1956
|
};
|
1736
1957
|
|
1737
|
-
const
|
1958
|
+
const StoreOps = {
|
1738
1959
|
i32: Opcodes.i32_store,
|
1739
1960
|
i64: Opcodes.i64_store,
|
1740
1961
|
f64: Opcodes.f64_store,
|
@@ -1749,7 +1970,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1749
1970
|
if (!arrays.has(name) || name === '$undeclared') {
|
1750
1971
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
1751
1972
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
1752
|
-
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}
|
1973
|
+
arrays.set(name, allocPage(`${itemType === 'i16' ? 'string' : 'array'}: ${uniqueName}`, itemType) * pageSize);
|
1753
1974
|
}
|
1754
1975
|
|
1755
1976
|
const pointer = arrays.get(name);
|
@@ -1766,7 +1987,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
1766
1987
|
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ]
|
1767
1988
|
);
|
1768
1989
|
|
1769
|
-
const storeOp =
|
1990
|
+
const storeOp = StoreOps[itemType];
|
1770
1991
|
const valtype = itemTypeToValtype[itemType];
|
1771
1992
|
|
1772
1993
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
@@ -1929,7 +2150,6 @@ const generateFunc = (scope, decl) => {
|
|
1929
2150
|
localInd: 0,
|
1930
2151
|
returns: [ valtypeBinary ],
|
1931
2152
|
returnType: null,
|
1932
|
-
memory: false,
|
1933
2153
|
throws: false,
|
1934
2154
|
name
|
1935
2155
|
};
|
@@ -2092,10 +2312,10 @@ const generateFunc = (scope, decl) => {
|
|
2092
2312
|
};
|
2093
2313
|
|
2094
2314
|
const generateCode = (scope, decl) => {
|
2095
|
-
|
2315
|
+
let out = [];
|
2096
2316
|
|
2097
2317
|
for (const x of decl.body) {
|
2098
|
-
out.
|
2318
|
+
out = out.concat(generate(scope, x));
|
2099
2319
|
}
|
2100
2320
|
|
2101
2321
|
return out;
|
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