porffor 0.2.0-c7b7423 → 0.2.0-eaee2da
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 +34 -12
- package/compiler/2c.js +6 -1
- package/compiler/builtins.js +134 -6
- package/compiler/codeGen.js +362 -174
- package/compiler/decompile.js +3 -3
- package/compiler/encoding.js +2 -116
- package/compiler/index.js +1 -1
- package/compiler/opt.js +23 -2
- package/compiler/parse.js +11 -12
- package/compiler/prototype.js +171 -16
- package/compiler/sections.js +1 -1
- package/compiler/wasmSpec.js +6 -2
- package/compiler/wrap.js +98 -8
- package/package.json +1 -1
- package/runner/repl.js +2 -2
- package/tmp.c +0 -69
package/compiler/codeGen.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from "./wasmSpec.js";
|
2
|
-
import { ieee754_binary64, signedLEB128, unsignedLEB128 } from "./encoding.js";
|
2
|
+
import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector } 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";
|
@@ -55,7 +55,7 @@ const todo = msg => {
|
|
55
55
|
};
|
56
56
|
|
57
57
|
const isFuncType = type => type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
|
58
|
-
const generate = (scope, decl, global = false, name = undefined) => {
|
58
|
+
const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
59
59
|
switch (decl.type) {
|
60
60
|
case 'BinaryExpression':
|
61
61
|
return generateBinaryExp(scope, decl, global, name);
|
@@ -68,7 +68,12 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
68
68
|
|
69
69
|
case 'ArrowFunctionExpression':
|
70
70
|
case 'FunctionDeclaration':
|
71
|
-
generateFunc(scope, decl);
|
71
|
+
const func = generateFunc(scope, decl);
|
72
|
+
|
73
|
+
if (decl.type.endsWith('Expression')) {
|
74
|
+
return number(func.index);
|
75
|
+
}
|
76
|
+
|
72
77
|
return [];
|
73
78
|
|
74
79
|
case 'BlockStatement':
|
@@ -81,7 +86,7 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
81
86
|
return generateExp(scope, decl);
|
82
87
|
|
83
88
|
case 'CallExpression':
|
84
|
-
return generateCall(scope, decl, global, name);
|
89
|
+
return generateCall(scope, decl, global, name, valueUnused);
|
85
90
|
|
86
91
|
case 'NewExpression':
|
87
92
|
return generateNew(scope, decl, global, name);
|
@@ -214,6 +219,11 @@ const generate = (scope, decl, global = false, name = undefined) => {
|
|
214
219
|
}
|
215
220
|
|
216
221
|
default:
|
222
|
+
if (decl.type.startsWith('TS')) {
|
223
|
+
// ignore typescript nodes
|
224
|
+
return [];
|
225
|
+
}
|
226
|
+
|
217
227
|
return todo(`no generation for ${decl.type}!`);
|
218
228
|
}
|
219
229
|
};
|
@@ -360,12 +370,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
360
370
|
...right,
|
361
371
|
// note type
|
362
372
|
...rightType,
|
363
|
-
|
373
|
+
setLastType(scope),
|
364
374
|
[ Opcodes.else ],
|
365
375
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
366
376
|
// note type
|
367
377
|
...leftType,
|
368
|
-
|
378
|
+
setLastType(scope),
|
369
379
|
[ Opcodes.end ],
|
370
380
|
Opcodes.i32_from
|
371
381
|
];
|
@@ -379,12 +389,12 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
379
389
|
...right,
|
380
390
|
// note type
|
381
391
|
...rightType,
|
382
|
-
|
392
|
+
setLastType(scope),
|
383
393
|
[ Opcodes.else ],
|
384
394
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
385
395
|
// note type
|
386
396
|
...leftType,
|
387
|
-
|
397
|
+
setLastType(scope),
|
388
398
|
[ Opcodes.end ]
|
389
399
|
];
|
390
400
|
};
|
@@ -675,6 +685,15 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
675
685
|
[ Opcodes.i32_eqz ], */
|
676
686
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
677
687
|
],
|
688
|
+
[TYPES._bytestring]: [ // duplicate of string
|
689
|
+
[ Opcodes.local_get, tmp ],
|
690
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
691
|
+
|
692
|
+
// get length
|
693
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
694
|
+
|
695
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
696
|
+
],
|
678
697
|
default: def
|
679
698
|
}, intOut ? Valtype.i32 : valtypeBinary)
|
680
699
|
];
|
@@ -702,6 +721,17 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
702
721
|
[ Opcodes.i32_eqz ],
|
703
722
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
704
723
|
],
|
724
|
+
[TYPES._bytestring]: [ // duplicate of string
|
725
|
+
[ Opcodes.local_get, tmp ],
|
726
|
+
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
727
|
+
|
728
|
+
// get length
|
729
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
730
|
+
|
731
|
+
// if length == 0
|
732
|
+
[ Opcodes.i32_eqz ],
|
733
|
+
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
734
|
+
],
|
705
735
|
default: [
|
706
736
|
// if value == 0
|
707
737
|
[ Opcodes.local_get, tmp ],
|
@@ -852,7 +882,16 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
852
882
|
|
853
883
|
let tmpLeft, tmpRight;
|
854
884
|
// if equal op, check if strings for compareStrings
|
855
|
-
if (op === '===' || op === '==' || op === '!==' || op === '!=') {
|
885
|
+
if (op === '===' || op === '==' || op === '!==' || op === '!=') (() => {
|
886
|
+
const knownLeft = knownType(scope, leftType);
|
887
|
+
const knownRight = knownType(scope, rightType);
|
888
|
+
|
889
|
+
// todo: intelligent partial skip later
|
890
|
+
// if neither known are string, stop this madness
|
891
|
+
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
892
|
+
return;
|
893
|
+
}
|
894
|
+
|
856
895
|
tmpLeft = localTmp(scope, '__tmpop_left');
|
857
896
|
tmpRight = localTmp(scope, '__tmpop_right');
|
858
897
|
|
@@ -902,7 +941,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
902
941
|
// endOut.push(stringOnly([ Opcodes.end ]));
|
903
942
|
endOut.unshift(stringOnly([ Opcodes.end ]));
|
904
943
|
// }
|
905
|
-
}
|
944
|
+
})();
|
906
945
|
|
907
946
|
return finalise([
|
908
947
|
...left,
|
@@ -933,6 +972,18 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
933
972
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
934
973
|
}
|
935
974
|
|
975
|
+
if (typeof wasm === 'function') {
|
976
|
+
const scope = {
|
977
|
+
name,
|
978
|
+
params,
|
979
|
+
locals,
|
980
|
+
returns,
|
981
|
+
localInd: allLocals.length,
|
982
|
+
};
|
983
|
+
|
984
|
+
wasm = wasm(scope, { TYPES, typeSwitch, makeArray });
|
985
|
+
}
|
986
|
+
|
936
987
|
let baseGlobalIdx, i = 0;
|
937
988
|
for (const type of globalTypes) {
|
938
989
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -1013,7 +1064,8 @@ const TYPES = {
|
|
1013
1064
|
|
1014
1065
|
// these are not "typeof" types but tracked internally
|
1015
1066
|
_array: 0x10,
|
1016
|
-
_regexp: 0x11
|
1067
|
+
_regexp: 0x11,
|
1068
|
+
_bytestring: 0x12
|
1017
1069
|
};
|
1018
1070
|
|
1019
1071
|
const TYPE_NAMES = {
|
@@ -1027,7 +1079,8 @@ const TYPE_NAMES = {
|
|
1027
1079
|
[TYPES.bigint]: 'BigInt',
|
1028
1080
|
|
1029
1081
|
[TYPES._array]: 'Array',
|
1030
|
-
[TYPES._regexp]: 'RegExp'
|
1082
|
+
[TYPES._regexp]: 'RegExp',
|
1083
|
+
[TYPES._bytestring]: 'ByteString'
|
1031
1084
|
};
|
1032
1085
|
|
1033
1086
|
const getType = (scope, _name) => {
|
@@ -1051,11 +1104,13 @@ const setType = (scope, _name, type) => {
|
|
1051
1104
|
|
1052
1105
|
const out = typeof type === 'number' ? number(type, Valtype.i32) : type;
|
1053
1106
|
|
1107
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return [];
|
1054
1108
|
if (scope.locals[name]) return [
|
1055
1109
|
...out,
|
1056
1110
|
[ Opcodes.local_set, scope.locals[name + '#type'].idx ]
|
1057
1111
|
];
|
1058
1112
|
|
1113
|
+
if (typedInput && globals[name]?.metadata?.type != null) return [];
|
1059
1114
|
if (globals[name]) return [
|
1060
1115
|
...out,
|
1061
1116
|
[ Opcodes.global_set, globals[name + '#type'].idx ]
|
@@ -1064,11 +1119,22 @@ const setType = (scope, _name, type) => {
|
|
1064
1119
|
// throw new Error('could not find var');
|
1065
1120
|
};
|
1066
1121
|
|
1122
|
+
const getLastType = scope => {
|
1123
|
+
scope.gotLastType = true;
|
1124
|
+
return [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ];
|
1125
|
+
};
|
1126
|
+
|
1127
|
+
const setLastType = scope => {
|
1128
|
+
return [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ];
|
1129
|
+
};
|
1130
|
+
|
1067
1131
|
const getNodeType = (scope, node) => {
|
1068
1132
|
const inner = () => {
|
1069
1133
|
if (node.type === 'Literal') {
|
1070
1134
|
if (node.regex) return TYPES._regexp;
|
1071
1135
|
|
1136
|
+
if (typeof node.value === 'string' && byteStringable(node.value)) return TYPES._bytestring;
|
1137
|
+
|
1072
1138
|
return TYPES[typeof node.value];
|
1073
1139
|
}
|
1074
1140
|
|
@@ -1082,6 +1148,15 @@ const getNodeType = (scope, node) => {
|
|
1082
1148
|
|
1083
1149
|
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
|
1084
1150
|
const name = node.callee.name;
|
1151
|
+
if (!name) {
|
1152
|
+
// iife
|
1153
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1154
|
+
|
1155
|
+
// presume
|
1156
|
+
// todo: warn here?
|
1157
|
+
return TYPES.number;
|
1158
|
+
}
|
1159
|
+
|
1085
1160
|
const func = funcs.find(x => x.name === name);
|
1086
1161
|
|
1087
1162
|
if (func) {
|
@@ -1092,7 +1167,19 @@ const getNodeType = (scope, node) => {
|
|
1092
1167
|
if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1093
1168
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1094
1169
|
|
1095
|
-
|
1170
|
+
// check if this is a prototype function
|
1171
|
+
// if so and there is only one impl (eg charCodeAt)
|
1172
|
+
// use that return type as that is the only possibility
|
1173
|
+
// (if non-matching type it would error out)
|
1174
|
+
if (name.startsWith('__')) {
|
1175
|
+
const spl = name.slice(2).split('_');
|
1176
|
+
|
1177
|
+
const func = spl[spl.length - 1];
|
1178
|
+
const protoFuncs = Object.values(prototypeFuncs).filter(x => x[func] != null);
|
1179
|
+
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1180
|
+
}
|
1181
|
+
|
1182
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1096
1183
|
|
1097
1184
|
// presume
|
1098
1185
|
// todo: warn here?
|
@@ -1164,7 +1251,7 @@ const getNodeType = (scope, node) => {
|
|
1164
1251
|
if (node.operator === '!') return TYPES.boolean;
|
1165
1252
|
if (node.operator === 'void') return TYPES.undefined;
|
1166
1253
|
if (node.operator === 'delete') return TYPES.boolean;
|
1167
|
-
if (node.operator === 'typeof') return TYPES.string;
|
1254
|
+
if (node.operator === 'typeof') return process.argv.includes('-bytestring') ? TYPES._bytestring : TYPES.string;
|
1168
1255
|
|
1169
1256
|
return TYPES.number;
|
1170
1257
|
}
|
@@ -1177,7 +1264,7 @@ const getNodeType = (scope, node) => {
|
|
1177
1264
|
return TYPES.number;
|
1178
1265
|
}
|
1179
1266
|
|
1180
|
-
if (scope.locals['#last_type']) return [
|
1267
|
+
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1181
1268
|
|
1182
1269
|
// presume
|
1183
1270
|
// todo: warn here?
|
@@ -1190,6 +1277,23 @@ const getNodeType = (scope, node) => {
|
|
1190
1277
|
return ret;
|
1191
1278
|
};
|
1192
1279
|
|
1280
|
+
const toString = (scope, wasm, type) => {
|
1281
|
+
const tmp = localTmp(scope, '#tostring_tmp');
|
1282
|
+
return [
|
1283
|
+
...wasm,
|
1284
|
+
[ Opcodes.local_set, tmp ],
|
1285
|
+
|
1286
|
+
...typeSwitch(scope, type, {
|
1287
|
+
[TYPES.string]: [
|
1288
|
+
[ Opcodes.local_get, tmp ]
|
1289
|
+
],
|
1290
|
+
[TYPES.undefined]: [
|
1291
|
+
// [ Opcodes.]
|
1292
|
+
]
|
1293
|
+
})
|
1294
|
+
]
|
1295
|
+
};
|
1296
|
+
|
1193
1297
|
const generateLiteral = (scope, decl, global, name) => {
|
1194
1298
|
if (decl.value === null) return number(NULL);
|
1195
1299
|
|
@@ -1207,16 +1311,7 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1207
1311
|
return number(decl.value ? 1 : 0);
|
1208
1312
|
|
1209
1313
|
case 'string':
|
1210
|
-
|
1211
|
-
const rawElements = new Array(str.length);
|
1212
|
-
let j = 0;
|
1213
|
-
for (let i = 0; i < str.length; i++) {
|
1214
|
-
rawElements[i] = str.charCodeAt(i);
|
1215
|
-
}
|
1216
|
-
|
1217
|
-
return makeArray(scope, {
|
1218
|
-
rawElements
|
1219
|
-
}, global, name, false, 'i16')[0];
|
1314
|
+
return makeString(scope, decl.value, global, name);
|
1220
1315
|
|
1221
1316
|
default:
|
1222
1317
|
return todo(`cannot generate literal of type ${typeof decl.value}`);
|
@@ -1237,9 +1332,9 @@ const countLeftover = wasm => {
|
|
1237
1332
|
|
1238
1333
|
if (depth === 0)
|
1239
1334
|
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1240
|
-
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
|
1335
|
+
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
|
1241
1336
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1242
|
-
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16].includes(inst[0])) count -= 2;
|
1337
|
+
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1243
1338
|
else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
|
1244
1339
|
else if (inst[0] === Opcodes.return) count = 0;
|
1245
1340
|
else if (inst[0] === Opcodes.call) {
|
@@ -1265,7 +1360,7 @@ const disposeLeftover = wasm => {
|
|
1265
1360
|
const generateExp = (scope, decl) => {
|
1266
1361
|
const expression = decl.expression;
|
1267
1362
|
|
1268
|
-
const out = generate(scope, expression);
|
1363
|
+
const out = generate(scope, expression, undefined, undefined, true);
|
1269
1364
|
disposeLeftover(out);
|
1270
1365
|
|
1271
1366
|
return out;
|
@@ -1323,7 +1418,7 @@ const RTArrayUtil = {
|
|
1323
1418
|
]
|
1324
1419
|
};
|
1325
1420
|
|
1326
|
-
const generateCall = (scope, decl, _global, _name) => {
|
1421
|
+
const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
1327
1422
|
/* const callee = decl.callee;
|
1328
1423
|
const args = decl.arguments;
|
1329
1424
|
|
@@ -1356,13 +1451,13 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1356
1451
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1357
1452
|
out.push(
|
1358
1453
|
...getNodeType(scope, finalStatement),
|
1359
|
-
|
1454
|
+
setLastType(scope)
|
1360
1455
|
);
|
1361
1456
|
} else if (countLeftover(out) === 0) {
|
1362
1457
|
out.push(...number(UNDEFINED));
|
1363
1458
|
out.push(
|
1364
1459
|
...number(TYPES.undefined, Valtype.i32),
|
1365
|
-
|
1460
|
+
setLastType(scope)
|
1366
1461
|
);
|
1367
1462
|
}
|
1368
1463
|
|
@@ -1380,8 +1475,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1380
1475
|
if (name && name.startsWith('__')) {
|
1381
1476
|
const spl = name.slice(2).split('_');
|
1382
1477
|
|
1383
|
-
|
1384
|
-
protoName = func;
|
1478
|
+
protoName = spl[spl.length - 1];
|
1385
1479
|
|
1386
1480
|
target = { ...decl.callee };
|
1387
1481
|
target.name = spl.slice(0, -1).join('_');
|
@@ -1407,12 +1501,11 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1407
1501
|
Opcodes.i32_from_u,
|
1408
1502
|
|
1409
1503
|
...number(TYPES.boolean, Valtype.i32),
|
1410
|
-
|
1504
|
+
setLastType(scope)
|
1411
1505
|
];
|
1412
1506
|
}
|
1413
1507
|
|
1414
|
-
|
1415
|
-
protoName = func;
|
1508
|
+
protoName = decl.callee.property.name;
|
1416
1509
|
|
1417
1510
|
target = decl.callee.object;
|
1418
1511
|
}
|
@@ -1444,10 +1537,18 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1444
1537
|
// use local for cached i32 length as commonly used
|
1445
1538
|
const lengthLocal = localTmp(scope, '__proto_length_cache', Valtype.i32);
|
1446
1539
|
const pointerLocal = localTmp(scope, '__proto_pointer_cache', Valtype.i32);
|
1447
|
-
const getPointer = [ [ Opcodes.local_get, pointerLocal ] ];
|
1448
1540
|
|
1449
1541
|
// TODO: long-term, prototypes should be their individual separate funcs
|
1450
1542
|
|
1543
|
+
const rawPointer = [
|
1544
|
+
...generate(scope, target),
|
1545
|
+
Opcodes.i32_to_u
|
1546
|
+
];
|
1547
|
+
|
1548
|
+
const usePointerCache = !Object.values(protoCands).every(x => x.noPointerCache === true);
|
1549
|
+
const getPointer = usePointerCache ? [ [ Opcodes.local_get, pointerLocal ] ] : rawPointer;
|
1550
|
+
|
1551
|
+
let allOptUnused = true;
|
1451
1552
|
let lengthI32CacheUsed = false;
|
1452
1553
|
const protoBC = {};
|
1453
1554
|
for (const x in protoCands) {
|
@@ -1457,7 +1558,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1457
1558
|
...RTArrayUtil.getLength(getPointer),
|
1458
1559
|
|
1459
1560
|
...number(TYPES.number, Valtype.i32),
|
1460
|
-
|
1561
|
+
setLastType(scope)
|
1461
1562
|
];
|
1462
1563
|
continue;
|
1463
1564
|
}
|
@@ -1467,6 +1568,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1467
1568
|
const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
|
1468
1569
|
const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
|
1469
1570
|
|
1571
|
+
let optUnused = false;
|
1470
1572
|
const protoOut = protoFunc(getPointer, {
|
1471
1573
|
getCachedI32: () => {
|
1472
1574
|
lengthI32CacheUsed = true;
|
@@ -1481,23 +1583,30 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1481
1583
|
return makeArray(scope, {
|
1482
1584
|
rawElements: new Array(length)
|
1483
1585
|
}, _global, _name, true, itemType);
|
1586
|
+
}, () => {
|
1587
|
+
optUnused = true;
|
1588
|
+
return unusedValue;
|
1484
1589
|
});
|
1485
1590
|
|
1591
|
+
if (!optUnused) allOptUnused = false;
|
1592
|
+
|
1486
1593
|
protoBC[x] = [
|
1487
|
-
[ Opcodes.block, valtypeBinary ],
|
1594
|
+
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1488
1595
|
...protoOut,
|
1489
1596
|
|
1490
1597
|
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1491
|
-
|
1598
|
+
setLastType(scope),
|
1492
1599
|
[ Opcodes.end ]
|
1493
1600
|
];
|
1494
1601
|
}
|
1495
1602
|
|
1496
|
-
|
1497
|
-
...generate(scope, target),
|
1603
|
+
// todo: if some cands use optUnused and some don't, we will probably crash
|
1498
1604
|
|
1499
|
-
|
1500
|
-
|
1605
|
+
return [
|
1606
|
+
...(usePointerCache ? [
|
1607
|
+
...rawPointer,
|
1608
|
+
[ Opcodes.local_set, pointerLocal ],
|
1609
|
+
] : []),
|
1501
1610
|
|
1502
1611
|
...(!lengthI32CacheUsed ? [] : [
|
1503
1612
|
...RTArrayUtil.getLengthI32(getPointer),
|
@@ -1509,7 +1618,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1509
1618
|
|
1510
1619
|
// TODO: error better
|
1511
1620
|
default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
|
1512
|
-
}, valtypeBinary),
|
1621
|
+
}, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
|
1513
1622
|
];
|
1514
1623
|
}
|
1515
1624
|
}
|
@@ -1556,7 +1665,9 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1556
1665
|
const func = funcs.find(x => x.index === idx);
|
1557
1666
|
|
1558
1667
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1559
|
-
const
|
1668
|
+
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1669
|
+
const typedReturn = userFunc || builtinFuncs[name]?.typedReturn;
|
1670
|
+
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1560
1671
|
|
1561
1672
|
let args = decl.arguments;
|
1562
1673
|
if (func && args.length < paramCount) {
|
@@ -1574,12 +1685,12 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1574
1685
|
let out = [];
|
1575
1686
|
for (const arg of args) {
|
1576
1687
|
out = out.concat(generate(scope, arg));
|
1577
|
-
if (
|
1688
|
+
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1578
1689
|
}
|
1579
1690
|
|
1580
1691
|
out.push([ Opcodes.call, idx ]);
|
1581
1692
|
|
1582
|
-
if (!
|
1693
|
+
if (!typedReturn) {
|
1583
1694
|
// let type;
|
1584
1695
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1585
1696
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1589,7 +1700,7 @@ const generateCall = (scope, decl, _global, _name) => {
|
|
1589
1700
|
// ...number(type, Valtype.i32),
|
1590
1701
|
// [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1591
1702
|
// );
|
1592
|
-
} else out.push(
|
1703
|
+
} else out.push(setLastType(scope));
|
1593
1704
|
|
1594
1705
|
return out;
|
1595
1706
|
};
|
@@ -1614,9 +1725,118 @@ const unhackName = name => {
|
|
1614
1725
|
return name;
|
1615
1726
|
};
|
1616
1727
|
|
1728
|
+
const knownType = (scope, type) => {
|
1729
|
+
if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
|
1730
|
+
return type[0][1];
|
1731
|
+
}
|
1732
|
+
|
1733
|
+
if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
|
1734
|
+
const idx = type[0][1];
|
1735
|
+
|
1736
|
+
// type idx = var idx + 1
|
1737
|
+
const v = Object.values(scope.locals).find(x => x.idx === idx - 1);
|
1738
|
+
if (v.metadata?.type != null) return v.metadata.type;
|
1739
|
+
}
|
1740
|
+
|
1741
|
+
return null;
|
1742
|
+
};
|
1743
|
+
|
1744
|
+
const brTable = (input, bc, returns) => {
|
1745
|
+
const out = [];
|
1746
|
+
const keys = Object.keys(bc);
|
1747
|
+
const count = keys.length;
|
1748
|
+
|
1749
|
+
if (count === 1) {
|
1750
|
+
// return [
|
1751
|
+
// ...input,
|
1752
|
+
// ...bc[keys[0]]
|
1753
|
+
// ];
|
1754
|
+
return bc[keys[0]];
|
1755
|
+
}
|
1756
|
+
|
1757
|
+
if (count === 2) {
|
1758
|
+
// just use if else
|
1759
|
+
const other = keys.find(x => x !== 'default');
|
1760
|
+
return [
|
1761
|
+
...input,
|
1762
|
+
...number(other, Valtype.i32),
|
1763
|
+
[ Opcodes.i32_eq ],
|
1764
|
+
[ Opcodes.if, returns ],
|
1765
|
+
...bc[other],
|
1766
|
+
[ Opcodes.else ],
|
1767
|
+
...bc.default,
|
1768
|
+
[ Opcodes.end ]
|
1769
|
+
];
|
1770
|
+
}
|
1771
|
+
|
1772
|
+
for (let i = 0; i < count; i++) {
|
1773
|
+
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
1774
|
+
else out.push([ Opcodes.block, Blocktype.void ]);
|
1775
|
+
}
|
1776
|
+
|
1777
|
+
const nums = keys.filter(x => +x);
|
1778
|
+
const offset = Math.min(...nums);
|
1779
|
+
const max = Math.max(...nums);
|
1780
|
+
|
1781
|
+
const table = [];
|
1782
|
+
let br = 1;
|
1783
|
+
|
1784
|
+
for (let i = offset; i <= max; i++) {
|
1785
|
+
// if branch for this num, go to that block
|
1786
|
+
if (bc[i]) {
|
1787
|
+
table.push(br);
|
1788
|
+
br++;
|
1789
|
+
continue;
|
1790
|
+
}
|
1791
|
+
|
1792
|
+
// else default
|
1793
|
+
table.push(0);
|
1794
|
+
}
|
1795
|
+
|
1796
|
+
out.push(
|
1797
|
+
[ Opcodes.block, Blocktype.void ],
|
1798
|
+
...input,
|
1799
|
+
...(offset > 0 ? [
|
1800
|
+
...number(offset, Valtype.i32),
|
1801
|
+
[ Opcodes.i32_sub ]
|
1802
|
+
] : []),
|
1803
|
+
[ Opcodes.br_table, ...encodeVector(table), 0 ]
|
1804
|
+
);
|
1805
|
+
|
1806
|
+
// if you can guess why we sort the wrong way and then reverse
|
1807
|
+
// (instead of just sorting the correct way)
|
1808
|
+
// dm me and if you are correct and the first person
|
1809
|
+
// I will somehow shout you out or something
|
1810
|
+
const orderedBc = keys.sort((a, b) => b - a).reverse();
|
1811
|
+
|
1812
|
+
br = count - 1;
|
1813
|
+
for (const x of orderedBc) {
|
1814
|
+
out.push(
|
1815
|
+
[ Opcodes.end ],
|
1816
|
+
...bc[x],
|
1817
|
+
...(br === 0 ? [] : [ [ Opcodes.br, br ] ])
|
1818
|
+
);
|
1819
|
+
br--;
|
1820
|
+
}
|
1821
|
+
|
1822
|
+
return [
|
1823
|
+
...out,
|
1824
|
+
[ Opcodes.end, 'br table end' ]
|
1825
|
+
];
|
1826
|
+
};
|
1827
|
+
|
1617
1828
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1618
|
-
|
1829
|
+
if (!process.argv.includes('-bytestring')) delete bc[TYPES._bytestring];
|
1830
|
+
|
1831
|
+
const known = knownType(scope, type);
|
1832
|
+
if (known != null) {
|
1833
|
+
return bc[known] ?? bc.default;
|
1834
|
+
}
|
1835
|
+
|
1836
|
+
if (process.argv.includes('-typeswitch-use-brtable'))
|
1837
|
+
return brTable(type, bc, returns);
|
1619
1838
|
|
1839
|
+
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
1620
1840
|
const out = [
|
1621
1841
|
...type,
|
1622
1842
|
[ Opcodes.local_set, tmp ],
|
@@ -1668,6 +1888,49 @@ const allocVar = (scope, name, global = false) => {
|
|
1668
1888
|
return idx;
|
1669
1889
|
};
|
1670
1890
|
|
1891
|
+
const addVarMetadata = (scope, name, global = false, metadata = {}) => {
|
1892
|
+
const target = global ? globals : scope.locals;
|
1893
|
+
|
1894
|
+
target[name].metadata ??= {};
|
1895
|
+
for (const x in metadata) {
|
1896
|
+
if (metadata[x] != null) target[name].metadata[x] = metadata[x];
|
1897
|
+
}
|
1898
|
+
};
|
1899
|
+
|
1900
|
+
const typeAnnoToPorfType = x => {
|
1901
|
+
if (TYPES[x]) return TYPES[x];
|
1902
|
+
if (TYPES['_' + x]) return TYPES['_' + x];
|
1903
|
+
|
1904
|
+
switch (x) {
|
1905
|
+
case 'i32':
|
1906
|
+
return TYPES.number;
|
1907
|
+
}
|
1908
|
+
|
1909
|
+
return null;
|
1910
|
+
};
|
1911
|
+
|
1912
|
+
const extractTypeAnnotation = decl => {
|
1913
|
+
let a = decl;
|
1914
|
+
while (a.typeAnnotation) a = a.typeAnnotation;
|
1915
|
+
|
1916
|
+
let type, elementType;
|
1917
|
+
if (a.typeName) {
|
1918
|
+
type = a.typeName.name;
|
1919
|
+
} else if (a.type.endsWith('Keyword')) {
|
1920
|
+
type = a.type.slice(2, -7).toLowerCase();
|
1921
|
+
} else if (a.type === 'TSArrayType') {
|
1922
|
+
type = 'array';
|
1923
|
+
elementType = extractTypeAnnotation(a.elementType).type;
|
1924
|
+
}
|
1925
|
+
|
1926
|
+
const typeName = type;
|
1927
|
+
type = typeAnnoToPorfType(type);
|
1928
|
+
|
1929
|
+
// if (decl.name) console.log(decl.name, { type, elementType });
|
1930
|
+
|
1931
|
+
return { type, typeName, elementType };
|
1932
|
+
};
|
1933
|
+
|
1671
1934
|
const generateVar = (scope, decl) => {
|
1672
1935
|
let out = [];
|
1673
1936
|
|
@@ -1695,6 +1958,11 @@ const generateVar = (scope, decl) => {
|
|
1695
1958
|
}
|
1696
1959
|
|
1697
1960
|
let idx = allocVar(scope, name, global);
|
1961
|
+
|
1962
|
+
if (typedInput && x.id.typeAnnotation) {
|
1963
|
+
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
1964
|
+
}
|
1965
|
+
|
1698
1966
|
if (x.init) {
|
1699
1967
|
out = out.concat(generate(scope, x.init, global, name));
|
1700
1968
|
|
@@ -1858,7 +2126,7 @@ const generateAssign = (scope, decl) => {
|
|
1858
2126
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
1859
2127
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1860
2128
|
|
1861
|
-
|
2129
|
+
getLastType(scope),
|
1862
2130
|
// hack: type is idx+1
|
1863
2131
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
1864
2132
|
];
|
@@ -1950,6 +2218,8 @@ const generateUnary = (scope, decl) => {
|
|
1950
2218
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
1951
2219
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
1952
2220
|
|
2221
|
+
[TYPES._bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2222
|
+
|
1953
2223
|
// object and internal types
|
1954
2224
|
default: makeString(scope, 'object', false, '#typeof_result'),
|
1955
2225
|
});
|
@@ -2025,7 +2295,7 @@ const generateConditional = (scope, decl) => {
|
|
2025
2295
|
// note type
|
2026
2296
|
out.push(
|
2027
2297
|
...getNodeType(scope, decl.consequent),
|
2028
|
-
|
2298
|
+
setLastType(scope)
|
2029
2299
|
);
|
2030
2300
|
|
2031
2301
|
out.push([ Opcodes.else ]);
|
@@ -2034,7 +2304,7 @@ const generateConditional = (scope, decl) => {
|
|
2034
2304
|
// note type
|
2035
2305
|
out.push(
|
2036
2306
|
...getNodeType(scope, decl.alternate),
|
2037
|
-
|
2307
|
+
setLastType(scope)
|
2038
2308
|
);
|
2039
2309
|
|
2040
2310
|
out.push([ Opcodes.end ]);
|
@@ -2138,6 +2408,7 @@ const generateForOf = (scope, decl) => {
|
|
2138
2408
|
}
|
2139
2409
|
|
2140
2410
|
// set type for local
|
2411
|
+
// todo: optimize away counter and use end pointer
|
2141
2412
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2142
2413
|
[TYPES._array]: [
|
2143
2414
|
...setType(scope, leftName, TYPES.number),
|
@@ -2356,7 +2627,8 @@ const StoreOps = {
|
|
2356
2627
|
f64: Opcodes.f64_store,
|
2357
2628
|
|
2358
2629
|
// expects i32 input!
|
2359
|
-
|
2630
|
+
i8: Opcodes.i32_store8,
|
2631
|
+
i16: Opcodes.i32_store16,
|
2360
2632
|
};
|
2361
2633
|
|
2362
2634
|
let data = [];
|
@@ -2375,6 +2647,15 @@ const compileBytes = (val, itemType, signed = true) => {
|
|
2375
2647
|
}
|
2376
2648
|
};
|
2377
2649
|
|
2650
|
+
const getAllocType = itemType => {
|
2651
|
+
switch (itemType) {
|
2652
|
+
case 'i8': return 'bytestring';
|
2653
|
+
case 'i16': return 'string';
|
2654
|
+
|
2655
|
+
default: return 'array';
|
2656
|
+
}
|
2657
|
+
};
|
2658
|
+
|
2378
2659
|
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
2379
2660
|
const out = [];
|
2380
2661
|
|
@@ -2384,7 +2665,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2384
2665
|
|
2385
2666
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2386
2667
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2387
|
-
arrays.set(name, allocPage(`${itemType
|
2668
|
+
arrays.set(name, allocPage(`${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2388
2669
|
}
|
2389
2670
|
|
2390
2671
|
const pointer = arrays.get(name);
|
@@ -2430,7 +2711,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2430
2711
|
out.push(
|
2431
2712
|
...number(0, Valtype.i32),
|
2432
2713
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
2433
|
-
[ storeOp, Math.log2(ValtypeSize[itemType]) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2714
|
+
[ storeOp, (Math.log2(ValtypeSize[itemType]) || 1) - 1, ...unsignedLEB128(pointer + ValtypeSize.i32 + i * ValtypeSize[itemType]) ]
|
2434
2715
|
);
|
2435
2716
|
}
|
2436
2717
|
|
@@ -2440,15 +2721,29 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2440
2721
|
return [ out, pointer ];
|
2441
2722
|
};
|
2442
2723
|
|
2724
|
+
const byteStringable = str => {
|
2725
|
+
if (!process.argv.includes('-bytestring')) return false;
|
2726
|
+
|
2727
|
+
for (let i = 0; i < str.length; i++) {
|
2728
|
+
if (str.charCodeAt(i) > 0xFF) return false;
|
2729
|
+
}
|
2730
|
+
|
2731
|
+
return true;
|
2732
|
+
};
|
2733
|
+
|
2443
2734
|
const makeString = (scope, str, global = false, name = '$undeclared') => {
|
2444
2735
|
const rawElements = new Array(str.length);
|
2736
|
+
let byteStringable = process.argv.includes('-bytestring');
|
2445
2737
|
for (let i = 0; i < str.length; i++) {
|
2446
|
-
|
2738
|
+
const c = str.charCodeAt(i);
|
2739
|
+
rawElements[i] = c;
|
2740
|
+
|
2741
|
+
if (byteStringable && c > 0xFF) byteStringable = false;
|
2447
2742
|
}
|
2448
2743
|
|
2449
2744
|
return makeArray(scope, {
|
2450
2745
|
rawElements
|
2451
|
-
}, global, name, false, 'i16')[0];
|
2746
|
+
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
2452
2747
|
};
|
2453
2748
|
|
2454
2749
|
let arrays = new Map();
|
@@ -2478,7 +2773,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2478
2773
|
|
2479
2774
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2480
2775
|
// hack: this is naughty and will break things!
|
2481
|
-
let newOut = number(0,
|
2776
|
+
let newOut = number(0, valtypeBinary), newPointer = -1;
|
2482
2777
|
if (pages.hasString) {
|
2483
2778
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2484
2779
|
rawElements: new Array(1)
|
@@ -2505,7 +2800,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2505
2800
|
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
2506
2801
|
|
2507
2802
|
...number(TYPES.number, Valtype.i32),
|
2508
|
-
|
2803
|
+
setLastType(scope)
|
2509
2804
|
],
|
2510
2805
|
|
2511
2806
|
[TYPES.string]: [
|
@@ -2537,7 +2832,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
2537
2832
|
...number(newPointer),
|
2538
2833
|
|
2539
2834
|
...number(TYPES.string, Valtype.i32),
|
2540
|
-
|
2835
|
+
setLastType(scope)
|
2541
2836
|
],
|
2542
2837
|
|
2543
2838
|
default: [ [ Opcodes.unreachable ] ]
|
@@ -2586,7 +2881,7 @@ const generateFunc = (scope, decl) => {
|
|
2586
2881
|
if (decl.generator) return todo('generator functions are not supported');
|
2587
2882
|
|
2588
2883
|
const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
|
2589
|
-
const params = decl.params
|
2884
|
+
const params = decl.params ?? [];
|
2590
2885
|
|
2591
2886
|
// const innerScope = { ...scope };
|
2592
2887
|
// TODO: share scope/locals between !!!
|
@@ -2600,7 +2895,11 @@ const generateFunc = (scope, decl) => {
|
|
2600
2895
|
};
|
2601
2896
|
|
2602
2897
|
for (let i = 0; i < params.length; i++) {
|
2603
|
-
allocVar(innerScope, params[i], false);
|
2898
|
+
allocVar(innerScope, params[i].name, false);
|
2899
|
+
|
2900
|
+
if (typedInput && params[i].typeAnnotation) {
|
2901
|
+
addVarMetadata(innerScope, params[i].name, false, extractTypeAnnotation(params[i]));
|
2902
|
+
}
|
2604
2903
|
}
|
2605
2904
|
|
2606
2905
|
let body = objectHack(decl.body);
|
@@ -2639,117 +2938,6 @@ const generateFunc = (scope, decl) => {
|
|
2639
2938
|
);
|
2640
2939
|
}
|
2641
2940
|
|
2642
|
-
// change v128 params into many <type> (i32x4 -> i32/etc) instead as unsupported param valtype
|
2643
|
-
let offset = 0, vecParams = 0;
|
2644
|
-
for (let i = 0; i < params.length; i++) {
|
2645
|
-
const name = params[i];
|
2646
|
-
const local = func.locals[name];
|
2647
|
-
if (local.type === Valtype.v128) {
|
2648
|
-
vecParams++;
|
2649
|
-
|
2650
|
-
/* wasm.unshift( // add v128 load for param
|
2651
|
-
[ Opcodes.i32_const, 0 ],
|
2652
|
-
[ ...Opcodes.v128_load, 0, i * 16 ],
|
2653
|
-
[ Opcodes.local_set, local.idx ]
|
2654
|
-
); */
|
2655
|
-
|
2656
|
-
// using params and replace_lane is noticably faster than just loading from memory (above) somehow
|
2657
|
-
|
2658
|
-
// extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
|
2659
|
-
const { vecType } = local;
|
2660
|
-
let [ type, lanes ] = vecType.split('x');
|
2661
|
-
if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
|
2662
|
-
|
2663
|
-
lanes = parseInt(lanes);
|
2664
|
-
type = Valtype[type];
|
2665
|
-
|
2666
|
-
const name = params[i]; // get original param name
|
2667
|
-
|
2668
|
-
func.params.splice(offset, 1, ...new Array(lanes).fill(type)); // add new params of {type}, {lanes} times
|
2669
|
-
|
2670
|
-
// update index of original local
|
2671
|
-
// delete func.locals[name];
|
2672
|
-
|
2673
|
-
// add new locals for params
|
2674
|
-
for (let j = 0; j < lanes; j++) {
|
2675
|
-
func.locals[name + j] = { idx: offset + j, type, vecParamAutogen: true };
|
2676
|
-
}
|
2677
|
-
|
2678
|
-
// prepend wasm to generate expected v128 locals
|
2679
|
-
wasm.splice(i * 2 + offset * 2, 0,
|
2680
|
-
...i32x4(0, 0, 0, 0),
|
2681
|
-
...new Array(lanes).fill(0).flatMap((_, j) => [
|
2682
|
-
[ Opcodes.local_get, offset + j ],
|
2683
|
-
[ ...Opcodes[vecType + '_replace_lane'], j ]
|
2684
|
-
]),
|
2685
|
-
[ Opcodes.local_set, i ]
|
2686
|
-
);
|
2687
|
-
|
2688
|
-
offset += lanes;
|
2689
|
-
|
2690
|
-
// note: wrapping is disabled for now due to perf/dx concerns (so this will never run)
|
2691
|
-
/* if (!func.name.startsWith('#')) func.name = '##' + func.name;
|
2692
|
-
|
2693
|
-
// add vec type index to hash name prefix for wrapper to know how to wrap
|
2694
|
-
const vecTypeIdx = [ 'i8x16', 'i16x8', 'i32x4', 'i64x2', 'f32x4', 'f64x2' ].indexOf(local.vecType);
|
2695
|
-
const secondHash = func.name.slice(1).indexOf('#');
|
2696
|
-
func.name = '#' + func.name.slice(1, secondHash) + vecTypeIdx + func.name.slice(secondHash); */
|
2697
|
-
}
|
2698
|
-
}
|
2699
|
-
|
2700
|
-
if (offset !== 0) {
|
2701
|
-
// bump local indexes for all other locals after
|
2702
|
-
for (const x in func.locals) {
|
2703
|
-
const local = func.locals[x];
|
2704
|
-
if (!local.vecParamAutogen) local.idx += offset;
|
2705
|
-
}
|
2706
|
-
|
2707
|
-
// bump local indexes in wasm local.get/set
|
2708
|
-
for (let j = 0; j < wasm.length; j++) {
|
2709
|
-
const inst = wasm[j];
|
2710
|
-
if (j < offset * 2 + vecParams * 2) {
|
2711
|
-
if (inst[0] === Opcodes.local_set) inst[1] += offset;
|
2712
|
-
continue;
|
2713
|
-
}
|
2714
|
-
|
2715
|
-
if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set) inst[1] += offset;
|
2716
|
-
}
|
2717
|
-
}
|
2718
|
-
|
2719
|
-
// change v128 return into many <type> instead as unsupported return valtype
|
2720
|
-
const lastReturnLocal = wasm.length > 2 && wasm[wasm.length - 1][0] === Opcodes.return && Object.values(func.locals).find(x => x.idx === wasm[wasm.length - 2][1]);
|
2721
|
-
if (lastReturnLocal && lastReturnLocal.type === Valtype.v128) {
|
2722
|
-
const name = Object.keys(func.locals)[Object.values(func.locals).indexOf(lastReturnLocal)];
|
2723
|
-
// extract valtype and lane count from vec type (i32x4 = i32 4, i8x16 = i8 16, etc)
|
2724
|
-
const { vecType } = lastReturnLocal;
|
2725
|
-
let [ type, lanes ] = vecType.split('x');
|
2726
|
-
if (!type || !lanes) throw new Error('bad metadata from vec params'); // sanity check
|
2727
|
-
|
2728
|
-
lanes = parseInt(lanes);
|
2729
|
-
type = Valtype[type];
|
2730
|
-
|
2731
|
-
const vecIdx = lastReturnLocal.idx;
|
2732
|
-
|
2733
|
-
const lastIdx = Math.max(0, ...Object.values(func.locals).map(x => x.idx));
|
2734
|
-
const tmpIdx = [];
|
2735
|
-
for (let i = 0; i < lanes; i++) {
|
2736
|
-
const idx = lastIdx + i + 1;
|
2737
|
-
tmpIdx.push(idx);
|
2738
|
-
func.locals[name + i] = { idx, type, vecReturnAutogen: true };
|
2739
|
-
}
|
2740
|
-
|
2741
|
-
wasm.splice(wasm.length - 1, 1,
|
2742
|
-
...new Array(lanes).fill(0).flatMap((_, i) => [
|
2743
|
-
i === 0 ? null : [ Opcodes.local_get, vecIdx ],
|
2744
|
-
[ ...Opcodes[vecType + '_extract_lane'], i ],
|
2745
|
-
[ Opcodes.local_set, tmpIdx[i] ],
|
2746
|
-
].filter(x => x !== null)),
|
2747
|
-
...new Array(lanes).fill(0).map((_, i) => [ Opcodes.local_get, tmpIdx[i]])
|
2748
|
-
);
|
2749
|
-
|
2750
|
-
func.returns = new Array(lanes).fill(type);
|
2751
|
-
}
|
2752
|
-
|
2753
2941
|
func.wasm = wasm;
|
2754
2942
|
|
2755
2943
|
funcs.push(func);
|