porffor 0.2.0-536e463 → 0.2.0-5ac7ea0

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.
@@ -0,0 +1,129 @@
1
+ import { Opcodes } from './wasmSpec.js';
2
+
3
+ import fs from 'node:fs';
4
+ import { join } from 'node:path';
5
+
6
+ import { fileURLToPath } from 'node:url';
7
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
8
+
9
+ const TYPES = {
10
+ number: 0x00,
11
+ boolean: 0x01,
12
+ string: 0x02,
13
+ undefined: 0x03,
14
+ object: 0x04,
15
+ function: 0x05,
16
+ symbol: 0x06,
17
+ bigint: 0x07,
18
+
19
+ // these are not "typeof" types but tracked internally
20
+ _array: 0x10,
21
+ _regexp: 0x11,
22
+ _bytestring: 0x12
23
+ };
24
+
25
+ // import porfParse from './parse.js';
26
+ // import porfCodegen from './codeGen.js';
27
+
28
+ const argv = process.argv.slice();
29
+
30
+ const compile = async (file, [ _funcs, _globals ]) => {
31
+ const source = fs.readFileSync(file, 'utf8');
32
+ const first = source.slice(0, source.indexOf('\n'));
33
+
34
+ let args = ['-bytestring'];
35
+ if (file.endsWith('.ts')) args.push('-parse-types', '-opt-types');
36
+ if (first.startsWith('// @porf')) {
37
+ args = args.concat(first.slice('// @porf '.length).split(' '));
38
+ }
39
+ process.argv = argv.concat(args);
40
+
41
+ // const porfParse = (await import(`./parse.js?_=${Date.now()}`)).default;
42
+ // const porfCodegen = (await import(`./codeGen.js?_=${Date.now()}`)).default;
43
+
44
+ // let { funcs, globals, data } = porfCodegen(porfParse(source, ['module']));
45
+
46
+ const porfCompile = (await import(`./index.js?_=${Date.now()}`)).default;
47
+
48
+ let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
49
+
50
+ const allocated = new Set();
51
+
52
+ const exports = funcs.filter(x => x.export);
53
+ for (const x of exports) {
54
+ if (x.data) x.data = x.data.map(x => data[x]);
55
+ if (x.exceptions) x.exceptions = x.exceptions.map(x => {
56
+ const obj = exceptions[x];
57
+ if (obj) obj.exceptId = x;
58
+ return obj;
59
+ }).filter(x => x);
60
+
61
+ const locals = Object.keys(x.locals).reduce((acc, y) => {
62
+ acc[x.locals[y].idx] = { ...x.locals[y], name: y };
63
+ return acc;
64
+ }, {});
65
+
66
+ for (let i = 0; i < x.wasm.length; i++) {
67
+ const y = x.wasm[i];
68
+ const n = x.wasm[i + 1];
69
+ if (y[0] === Opcodes.call) {
70
+ const f = funcs.find(x => x.index === y[1]);
71
+ if (!f) continue;
72
+
73
+ y[1] = f.name;
74
+ }
75
+
76
+ if (y[0] === Opcodes.const && (n[0] === Opcodes.local_set || n[0] === Opcodes.local_tee)) {
77
+ const l = locals[n[1]];
78
+ if (!l) continue;
79
+ if (![TYPES.string, TYPES._array, TYPES._bytestring].includes(l.metadata?.type)) continue;
80
+ if (!x.pages) continue;
81
+
82
+ const pageName = [...x.pages.keys()].find(z => z.endsWith(l.name));
83
+ if (!pageName || allocated.has(pageName)) continue;
84
+ allocated.add(pageName);
85
+
86
+ y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type);
87
+ // y.push(x.pages.get(pageName));
88
+ }
89
+ }
90
+ }
91
+
92
+ _funcs.push(...exports);
93
+ _globals.push(...Object.values(globals));
94
+ };
95
+
96
+ const precompile = async () => {
97
+ const dir = join(__dirname, 'builtins');
98
+
99
+ let funcs = [], globals = [];
100
+ for (const file of fs.readdirSync(dir)) {
101
+ if (file.endsWith('.d.ts')) continue;
102
+ await compile(join(dir, file), [ funcs, globals ]);
103
+ }
104
+
105
+ // ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
106
+ // ${x.used && x.used.length > 0 ? ` used: ${JSON.stringify(x.used)},` : ''}
107
+
108
+ return `// autogenerated by compiler/precompile.js
109
+ import { number } from './embedding.js';
110
+
111
+ export const BuiltinFuncs = function() {
112
+ ${funcs.map(x => ` this.${x.name} = {
113
+ wasm: (scope, { allocPage, builtin }) => ${JSON.stringify(x.wasm.filter(x => x.length && x[0] != null)).replace(/\["alloc","(.*?)","(.*?)"\]/g, (_, reason, type) => `...number(allocPage(scope, '${reason}', '${type}') * pageSize, ${valtypeBinary})`).replace(/\[16,"(.*?)"]/g, (_, name) => `[16, builtin('${name}')]`)},
114
+ params: ${JSON.stringify(x.params)},
115
+ typedParams: true,
116
+ returns: ${JSON.stringify(x.returns)},
117
+ typedReturns: true,
118
+ locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
119
+ localNames: ${JSON.stringify(Object.keys(x.locals))},
120
+ ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
121
+ ${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
122
+ };`.replaceAll('\n\n', '\n')).join('\n')}
123
+ };`;
124
+ };
125
+
126
+ const code = await precompile();
127
+ // console.log(code);
128
+
129
+ fs.writeFileSync(join(__dirname, 'generated_builtins.js'), code);
@@ -0,0 +1,26 @@
1
+ const onByDefault = [ 'bytestring' ];
2
+
3
+ const cache = {};
4
+ const obj = new Proxy({}, {
5
+ get(_, p) {
6
+ // intentionally misses with undefined values cached
7
+ if (cache[p]) return cache[p];
8
+
9
+ return cache[p] = (() => {
10
+ // fooBar -> foo-bar
11
+ const name = p[0] === '_' ? p : p.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
12
+ if (process.argv.includes('-' + name)) return true;
13
+ if (process.argv.includes('-no-' + name)) return false;
14
+
15
+ const valArg = process.argv.find(x => x.startsWith(`-${name}=`));
16
+ if (valArg) return valArg.slice(name.length + 2);
17
+
18
+ if (onByDefault.includes(p)) return true;
19
+ return undefined;
20
+ })();
21
+ }
22
+ });
23
+
24
+ obj.uncache = () => cache = {};
25
+
26
+ export default obj;
@@ -2,6 +2,7 @@ import { Opcodes, Blocktype, Valtype, ValtypeSize, PageSize } from "./wasmSpec.j
2
2
  import { number } from "./embedding.js";
3
3
  import { unsignedLEB128 } from "./encoding.js";
4
4
  import { UNDEFINED } from "./builtins.js";
5
+ import Prefs from './prefs.js';
5
6
 
6
7
  // todo: do not duplicate this
7
8
  const TYPES = {
@@ -16,15 +17,17 @@ const TYPES = {
16
17
 
17
18
  // these are not "typeof" types but tracked internally
18
19
  _array: 0x10,
19
- _regexp: 0x11
20
+ _regexp: 0x11,
21
+ _bytestring: 0x12
20
22
  };
21
23
 
22
24
  // todo: turn these into built-ins once arrays and these become less hacky
23
25
 
24
26
  export const PrototypeFuncs = function() {
25
- const noUnlikelyChecks = process.argv.includes('-funsafe-no-unlikely-proto-checks');
26
- let zeroChecks = process.argv.find(x => x.startsWith('-funsafe-zero-proto-checks='));
27
- if (zeroChecks) zeroChecks = zeroChecks.split('=')[1].split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
27
+ const noUnlikelyChecks = Prefs.funsafeNoUnlikelyProtoChecks;
28
+
29
+ let zeroChecks;
30
+ if (Prefs.zeroChecks) zeroChecks = Prefs.zeroChecks.split('=')[1].split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
28
31
  else zeroChecks = {};
29
32
 
30
33
  this[TYPES._array] = {
@@ -71,7 +74,7 @@ export const PrototypeFuncs = function() {
71
74
  ],
72
75
 
73
76
  // todo: only for 1 argument
74
- push: (pointer, length, wNewMember) => [
77
+ push: (pointer, length, wNewMember, _1, _2, _3, unusedValue) => [
75
78
  // get memory offset of array at last index (length)
76
79
  ...length.getCachedI32(),
77
80
  ...number(ValtypeSize[valtype], Valtype.i32),
@@ -92,22 +95,28 @@ export const PrototypeFuncs = function() {
92
95
  ...number(1, Valtype.i32),
93
96
  [ Opcodes.i32_add ],
94
97
 
95
- ...length.setCachedI32(),
96
- ...length.getCachedI32(),
98
+ ...(unusedValue() ? [] : [
99
+ ...length.setCachedI32(),
100
+ ...length.getCachedI32(),
101
+ ])
97
102
  ]),
98
103
 
99
- ...length.getCachedI32(),
100
- Opcodes.i32_from_u
104
+ ...(unusedValue() ? [] : [
105
+ ...length.getCachedI32(),
106
+ Opcodes.i32_from_u
107
+ ])
101
108
 
102
109
  // ...length.get()
103
110
  ],
104
111
 
105
- pop: (pointer, length) => [
112
+ pop: (pointer, length, _1, _2, _3, _4, unusedValue) => [
106
113
  // if length == 0, noop
107
114
  ...length.getCachedI32(),
108
115
  [ Opcodes.i32_eqz ],
109
116
  [ Opcodes.if, Blocktype.void ],
110
- ...number(UNDEFINED),
117
+ ...(unusedValue() ? [] : [
118
+ ...number(UNDEFINED),
119
+ ]),
111
120
  [ Opcodes.br, 1 ],
112
121
  [ Opcodes.end ],
113
122
 
@@ -119,19 +128,23 @@ export const PrototypeFuncs = function() {
119
128
  ...number(1, Valtype.i32),
120
129
  [ Opcodes.i32_sub ],
121
130
 
122
- ...length.setCachedI32(),
123
- ...length.getCachedI32(),
131
+ ...(unusedValue() ? [] : [
132
+ ...length.setCachedI32(),
133
+ ...length.getCachedI32(),
134
+ ])
124
135
  ]),
125
136
 
126
137
  // load last element
127
- ...length.getCachedI32(),
128
- ...number(ValtypeSize[valtype], Valtype.i32),
129
- [ Opcodes.i32_mul ],
138
+ ...(unusedValue() ? [] : [
139
+ ...length.getCachedI32(),
140
+ ...number(ValtypeSize[valtype], Valtype.i32),
141
+ [ Opcodes.i32_mul ],
130
142
 
131
- ...pointer,
132
- [ Opcodes.i32_add ],
143
+ ...pointer,
144
+ [ Opcodes.i32_add ],
133
145
 
134
- [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
146
+ [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128(ValtypeSize.i32) ]
147
+ ])
135
148
  ],
136
149
 
137
150
  shift: (pointer, length) => [
@@ -331,8 +344,8 @@ export const PrototypeFuncs = function() {
331
344
  ...number(0, Valtype.i32), // base 0 for store later
332
345
 
333
346
  ...wIndex,
334
-
335
347
  Opcodes.i32_to,
348
+
336
349
  ...number(ValtypeSize.i16, Valtype.i32),
337
350
  [ Opcodes.i32_mul ],
338
351
 
@@ -372,7 +385,7 @@ export const PrototypeFuncs = function() {
372
385
 
373
386
  ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
374
387
  [ Opcodes.if, Blocktype.void ],
375
- ...number(NaN),
388
+ ...number(valtype === 'i32' ? -1 : NaN),
376
389
  [ Opcodes.br, 1 ],
377
390
  [ Opcodes.end ],
378
391
 
@@ -472,8 +485,151 @@ export const PrototypeFuncs = function() {
472
485
  this[TYPES.string].at.returnType = TYPES.string;
473
486
  this[TYPES.string].charAt.returnType = TYPES.string;
474
487
  this[TYPES.string].charCodeAt.local = Valtype.i32;
488
+ this[TYPES.string].charCodeAt.noPointerCache = zeroChecks.charcodeat;
475
489
 
476
490
  this[TYPES.string].isWellFormed.local = Valtype.i32;
477
491
  this[TYPES.string].isWellFormed.local2 = Valtype.i32;
478
492
  this[TYPES.string].isWellFormed.returnType = TYPES.boolean;
493
+
494
+ if (Prefs.bytestring) {
495
+ this[TYPES._bytestring] = {
496
+ at: (pointer, length, wIndex, iTmp, _, arrayShell) => {
497
+ const [ newOut, newPointer ] = arrayShell(1, 'i8');
498
+
499
+ return [
500
+ // setup new/out array
501
+ ...newOut,
502
+ [ Opcodes.drop ],
503
+
504
+ ...number(0, Valtype.i32), // base 0 for store later
505
+
506
+ ...wIndex,
507
+ Opcodes.i32_to_u,
508
+ [ Opcodes.local_tee, iTmp ],
509
+
510
+ // if index < 0: access index + array length
511
+ ...number(0, Valtype.i32),
512
+ [ Opcodes.i32_lt_s ],
513
+ [ Opcodes.if, Blocktype.void ],
514
+ [ Opcodes.local_get, iTmp ],
515
+ ...length.getCachedI32(),
516
+ [ Opcodes.i32_add ],
517
+ [ Opcodes.local_set, iTmp ],
518
+ [ Opcodes.end ],
519
+
520
+ // if still < 0 or >= length: return undefined
521
+ [ Opcodes.local_get, iTmp ],
522
+ ...number(0, Valtype.i32),
523
+ [ Opcodes.i32_lt_s ],
524
+
525
+ [ Opcodes.local_get, iTmp ],
526
+ ...length.getCachedI32(),
527
+ [ Opcodes.i32_ge_s ],
528
+ [ Opcodes.i32_or ],
529
+
530
+ [ Opcodes.if, Blocktype.void ],
531
+ ...number(UNDEFINED),
532
+ [ Opcodes.br, 1 ],
533
+ [ Opcodes.end ],
534
+
535
+ [ Opcodes.local_get, iTmp ],
536
+
537
+ ...pointer,
538
+ [ Opcodes.i32_add ],
539
+
540
+ // load current string ind {arg}
541
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
542
+
543
+ // store to new string ind 0
544
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
545
+
546
+ // return new string (pointer)
547
+ ...number(newPointer)
548
+ ];
549
+ },
550
+
551
+ // todo: out of bounds properly
552
+ charAt: (pointer, length, wIndex, _1, _2, arrayShell) => {
553
+ const [ newOut, newPointer ] = arrayShell(1, 'i8');
554
+
555
+ return [
556
+ // setup new/out array
557
+ ...newOut,
558
+ [ Opcodes.drop ],
559
+
560
+ ...number(0, Valtype.i32), // base 0 for store later
561
+
562
+ ...wIndex,
563
+ Opcodes.i32_to,
564
+
565
+ ...pointer,
566
+ [ Opcodes.i32_add ],
567
+
568
+ // load current string ind {arg}
569
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
570
+
571
+ // store to new string ind 0
572
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
573
+
574
+ // return new string (page)
575
+ ...number(newPointer)
576
+ ];
577
+ },
578
+
579
+ charCodeAt: (pointer, length, wIndex, iTmp) => {
580
+ return [
581
+ ...wIndex,
582
+ Opcodes.i32_to,
583
+
584
+ ...(zeroChecks.charcodeat ? [] : [
585
+ [ Opcodes.local_set, iTmp ],
586
+
587
+ // index < 0
588
+ ...(noUnlikelyChecks ? [] : [
589
+ [ Opcodes.local_get, iTmp ],
590
+ ...number(0, Valtype.i32),
591
+ [ Opcodes.i32_lt_s ],
592
+ ]),
593
+
594
+ // index >= length
595
+ [ Opcodes.local_get, iTmp ],
596
+ ...length.getCachedI32(),
597
+ [ Opcodes.i32_ge_s ],
598
+
599
+ ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
600
+ [ Opcodes.if, Blocktype.void ],
601
+ ...number(valtype === 'i32' ? -1 : NaN),
602
+ [ Opcodes.br, 1 ],
603
+ [ Opcodes.end ],
604
+
605
+ [ Opcodes.local_get, iTmp ],
606
+ ]),
607
+
608
+ ...pointer,
609
+ [ Opcodes.i32_add ],
610
+
611
+ // load current string ind {arg}
612
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
613
+ Opcodes.i32_from_u
614
+ ];
615
+ },
616
+
617
+ isWellFormed: () => {
618
+ return [
619
+ // we know it must be true as it is a bytestring lol
620
+ ...number(1)
621
+ ]
622
+ }
623
+ };
624
+
625
+ this[TYPES._bytestring].at.local = Valtype.i32;
626
+ this[TYPES._bytestring].at.returnType = TYPES._bytestring;
627
+ this[TYPES._bytestring].charAt.returnType = TYPES._bytestring;
628
+ this[TYPES._bytestring].charCodeAt.local = Valtype.i32;
629
+ this[TYPES._bytestring].charCodeAt.noPointerCache = zeroChecks.charcodeat;
630
+
631
+ this[TYPES._bytestring].isWellFormed.local = Valtype.i32;
632
+ this[TYPES._bytestring].isWellFormed.local2 = Valtype.i32;
633
+ this[TYPES._bytestring].isWellFormed.returnType = TYPES.boolean;
634
+ }
479
635
  };
@@ -3,6 +3,7 @@ import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128 }
3
3
  import { number } from './embedding.js';
4
4
  import { importedFuncs } from './builtins.js';
5
5
  import { log } from "./log.js";
6
+ import Prefs from './prefs.js';
6
7
 
7
8
  const createSection = (type, data) => [
8
9
  type,
@@ -26,12 +27,12 @@ export default (funcs, globals, tags, pages, data, flags) => {
26
27
 
27
28
  const optLevel = parseInt(process.argv.find(x => x.startsWith('-O'))?.[2] ?? 1);
28
29
 
29
- const compileHints = process.argv.includes('-compile-hints');
30
+ const compileHints = Prefs.compileHints;
30
31
  if (compileHints) log.warning('sections', 'compile hints is V8 only w/ experimental arg! (you used -compile-hints)');
31
32
 
32
33
  const getType = (params, returns) => {
33
34
  const hash = `${params.join(',')}_${returns.join(',')}`;
34
- if (optLog) log('sections', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
35
+ if (Prefs.optLog) log('sections', `getType(${JSON.stringify(params)}, ${JSON.stringify(returns)}) -> ${hash} | cache: ${typeCache[hash]}`);
35
36
  if (optLevel >= 1 && typeCache[hash] !== undefined) return typeCache[hash];
36
37
 
37
38
  const type = [ FuncType, ...encodeVector(params), ...encodeVector(returns) ];
@@ -79,7 +80,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
79
80
  }
80
81
  globalThis.importFuncs = importFuncs;
81
82
 
82
- if (optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
83
+ if (Prefs.optLog) log('sections', `treeshake: using ${importFuncs.length}/${importedFuncs.length} imports`);
83
84
 
84
85
  const importSection = importFuncs.length === 0 ? [] : createSection(
85
86
  Section.import,
@@ -95,7 +96,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
95
96
  // https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
96
97
  const chSection = !compileHints ? [] : customSection(
97
98
  'compilationHints',
98
- // for now just do everything as optimise eager
99
+ // for now just do everything as optimize eager
99
100
  encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
100
101
  );
101
102
 
@@ -106,7 +107,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
106
107
 
107
108
  const exports = funcs.filter(x => x.export).map((x, i) => [ ...encodeString(x.name === 'main' ? 'm' : x.name), ExportDesc.func, x.index ]);
108
109
 
109
- if (process.argv.includes('-always-memory') && pages.size === 0) pages.set('-always-memory', 0);
110
+ if (Prefs.alwaysMemory && pages.size === 0) pages.set('-always-memory', 0);
110
111
  if (optLevel === 0) pages.set('O0 precaution', 0);
111
112
 
112
113
  const usesMemory = pages.size > 0;
@@ -150,7 +151,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
150
151
 
151
152
  if (typeCount !== 0) localDecl.push(encodeLocal(typeCount, lastType));
152
153
 
153
- return encodeVector([ ...encodeVector(localDecl), ...x.wasm.flat().filter(x => x <= 0xff), Opcodes.end ]);
154
+ return encodeVector([ ...encodeVector(localDecl), ...x.wasm.flat().filter(x => x != null && x <= 0xff), Opcodes.end ]);
154
155
  }))
155
156
  );
156
157
 
@@ -169,7 +170,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
169
170
  unsignedLEB128(data.length)
170
171
  );
171
172
 
172
- if (process.argv.includes('-sections')) console.log({
173
+ if (Prefs.sections) console.log({
173
174
  typeSection: typeSection.map(x => x.toString(16)),
174
175
  importSection: importSection.map(x => x.toString(16)),
175
176
  funcSection: funcSection.map(x => x.toString(16)),
@@ -32,17 +32,17 @@ export const Opcodes = {
32
32
  throw: 0x08,
33
33
  rethrow: 0x09,
34
34
 
35
- return: 0x0F,
35
+ end: 0x0b,
36
+ br: 0x0c,
37
+ br_if: 0x0d,
38
+ br_table: 0x0e,
39
+ return: 0x0f,
36
40
 
37
41
  call: 0x10,
38
42
  call_indirect: 0x11,
39
43
  return_call: 0x12,
40
44
  return_call_indirect: 0x13,
41
45
 
42
- end: 0x0b,
43
- br: 0x0c,
44
- br_if: 0x0d,
45
- call: 0x10,
46
46
  drop: 0x1a,
47
47
 
48
48
  local_get: 0x20,
@@ -56,15 +56,26 @@ export const Opcodes = {
56
56
  i64_load: 0x29,
57
57
  f64_load: 0x2b,
58
58
 
59
+ i32_load8_s: 0x2c,
60
+ i32_load8_u: 0x2d,
59
61
  i32_load16_s: 0x2e,
60
62
  i32_load16_u: 0x2f,
61
63
 
62
- i32_store16: 0x3b,
64
+ i64_load8_s: 0x30,
65
+ i64_load8_u: 0x31,
66
+ i64_load16_s: 0x32,
67
+ i64_load16_u: 0x33,
63
68
 
64
69
  i32_store: 0x36,
65
70
  i64_store: 0x37,
66
71
  f64_store: 0x39,
67
72
 
73
+ i32_store8: 0x3a,
74
+ i32_store16: 0x3b,
75
+
76
+ i64_store8: 0x3c,
77
+ i64_store16: 0x3d,
78
+
68
79
  memory_grow: 0x40,
69
80
 
70
81
  i32_const: 0x41,
@@ -96,6 +107,8 @@ export const Opcodes = {
96
107
  i32_shl: 0x74,
97
108
  i32_shr_s: 0x75,
98
109
  i32_shr_u: 0x76,
110
+ i32_rotl: 0x77,
111
+ i32_rotr: 0x78,
99
112
 
100
113
  i64_eqz: 0x50,
101
114
  i64_eq: 0x51,
@@ -119,6 +132,7 @@ export const Opcodes = {
119
132
  i64_shr_s: 0x87,
120
133
  i64_shr_u: 0x88,
121
134
  i64_rotl: 0x89,
135
+ i64_rotr: 0x8a,
122
136
 
123
137
  f64_eq: 0x61,
124
138
  f64_ne: 0x62,