porffor 0.0.0-c743344 → 0.0.0-e6047ea

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.
@@ -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
- /* case 'ForOfStatement':
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
- // hack for inline asm
156
- if (decl.tag.name !== 'asm') return todo('tagged template expressions not implemented');
155
+ case 'TaggedTemplateExpression': {
156
+ const funcs = {
157
+ asm: str => {
158
+ let out = [];
157
159
 
158
- const str = decl.quasi.quasis[0].value.raw;
159
- let out = [];
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
- for (const line of str.split('\n')) {
162
- const asm = line.trim().split(';;')[0].split(' ');
163
- if (asm[0] === '') continue; // blank
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
- if (asm[0] === 'local') {
166
- const [ name, idx, type ] = asm.slice(1);
167
- scope.locals[name] = { idx: parseInt(idx), type: Valtype[type] };
168
- continue;
169
- }
170
+ if (asm[0] === 'returns') {
171
+ scope.returns = asm.slice(1).map(x => Valtype[x]);
172
+ continue;
173
+ }
170
174
 
171
- if (asm[0] === 'returns') {
172
- scope.returns = asm.slice(1).map(x => Valtype[x]);
173
- continue;
174
- }
175
+ if (asm[0] === 'memory') {
176
+ allocPage('asm instrinsic');
177
+ // todo: add to store/load offset insts
178
+ continue;
179
+ }
175
180
 
176
- if (asm[0] === 'memory') {
177
- allocPage('asm instrinsic');
178
- // todo: add to store/load offset insts
179
- continue;
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
- let inst = Opcodes[asm[0].replace('.', '_')];
183
- if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
190
+ return out;
191
+ },
184
192
 
185
- if (!Array.isArray(inst)) inst = [ inst ];
186
- const immediates = asm.slice(1).map(x => parseInt(x));
193
+ __internal_print_type: str => {
194
+ const type = getType(scope, str) - TYPES.number;
187
195
 
188
- out.push([ ...inst, ...immediates ]);
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
- return out;
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
- binaryExpDepth++;
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, memory, localNames = [], globalNames = [] }) => {
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: 0xffffffffffff8
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('while');
1744
+ depth.push('forof');
1608
1745
 
1609
- out.push(...generate(scope, decl.test));
1610
- out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
1611
- depth.push('if');
1746
+ // setup local for left
1747
+ generate(scope, decl.left);
1612
1748
 
1613
- out.push(...generate(scope, decl.body));
1749
+ const leftName = decl.left.declarations[0].id.name;
1614
1750
 
1615
- out.push([ Opcodes.br, 1 ]);
1616
- out.push([ Opcodes.end ], [ Opcodes.end ]);
1617
- depth.pop(); depth.pop();
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
- let ind = pages.size;
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
- let ind = pages.get(reason);
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 storeOps = {
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}`) * pageSize);
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 = storeOps[itemType];
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
  };
@@ -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
- return { wasm: sections, funcs, globals, tags, exceptions, pages };
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
@@ -142,7 +142,7 @@ export default (funcs, globals) => {
142
142
  depth--;
143
143
  if (depth <= 0) break;
144
144
  }
145
- if (op === Opcodes.br) {
145
+ if (op === Opcodes.br || op === Opcodes.br_if) {
146
146
  hasBranch = true;
147
147
  break;
148
148
  }
package/compiler/parse.js CHANGED
@@ -1,3 +1,4 @@
1
+ // import { parse } from 'acorn';
1
2
  const { parse } = (await import(globalThis.document ? 'https://esm.sh/acorn' : 'acorn'));
2
3
 
3
4
  export default (input, flags) => {
@@ -15,7 +15,8 @@ const TYPES = {
15
15
  bigint: 0xffffffffffff7,
16
16
 
17
17
  // these are not "typeof" types but tracked internally
18
- _array: 0xffffffffffff8
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
@@ -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,