porffor 0.2.0-623cdf0 → 0.2.0-6aff0fa
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 +69 -44
- package/asur/README.md +2 -0
- package/asur/index.js +978 -0
- package/compiler/2c.js +11 -11
- package/compiler/builtins/base64.ts +153 -0
- package/compiler/builtins/porffor.d.ts +23 -0
- package/compiler/builtins.js +65 -192
- package/compiler/codeGen.js +206 -110
- package/compiler/generated_builtins.js +15 -0
- package/compiler/index.js +9 -20
- package/compiler/log.js +4 -1
- package/compiler/opt.js +39 -26
- package/compiler/parse.js +4 -2
- package/compiler/precompile.js +129 -0
- package/compiler/prefs.js +26 -0
- package/compiler/prototype.js +11 -10
- package/compiler/sections.js +7 -6
- package/compiler/wasmSpec.js +14 -7
- package/compiler/wrap.js +12 -4
- package/package.json +1 -1
- package/porf +2 -0
- package/rhemyn/compile.js +2 -1
- package/runner/index.js +12 -2
- package/runner/profiler.js +83 -0
- package/compiler/builtins/base64.js +0 -92
- package/filesize.cmd +0 -2
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
package/compiler/codeGen.js
CHANGED
@@ -7,6 +7,7 @@ import { number, i32x4, enforceOneByte, enforceTwoBytes, enforceFourBytes, enfor
|
|
7
7
|
import { log } from "./log.js";
|
8
8
|
import parse from "./parse.js";
|
9
9
|
import * as Rhemyn from "../rhemyn/compile.js";
|
10
|
+
import Prefs from './prefs.js';
|
10
11
|
|
11
12
|
let globals = {};
|
12
13
|
let globalInd = 0;
|
@@ -160,7 +161,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
160
161
|
|
161
162
|
case 'TaggedTemplateExpression': {
|
162
163
|
const funcs = {
|
163
|
-
|
164
|
+
__Porffor_wasm: str => {
|
164
165
|
let out = [];
|
165
166
|
|
166
167
|
for (const line of str.split('\n')) {
|
@@ -168,8 +169,8 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
168
169
|
if (asm[0] === '') continue; // blank
|
169
170
|
|
170
171
|
if (asm[0] === 'local') {
|
171
|
-
const [ name,
|
172
|
-
scope.locals[name] = { idx:
|
172
|
+
const [ name, type ] = asm.slice(1);
|
173
|
+
scope.locals[name] = { idx: scope.localInd++, type: Valtype[type] };
|
173
174
|
continue;
|
174
175
|
}
|
175
176
|
|
@@ -179,7 +180,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
179
180
|
}
|
180
181
|
|
181
182
|
if (asm[0] === 'memory') {
|
182
|
-
allocPage('asm instrinsic');
|
183
|
+
allocPage(scope, 'asm instrinsic');
|
183
184
|
// todo: add to store/load offset insts
|
184
185
|
continue;
|
185
186
|
}
|
@@ -188,7 +189,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
188
189
|
if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
|
189
190
|
|
190
191
|
if (!Array.isArray(inst)) inst = [ inst ];
|
191
|
-
const immediates = asm.slice(1).map(x =>
|
192
|
+
const immediates = asm.slice(1).map(x => {
|
193
|
+
const int = parseInt(x);
|
194
|
+
if (Number.isNaN(int)) return scope.locals[x]?.idx;
|
195
|
+
return int;
|
196
|
+
});
|
192
197
|
|
193
198
|
out.push([ ...inst, ...immediates ]);
|
194
199
|
}
|
@@ -214,13 +219,22 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
214
219
|
// hack for inline asm
|
215
220
|
if (!funcs[name]) return todo('tagged template expressions not implemented');
|
216
221
|
|
217
|
-
const
|
222
|
+
const { quasis, expressions } = decl.quasi;
|
223
|
+
let str = quasis[0].value.raw;
|
224
|
+
|
225
|
+
for (let i = 0; i < expressions.length; i++) {
|
226
|
+
const e = expressions[i];
|
227
|
+
str += lookupName(scope, e.name)[0].idx;
|
228
|
+
str += quasis[i + 1].value.raw;
|
229
|
+
}
|
230
|
+
|
218
231
|
return funcs[name](str);
|
219
232
|
}
|
220
233
|
|
221
234
|
default:
|
222
|
-
|
223
|
-
|
235
|
+
// ignore typescript nodes
|
236
|
+
if (decl.type.startsWith('TS') ||
|
237
|
+
decl.type === 'ImportDeclaration' && decl.importKind === 'type') {
|
224
238
|
return [];
|
225
239
|
}
|
226
240
|
|
@@ -276,7 +290,10 @@ const generateIdent = (scope, decl) => {
|
|
276
290
|
|
277
291
|
if (Object.hasOwn(builtinVars, name)) {
|
278
292
|
if (builtinVars[name].floatOnly && valtype[0] === 'i') throw new Error(`Cannot use ${unhackName(name)} with integer valtype`);
|
279
|
-
|
293
|
+
|
294
|
+
let wasm = builtinVars[name];
|
295
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name });
|
296
|
+
return wasm;
|
280
297
|
}
|
281
298
|
|
282
299
|
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
|
@@ -409,9 +426,6 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
409
426
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
410
427
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
411
428
|
|
412
|
-
const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
|
413
|
-
if (aotWFA) addVarMeta(name, { wellFormed: undefined });
|
414
|
-
|
415
429
|
if (assign) {
|
416
430
|
const pointer = arrays.get(name ?? '$undeclared');
|
417
431
|
|
@@ -649,11 +663,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
649
663
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
650
664
|
];
|
651
665
|
|
652
|
-
const
|
666
|
+
const useTmp = knownType(scope, type) == null;
|
667
|
+
const tmp = useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
653
668
|
|
654
669
|
const def = [
|
655
670
|
// if value != 0
|
656
|
-
[ Opcodes.local_get, tmp ],
|
671
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
657
672
|
|
658
673
|
// ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
659
674
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
@@ -665,7 +680,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
665
680
|
|
666
681
|
return [
|
667
682
|
...wasm,
|
668
|
-
[ Opcodes.local_set, tmp ],
|
683
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
669
684
|
|
670
685
|
...typeSwitch(scope, type, {
|
671
686
|
// [TYPES.number]: def,
|
@@ -674,7 +689,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
674
689
|
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
675
690
|
],
|
676
691
|
[TYPES.string]: [
|
677
|
-
[ Opcodes.local_get, tmp ],
|
692
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
678
693
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
679
694
|
|
680
695
|
// get length
|
@@ -686,7 +701,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
686
701
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
687
702
|
],
|
688
703
|
[TYPES._bytestring]: [ // duplicate of string
|
689
|
-
|
704
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
690
705
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
691
706
|
|
692
707
|
// get length
|
@@ -700,10 +715,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
700
715
|
};
|
701
716
|
|
702
717
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
703
|
-
const
|
718
|
+
const useTmp = knownType(scope, type) == null;
|
719
|
+
const tmp = useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
720
|
+
|
704
721
|
return [
|
705
722
|
...wasm,
|
706
|
-
[ Opcodes.local_set, tmp ],
|
723
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
707
724
|
|
708
725
|
...typeSwitch(scope, type, {
|
709
726
|
[TYPES._array]: [
|
@@ -711,7 +728,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
711
728
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
712
729
|
],
|
713
730
|
[TYPES.string]: [
|
714
|
-
[ Opcodes.local_get, tmp ],
|
731
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
715
732
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
716
733
|
|
717
734
|
// get length
|
@@ -722,7 +739,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
722
739
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
723
740
|
],
|
724
741
|
[TYPES._bytestring]: [ // duplicate of string
|
725
|
-
[ Opcodes.local_get, tmp ],
|
742
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
726
743
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
727
744
|
|
728
745
|
// get length
|
@@ -734,7 +751,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
734
751
|
],
|
735
752
|
default: [
|
736
753
|
// if value == 0
|
737
|
-
[ Opcodes.local_get, tmp ],
|
754
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
738
755
|
|
739
756
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
740
757
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -744,10 +761,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
744
761
|
};
|
745
762
|
|
746
763
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
747
|
-
const
|
764
|
+
const useTmp = knownType(scope, type) == null;
|
765
|
+
const tmp = useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
766
|
+
|
748
767
|
return [
|
749
768
|
...wasm,
|
750
|
-
[ Opcodes.local_set, tmp ],
|
769
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
751
770
|
|
752
771
|
...typeSwitch(scope, type, {
|
753
772
|
[TYPES.undefined]: [
|
@@ -756,7 +775,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
756
775
|
],
|
757
776
|
[TYPES.object]: [
|
758
777
|
// object, null if == 0
|
759
|
-
[ Opcodes.local_get, tmp ],
|
778
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
760
779
|
|
761
780
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
762
781
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -785,11 +804,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
785
804
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
786
805
|
}
|
787
806
|
|
807
|
+
const knownLeft = knownType(scope, leftType);
|
808
|
+
const knownRight = knownType(scope, rightType);
|
809
|
+
|
788
810
|
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
789
811
|
const strictOp = op === '===' || op === '!==';
|
790
812
|
|
791
813
|
const startOut = [], endOut = [];
|
792
|
-
const
|
814
|
+
const finalize = out => startOut.concat(out, endOut);
|
793
815
|
|
794
816
|
// if strict (in)equal check types match
|
795
817
|
if (strictOp) {
|
@@ -834,31 +856,32 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
834
856
|
// todo: if equality op and an operand is undefined, return false
|
835
857
|
// todo: niche null hell with 0
|
836
858
|
|
837
|
-
//
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
859
|
+
// todo: this should be dynamic but for now only static
|
860
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
861
|
+
if (op === '+') {
|
862
|
+
// string concat (a + b)
|
863
|
+
return concatStrings(scope, left, right, _global, _name, assign);
|
864
|
+
}
|
865
|
+
|
866
|
+
// not an equality op, NaN
|
867
|
+
if (!eqOp) return number(NaN);
|
868
|
+
|
869
|
+
// else leave bool ops
|
870
|
+
// todo: convert string to number if string and number/bool
|
871
|
+
// todo: string (>|>=|<|<=) string
|
872
|
+
|
873
|
+
// string comparison
|
874
|
+
if (op === '===' || op === '==') {
|
875
|
+
return compareStrings(scope, left, right);
|
876
|
+
}
|
877
|
+
|
878
|
+
if (op === '!==' || op === '!=') {
|
879
|
+
return [
|
880
|
+
...compareStrings(scope, left, right),
|
881
|
+
[ Opcodes.i32_eqz ]
|
882
|
+
];
|
883
|
+
}
|
884
|
+
}
|
862
885
|
|
863
886
|
let ops = operatorOpcode[valtype][op];
|
864
887
|
|
@@ -868,7 +891,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
868
891
|
includeBuiltin(scope, builtinName);
|
869
892
|
const idx = funcIndex[builtinName];
|
870
893
|
|
871
|
-
return
|
894
|
+
return finalize([
|
872
895
|
...left,
|
873
896
|
...right,
|
874
897
|
[ Opcodes.call, idx ]
|
@@ -883,9 +906,6 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
883
906
|
let tmpLeft, tmpRight;
|
884
907
|
// if equal op, check if strings for compareStrings
|
885
908
|
if (op === '===' || op === '==' || op === '!==' || op === '!=') (() => {
|
886
|
-
const knownLeft = knownType(scope, leftType);
|
887
|
-
const knownRight = knownType(scope, rightType);
|
888
|
-
|
889
909
|
// todo: intelligent partial skip later
|
890
910
|
// if neither known are string, stop this madness
|
891
911
|
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
@@ -943,7 +963,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
943
963
|
// }
|
944
964
|
})();
|
945
965
|
|
946
|
-
return
|
966
|
+
return finalize([
|
947
967
|
...left,
|
948
968
|
...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
|
949
969
|
...right,
|
@@ -960,7 +980,22 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
960
980
|
return out;
|
961
981
|
};
|
962
982
|
|
963
|
-
const
|
983
|
+
const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals = [], returns = [], localInd = 0 }) => {
|
984
|
+
return func({ name, params, locals, returns, localInd }, {
|
985
|
+
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
|
986
|
+
builtin: name => {
|
987
|
+
let idx = funcIndex[name] ?? importedFuncs[name];
|
988
|
+
if (idx === undefined && builtinFuncs[name]) {
|
989
|
+
includeBuiltin(null, name);
|
990
|
+
idx = funcIndex[name];
|
991
|
+
}
|
992
|
+
|
993
|
+
return idx;
|
994
|
+
}
|
995
|
+
});
|
996
|
+
};
|
997
|
+
|
998
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
964
999
|
const existing = funcs.find(x => x.name === name);
|
965
1000
|
if (existing) return existing;
|
966
1001
|
|
@@ -972,18 +1007,14 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
972
1007
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
973
1008
|
}
|
974
1009
|
|
975
|
-
|
976
|
-
const
|
977
|
-
|
978
|
-
|
979
|
-
locals,
|
980
|
-
returns,
|
981
|
-
localInd: allLocals.length,
|
982
|
-
};
|
983
|
-
|
984
|
-
wasm = wasm(scope, { TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString });
|
1010
|
+
for (const x of _data) {
|
1011
|
+
const copy = { ...x };
|
1012
|
+
copy.offset += pages.size * pageSize;
|
1013
|
+
data.push(copy);
|
985
1014
|
}
|
986
1015
|
|
1016
|
+
if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
|
1017
|
+
|
987
1018
|
let baseGlobalIdx, i = 0;
|
988
1019
|
for (const type of globalTypes) {
|
989
1020
|
if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
|
@@ -1086,7 +1117,10 @@ const TYPE_NAMES = {
|
|
1086
1117
|
const getType = (scope, _name) => {
|
1087
1118
|
const name = mapName(_name);
|
1088
1119
|
|
1120
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1089
1121
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1122
|
+
|
1123
|
+
if (typedInput && globals[name]?.metadata?.type != null) return number(globals[name].metadata.type, Valtype.i32);
|
1090
1124
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1091
1125
|
|
1092
1126
|
let type = TYPES.undefined;
|
@@ -1164,7 +1198,7 @@ const getNodeType = (scope, node) => {
|
|
1164
1198
|
if (func.returnType) return func.returnType;
|
1165
1199
|
}
|
1166
1200
|
|
1167
|
-
if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1201
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1168
1202
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1169
1203
|
|
1170
1204
|
// check if this is a prototype function
|
@@ -1179,6 +1213,11 @@ const getNodeType = (scope, node) => {
|
|
1179
1213
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1180
1214
|
}
|
1181
1215
|
|
1216
|
+
if (name.startsWith('__Porffor_wasm_')) {
|
1217
|
+
// todo: return undefined for non-returning ops
|
1218
|
+
return TYPES.number;
|
1219
|
+
}
|
1220
|
+
|
1182
1221
|
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1183
1222
|
|
1184
1223
|
// presume
|
@@ -1227,6 +1266,14 @@ const getNodeType = (scope, node) => {
|
|
1227
1266
|
|
1228
1267
|
if (node.type === 'BinaryExpression') {
|
1229
1268
|
if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
|
1269
|
+
if (node.operator !== '+') return TYPES.number;
|
1270
|
+
|
1271
|
+
const knownLeft = knownType(scope, getNodeType(scope, node.left));
|
1272
|
+
const knownRight = knownType(scope, getNodeType(scope, node.right));
|
1273
|
+
|
1274
|
+
// todo: this should be dynamic but for now only static
|
1275
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1276
|
+
|
1230
1277
|
return TYPES.number;
|
1231
1278
|
|
1232
1279
|
// todo: string concat types
|
@@ -1251,7 +1298,7 @@ const getNodeType = (scope, node) => {
|
|
1251
1298
|
if (node.operator === '!') return TYPES.boolean;
|
1252
1299
|
if (node.operator === 'void') return TYPES.undefined;
|
1253
1300
|
if (node.operator === 'delete') return TYPES.boolean;
|
1254
|
-
if (node.operator === 'typeof') return
|
1301
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES._bytestring : TYPES.string;
|
1255
1302
|
|
1256
1303
|
return TYPES.number;
|
1257
1304
|
}
|
@@ -1320,7 +1367,7 @@ const countLeftover = wasm => {
|
|
1320
1367
|
if (inst[0] === Opcodes.end) depth--;
|
1321
1368
|
|
1322
1369
|
if (depth === 0)
|
1323
|
-
if ([Opcodes.throw,Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1370
|
+
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1324
1371
|
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)) {}
|
1325
1372
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const].includes(inst[0])) count++;
|
1326
1373
|
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
@@ -1645,6 +1692,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1645
1692
|
idx = -1;
|
1646
1693
|
}
|
1647
1694
|
|
1695
|
+
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1696
|
+
const wasmOps = {
|
1697
|
+
// pointer, align, offset
|
1698
|
+
i32_load8_u: { imms: 2, args: 1 },
|
1699
|
+
// pointer, value, align, offset
|
1700
|
+
i32_store8: { imms: 2, args: 2 },
|
1701
|
+
};
|
1702
|
+
|
1703
|
+
const opName = name.slice('__Porffor_wasm_'.length);
|
1704
|
+
|
1705
|
+
if (wasmOps[opName]) {
|
1706
|
+
const op = wasmOps[opName];
|
1707
|
+
|
1708
|
+
const argOut = [];
|
1709
|
+
for (let i = 0; i < op.args; i++) argOut.push(...generate(scope, decl.arguments[i]));
|
1710
|
+
|
1711
|
+
// literals only
|
1712
|
+
const imms = decl.arguments.slice(op.args).map(x => x.value);
|
1713
|
+
|
1714
|
+
return [
|
1715
|
+
...argOut,
|
1716
|
+
[ Opcodes[opName], ...imms ]
|
1717
|
+
];
|
1718
|
+
}
|
1719
|
+
}
|
1720
|
+
|
1648
1721
|
if (idx === undefined) {
|
1649
1722
|
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`);
|
1650
1723
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
|
@@ -1654,7 +1727,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1654
1727
|
|
1655
1728
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1656
1729
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1657
|
-
const
|
1730
|
+
const typedReturns = userFunc || builtinFuncs[name]?.typedReturns;
|
1658
1731
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1659
1732
|
|
1660
1733
|
let args = decl.arguments;
|
@@ -1671,14 +1744,20 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1671
1744
|
if (func && func.throws) scope.throws = true;
|
1672
1745
|
|
1673
1746
|
let out = [];
|
1674
|
-
for (
|
1747
|
+
for (let i = 0; i < args.length; i++) {
|
1748
|
+
const arg = args[i];
|
1675
1749
|
out = out.concat(generate(scope, arg));
|
1750
|
+
|
1751
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1752
|
+
out.push(Opcodes.i32_to);
|
1753
|
+
}
|
1754
|
+
|
1676
1755
|
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1677
1756
|
}
|
1678
1757
|
|
1679
1758
|
out.push([ Opcodes.call, idx ]);
|
1680
1759
|
|
1681
|
-
if (!
|
1760
|
+
if (!typedReturns) {
|
1682
1761
|
// let type;
|
1683
1762
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1684
1763
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1690,6 +1769,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1690
1769
|
// );
|
1691
1770
|
} else out.push(setLastType(scope));
|
1692
1771
|
|
1772
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1773
|
+
out.push(Opcodes.i32_from);
|
1774
|
+
}
|
1775
|
+
|
1693
1776
|
return out;
|
1694
1777
|
};
|
1695
1778
|
|
@@ -1814,14 +1897,14 @@ const brTable = (input, bc, returns) => {
|
|
1814
1897
|
};
|
1815
1898
|
|
1816
1899
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1817
|
-
if (!
|
1900
|
+
if (!Prefs.bytestring) delete bc[TYPES._bytestring];
|
1818
1901
|
|
1819
1902
|
const known = knownType(scope, type);
|
1820
1903
|
if (known != null) {
|
1821
1904
|
return bc[known] ?? bc.default;
|
1822
1905
|
}
|
1823
1906
|
|
1824
|
-
if (
|
1907
|
+
if (Prefs.typeswitchUseBrtable)
|
1825
1908
|
return brTable(type, bc, returns);
|
1826
1909
|
|
1827
1910
|
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
@@ -1856,7 +1939,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1856
1939
|
return out;
|
1857
1940
|
};
|
1858
1941
|
|
1859
|
-
const allocVar = (scope, name, global = false) => {
|
1942
|
+
const allocVar = (scope, name, global = false, type = true) => {
|
1860
1943
|
const target = global ? globals : scope.locals;
|
1861
1944
|
|
1862
1945
|
// already declared
|
@@ -1870,8 +1953,10 @@ const allocVar = (scope, name, global = false) => {
|
|
1870
1953
|
let idx = global ? globalInd++ : scope.localInd++;
|
1871
1954
|
target[name] = { idx, type: valtypeBinary };
|
1872
1955
|
|
1873
|
-
|
1874
|
-
|
1956
|
+
if (type) {
|
1957
|
+
let typeIdx = global ? globalInd++ : scope.localInd++;
|
1958
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
1959
|
+
}
|
1875
1960
|
|
1876
1961
|
return idx;
|
1877
1962
|
};
|
@@ -1891,6 +1976,7 @@ const typeAnnoToPorfType = x => {
|
|
1891
1976
|
|
1892
1977
|
switch (x) {
|
1893
1978
|
case 'i32':
|
1979
|
+
case 'i64':
|
1894
1980
|
return TYPES.number;
|
1895
1981
|
}
|
1896
1982
|
|
@@ -1914,7 +2000,7 @@ const extractTypeAnnotation = decl => {
|
|
1914
2000
|
const typeName = type;
|
1915
2001
|
type = typeAnnoToPorfType(type);
|
1916
2002
|
|
1917
|
-
if (type === TYPES._bytestring && !
|
2003
|
+
if (type === TYPES._bytestring && !Prefs.bytestring) type = TYPES.string;
|
1918
2004
|
|
1919
2005
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1920
2006
|
|
@@ -1949,7 +2035,7 @@ const generateVar = (scope, decl) => {
|
|
1949
2035
|
continue; // always ignore
|
1950
2036
|
}
|
1951
2037
|
|
1952
|
-
let idx = allocVar(scope, name, global);
|
2038
|
+
let idx = allocVar(scope, name, global, !x.id.typeAnnotation);
|
1953
2039
|
|
1954
2040
|
if (typedInput && x.id.typeAnnotation) {
|
1955
2041
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
@@ -2120,9 +2206,7 @@ const generateAssign = (scope, decl) => {
|
|
2120
2206
|
], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
|
2121
2207
|
[ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
2122
2208
|
|
2123
|
-
getLastType(scope)
|
2124
|
-
// hack: type is idx+1
|
2125
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2209
|
+
...setType(scope, name, getLastType(scope))
|
2126
2210
|
];
|
2127
2211
|
}
|
2128
2212
|
|
@@ -2133,9 +2217,7 @@ const generateAssign = (scope, decl) => {
|
|
2133
2217
|
|
2134
2218
|
// todo: string concat types
|
2135
2219
|
|
2136
|
-
|
2137
|
-
...number(TYPES.number, Valtype.i32),
|
2138
|
-
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx + 1 ],
|
2220
|
+
...setType(scope, name, TYPES.number)
|
2139
2221
|
];
|
2140
2222
|
};
|
2141
2223
|
|
@@ -2546,6 +2628,9 @@ const generateThrow = (scope, decl) => {
|
|
2546
2628
|
let exceptId = exceptions.push({ constructor, message }) - 1;
|
2547
2629
|
let tagIdx = tags[0].idx;
|
2548
2630
|
|
2631
|
+
scope.exceptions ??= [];
|
2632
|
+
scope.exceptions.push(exceptId);
|
2633
|
+
|
2549
2634
|
// todo: write a description of how this works lol
|
2550
2635
|
|
2551
2636
|
return [
|
@@ -2590,7 +2675,7 @@ const generateAssignPat = (scope, decl) => {
|
|
2590
2675
|
};
|
2591
2676
|
|
2592
2677
|
let pages = new Map();
|
2593
|
-
const allocPage = (reason, type) => {
|
2678
|
+
const allocPage = (scope, reason, type) => {
|
2594
2679
|
if (pages.has(reason)) return pages.get(reason).ind;
|
2595
2680
|
|
2596
2681
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
@@ -2601,16 +2686,20 @@ const allocPage = (reason, type) => {
|
|
2601
2686
|
const ind = pages.size;
|
2602
2687
|
pages.set(reason, { ind, type });
|
2603
2688
|
|
2604
|
-
|
2689
|
+
scope.pages ??= new Map();
|
2690
|
+
scope.pages.set(reason, { ind, type });
|
2691
|
+
|
2692
|
+
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2605
2693
|
|
2606
2694
|
return ind;
|
2607
2695
|
};
|
2608
2696
|
|
2697
|
+
// todo: add scope.pages
|
2609
2698
|
const freePage = reason => {
|
2610
2699
|
const { ind } = pages.get(reason);
|
2611
2700
|
pages.delete(reason);
|
2612
2701
|
|
2613
|
-
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2702
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2614
2703
|
|
2615
2704
|
return ind;
|
2616
2705
|
};
|
@@ -2636,15 +2725,14 @@ const StoreOps = {
|
|
2636
2725
|
|
2637
2726
|
let data = [];
|
2638
2727
|
|
2639
|
-
const compileBytes = (val, itemType
|
2728
|
+
const compileBytes = (val, itemType) => {
|
2640
2729
|
// todo: this is a mess and needs confirming / ????
|
2641
2730
|
switch (itemType) {
|
2642
2731
|
case 'i8': return [ val % 256 ];
|
2643
|
-
case 'i16': return [ val % 256,
|
2644
|
-
|
2645
|
-
case 'i32':
|
2646
|
-
|
2647
|
-
return enforceFourBytes(signedLEB128(val));
|
2732
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2733
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2734
|
+
case 'i32': return [...new Uint8Array(new Int32Array([ val ]).buffer)];
|
2735
|
+
// todo: i64
|
2648
2736
|
|
2649
2737
|
case 'f64': return ieee754_binary64(val);
|
2650
2738
|
}
|
@@ -2668,7 +2756,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2668
2756
|
|
2669
2757
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2670
2758
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2671
|
-
arrays.set(name, allocPage(`${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2759
|
+
arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2672
2760
|
}
|
2673
2761
|
|
2674
2762
|
const pointer = arrays.get(name);
|
@@ -2679,19 +2767,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2679
2767
|
const valtype = itemTypeToValtype[itemType];
|
2680
2768
|
const length = elements.length;
|
2681
2769
|
|
2682
|
-
if (firstAssign && useRawElements) {
|
2683
|
-
|
2770
|
+
if (firstAssign && useRawElements && !Prefs.noData) {
|
2771
|
+
// if length is 0 memory/data will just be 0000... anyway
|
2772
|
+
if (length !== 0) {
|
2773
|
+
let bytes = compileBytes(length, 'i32');
|
2684
2774
|
|
2685
|
-
|
2686
|
-
|
2775
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
2776
|
+
if (elements[i] == null) continue;
|
2687
2777
|
|
2688
|
-
|
2689
|
-
|
2778
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
2779
|
+
}
|
2690
2780
|
|
2691
|
-
|
2692
|
-
|
2693
|
-
|
2694
|
-
|
2781
|
+
const ind = data.push({
|
2782
|
+
offset: pointer,
|
2783
|
+
bytes
|
2784
|
+
}) - 1;
|
2785
|
+
|
2786
|
+
scope.data ??= [];
|
2787
|
+
scope.data.push(ind);
|
2788
|
+
}
|
2695
2789
|
|
2696
2790
|
// local value as pointer
|
2697
2791
|
out.push(...number(pointer));
|
@@ -2725,7 +2819,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2725
2819
|
};
|
2726
2820
|
|
2727
2821
|
const byteStringable = str => {
|
2728
|
-
if (!
|
2822
|
+
if (!Prefs.bytestring) return false;
|
2729
2823
|
|
2730
2824
|
for (let i = 0; i < str.length; i++) {
|
2731
2825
|
if (str.charCodeAt(i) > 0xFF) return false;
|
@@ -2736,7 +2830,7 @@ const byteStringable = str => {
|
|
2736
2830
|
|
2737
2831
|
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2738
2832
|
const rawElements = new Array(str.length);
|
2739
|
-
let byteStringable =
|
2833
|
+
let byteStringable = Prefs.bytestring;
|
2740
2834
|
for (let i = 0; i < str.length; i++) {
|
2741
2835
|
const c = str.charCodeAt(i);
|
2742
2836
|
rawElements[i] = c;
|
@@ -2897,7 +2991,7 @@ const objectHack = node => {
|
|
2897
2991
|
if (!objectName) return node;
|
2898
2992
|
|
2899
2993
|
const name = '__' + objectName + '_' + node.property.name;
|
2900
|
-
if (codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2994
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2901
2995
|
|
2902
2996
|
return {
|
2903
2997
|
type: 'Identifier',
|
@@ -2959,6 +3053,8 @@ const generateFunc = (scope, decl) => {
|
|
2959
3053
|
};
|
2960
3054
|
funcIndex[name] = func.index;
|
2961
3055
|
|
3056
|
+
if (name === 'main') func.gotLastType = true;
|
3057
|
+
|
2962
3058
|
// quick hack fixes
|
2963
3059
|
for (const inst of wasm) {
|
2964
3060
|
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
@@ -3124,7 +3220,7 @@ export default program => {
|
|
3124
3220
|
body: program.body
|
3125
3221
|
};
|
3126
3222
|
|
3127
|
-
if (
|
3223
|
+
if (Prefs.astLog) console.log(program.body.body);
|
3128
3224
|
|
3129
3225
|
generateFunc(scope, program);
|
3130
3226
|
|