porffor 0.2.0-1afe9b8 → 0.2.0-2265539
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 +62 -42
- package/compiler/2c.js +316 -71
- package/compiler/builtins/base64.ts +141 -0
- package/compiler/builtins/porffor.d.ts +35 -0
- package/compiler/builtins.js +72 -183
- package/compiler/codeGen.js +229 -97
- package/compiler/generated_builtins.js +15 -0
- package/compiler/index.js +22 -22
- 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/wrap.js +2 -1
- package/demo.js +3 -0
- package/demo.ts +1 -0
- package/filesize.cmd +2 -0
- package/hello +0 -0
- package/package.json +1 -1
- package/porf +2 -0
- package/rhemyn/compile.js +2 -1
- package/runner/index.js +20 -3
- package/tmp.c +152 -0
- package/compiler/builtins/base64.js +0 -92
- package/r.js +0 -4
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,26 +189,54 @@ 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
|
}
|
195
200
|
|
196
201
|
return out;
|
197
|
-
}
|
198
|
-
|
202
|
+
},
|
203
|
+
|
204
|
+
__Porffor_bs: str => [
|
205
|
+
...makeString(scope, str, undefined, undefined, true),
|
206
|
+
|
207
|
+
...number(TYPES._bytestring, Valtype.i32),
|
208
|
+
setLastType(scope)
|
209
|
+
],
|
210
|
+
__Porffor_s: str => [
|
211
|
+
...makeString(scope, str, undefined, undefined, false),
|
212
|
+
|
213
|
+
...number(TYPES.string, Valtype.i32),
|
214
|
+
setLastType(scope)
|
215
|
+
],
|
216
|
+
};
|
199
217
|
|
200
218
|
const name = decl.tag.name;
|
201
219
|
// hack for inline asm
|
202
220
|
if (!funcs[name]) return todo('tagged template expressions not implemented');
|
203
221
|
|
204
|
-
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
|
+
|
231
|
+
console.log(str);
|
232
|
+
|
205
233
|
return funcs[name](str);
|
206
234
|
}
|
207
235
|
|
208
236
|
default:
|
209
|
-
|
210
|
-
|
237
|
+
// ignore typescript nodes
|
238
|
+
if (decl.type.startsWith('TS') ||
|
239
|
+
decl.type === 'ImportDeclaration' && decl.importKind === 'type') {
|
211
240
|
return [];
|
212
241
|
}
|
213
242
|
|
@@ -396,9 +425,6 @@ const concatStrings = (scope, left, right, global, name, assign) => {
|
|
396
425
|
const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
|
397
426
|
const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
|
398
427
|
|
399
|
-
const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
|
400
|
-
if (aotWFA) addVarMeta(name, { wellFormed: undefined });
|
401
|
-
|
402
428
|
if (assign) {
|
403
429
|
const pointer = arrays.get(name ?? '$undeclared');
|
404
430
|
|
@@ -636,11 +662,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
636
662
|
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
637
663
|
];
|
638
664
|
|
639
|
-
const
|
665
|
+
const useTmp = knownType(scope, type) == null;
|
666
|
+
const tmp = !useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
640
667
|
|
641
668
|
const def = [
|
642
669
|
// if value != 0
|
643
|
-
[ Opcodes.local_get, tmp ],
|
670
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
644
671
|
|
645
672
|
// ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
646
673
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
@@ -652,7 +679,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
652
679
|
|
653
680
|
return [
|
654
681
|
...wasm,
|
655
|
-
[ Opcodes.local_set, tmp ],
|
682
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
656
683
|
|
657
684
|
...typeSwitch(scope, type, {
|
658
685
|
// [TYPES.number]: def,
|
@@ -661,7 +688,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
661
688
|
...number(1, intOut ? Valtype.i32 : valtypeBinary)
|
662
689
|
],
|
663
690
|
[TYPES.string]: [
|
664
|
-
[ Opcodes.local_get, tmp ],
|
691
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
665
692
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
666
693
|
|
667
694
|
// get length
|
@@ -673,7 +700,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
673
700
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
674
701
|
],
|
675
702
|
[TYPES._bytestring]: [ // duplicate of string
|
676
|
-
|
703
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
677
704
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
678
705
|
|
679
706
|
// get length
|
@@ -687,10 +714,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
687
714
|
};
|
688
715
|
|
689
716
|
const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
690
|
-
const
|
717
|
+
const useTmp = knownType(scope, type) == null;
|
718
|
+
const tmp = !useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
719
|
+
|
691
720
|
return [
|
692
721
|
...wasm,
|
693
|
-
[ Opcodes.local_set, tmp ],
|
722
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
694
723
|
|
695
724
|
...typeSwitch(scope, type, {
|
696
725
|
[TYPES._array]: [
|
@@ -698,7 +727,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
698
727
|
...number(0, intOut ? Valtype.i32 : valtypeBinary)
|
699
728
|
],
|
700
729
|
[TYPES.string]: [
|
701
|
-
[ Opcodes.local_get, tmp ],
|
730
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
702
731
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
703
732
|
|
704
733
|
// get length
|
@@ -709,7 +738,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
709
738
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
710
739
|
],
|
711
740
|
[TYPES._bytestring]: [ // duplicate of string
|
712
|
-
[ Opcodes.local_get, tmp ],
|
741
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
713
742
|
...(intIn ? [] : [ Opcodes.i32_to_u ]),
|
714
743
|
|
715
744
|
// get length
|
@@ -721,7 +750,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
721
750
|
],
|
722
751
|
default: [
|
723
752
|
// if value == 0
|
724
|
-
[ Opcodes.local_get, tmp ],
|
753
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
725
754
|
|
726
755
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
727
756
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -731,10 +760,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
731
760
|
};
|
732
761
|
|
733
762
|
const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
734
|
-
const
|
763
|
+
const useTmp = knownType(scope, type) == null;
|
764
|
+
const tmp = !useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
|
765
|
+
|
735
766
|
return [
|
736
767
|
...wasm,
|
737
|
-
[ Opcodes.local_set, tmp ],
|
768
|
+
...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
|
738
769
|
|
739
770
|
...typeSwitch(scope, type, {
|
740
771
|
[TYPES.undefined]: [
|
@@ -743,7 +774,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
743
774
|
],
|
744
775
|
[TYPES.object]: [
|
745
776
|
// object, null if == 0
|
746
|
-
[ Opcodes.local_get, tmp ],
|
777
|
+
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
747
778
|
|
748
779
|
...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
749
780
|
...(intOut ? [] : [ Opcodes.i32_from_u ])
|
@@ -772,11 +803,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
772
803
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
773
804
|
}
|
774
805
|
|
806
|
+
const knownLeft = knownType(scope, leftType);
|
807
|
+
const knownRight = knownType(scope, rightType);
|
808
|
+
|
775
809
|
const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
|
776
810
|
const strictOp = op === '===' || op === '!==';
|
777
811
|
|
778
812
|
const startOut = [], endOut = [];
|
779
|
-
const
|
813
|
+
const finalize = out => startOut.concat(out, endOut);
|
780
814
|
|
781
815
|
// if strict (in)equal check types match
|
782
816
|
if (strictOp) {
|
@@ -821,31 +855,32 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
821
855
|
// todo: if equality op and an operand is undefined, return false
|
822
856
|
// todo: niche null hell with 0
|
823
857
|
|
824
|
-
//
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
858
|
+
// todo: this should be dynamic but for now only static
|
859
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) {
|
860
|
+
if (op === '+') {
|
861
|
+
// string concat (a + b)
|
862
|
+
return concatStrings(scope, left, right, _global, _name, assign);
|
863
|
+
}
|
864
|
+
|
865
|
+
// not an equality op, NaN
|
866
|
+
if (!eqOp) return number(NaN);
|
867
|
+
|
868
|
+
// else leave bool ops
|
869
|
+
// todo: convert string to number if string and number/bool
|
870
|
+
// todo: string (>|>=|<|<=) string
|
871
|
+
|
872
|
+
// string comparison
|
873
|
+
if (op === '===' || op === '==') {
|
874
|
+
return compareStrings(scope, left, right);
|
875
|
+
}
|
876
|
+
|
877
|
+
if (op === '!==' || op === '!=') {
|
878
|
+
return [
|
879
|
+
...compareStrings(scope, left, right),
|
880
|
+
[ Opcodes.i32_eqz ]
|
881
|
+
];
|
882
|
+
}
|
883
|
+
}
|
849
884
|
|
850
885
|
let ops = operatorOpcode[valtype][op];
|
851
886
|
|
@@ -855,7 +890,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
855
890
|
includeBuiltin(scope, builtinName);
|
856
891
|
const idx = funcIndex[builtinName];
|
857
892
|
|
858
|
-
return
|
893
|
+
return finalize([
|
859
894
|
...left,
|
860
895
|
...right,
|
861
896
|
[ Opcodes.call, idx ]
|
@@ -870,9 +905,6 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
870
905
|
let tmpLeft, tmpRight;
|
871
906
|
// if equal op, check if strings for compareStrings
|
872
907
|
if (op === '===' || op === '==' || op === '!==' || op === '!=') (() => {
|
873
|
-
const knownLeft = knownType(scope, leftType);
|
874
|
-
const knownRight = knownType(scope, rightType);
|
875
|
-
|
876
908
|
// todo: intelligent partial skip later
|
877
909
|
// if neither known are string, stop this madness
|
878
910
|
if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
|
@@ -912,7 +944,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
912
944
|
[ Opcodes.i32_or ],
|
913
945
|
[ Opcodes.if, Blocktype.void ],
|
914
946
|
...number(0, Valtype.i32),
|
915
|
-
[ Opcodes.br,
|
947
|
+
[ Opcodes.br, 2 ],
|
916
948
|
[ Opcodes.end ],
|
917
949
|
|
918
950
|
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
|
@@ -930,7 +962,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
930
962
|
// }
|
931
963
|
})();
|
932
964
|
|
933
|
-
return
|
965
|
+
return finalize([
|
934
966
|
...left,
|
935
967
|
...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
|
936
968
|
...right,
|
@@ -947,7 +979,7 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
947
979
|
return out;
|
948
980
|
};
|
949
981
|
|
950
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
|
982
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
951
983
|
const existing = funcs.find(x => x.name === name);
|
952
984
|
if (existing) return existing;
|
953
985
|
|
@@ -959,6 +991,12 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
959
991
|
locals[nameParam(i)] = { idx: i, type: allLocals[i] };
|
960
992
|
}
|
961
993
|
|
994
|
+
for (const x of _data) {
|
995
|
+
const copy = { ...x };
|
996
|
+
copy.offset += pages.size * pageSize;
|
997
|
+
data.push(copy);
|
998
|
+
}
|
999
|
+
|
962
1000
|
if (typeof wasm === 'function') {
|
963
1001
|
const scope = {
|
964
1002
|
name,
|
@@ -968,7 +1006,18 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
968
1006
|
localInd: allLocals.length,
|
969
1007
|
};
|
970
1008
|
|
971
|
-
wasm = wasm(scope, {
|
1009
|
+
wasm = wasm(scope, {
|
1010
|
+
TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
|
1011
|
+
builtin: name => {
|
1012
|
+
let idx = funcIndex[name] ?? importedFuncs[name];
|
1013
|
+
if (idx === undefined && builtinFuncs[name]) {
|
1014
|
+
includeBuiltin(scope, name);
|
1015
|
+
idx = funcIndex[name];
|
1016
|
+
}
|
1017
|
+
|
1018
|
+
return idx;
|
1019
|
+
}
|
1020
|
+
});
|
972
1021
|
}
|
973
1022
|
|
974
1023
|
let baseGlobalIdx, i = 0;
|
@@ -1073,7 +1122,10 @@ const TYPE_NAMES = {
|
|
1073
1122
|
const getType = (scope, _name) => {
|
1074
1123
|
const name = mapName(_name);
|
1075
1124
|
|
1125
|
+
if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
|
1076
1126
|
if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
|
1127
|
+
|
1128
|
+
if (typedInput && globals[name]?.metadata?.type != null) return number(globals[name].metadata.type, Valtype.i32);
|
1077
1129
|
if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
|
1078
1130
|
|
1079
1131
|
let type = TYPES.undefined;
|
@@ -1151,7 +1203,7 @@ const getNodeType = (scope, node) => {
|
|
1151
1203
|
if (func.returnType) return func.returnType;
|
1152
1204
|
}
|
1153
1205
|
|
1154
|
-
if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1206
|
+
if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1155
1207
|
if (internalConstrs[name]) return internalConstrs[name].type;
|
1156
1208
|
|
1157
1209
|
// check if this is a prototype function
|
@@ -1166,6 +1218,11 @@ const getNodeType = (scope, node) => {
|
|
1166
1218
|
if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
|
1167
1219
|
}
|
1168
1220
|
|
1221
|
+
if (name.startsWith('__Porffor_wasm_')) {
|
1222
|
+
// todo: return undefined for non-returning ops
|
1223
|
+
return TYPES.number;
|
1224
|
+
}
|
1225
|
+
|
1169
1226
|
if (scope.locals['#last_type']) return [ getLastType(scope) ];
|
1170
1227
|
|
1171
1228
|
// presume
|
@@ -1214,6 +1271,14 @@ const getNodeType = (scope, node) => {
|
|
1214
1271
|
|
1215
1272
|
if (node.type === 'BinaryExpression') {
|
1216
1273
|
if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
|
1274
|
+
if (node.operator !== '+') return TYPES.number;
|
1275
|
+
|
1276
|
+
const knownLeft = knownType(scope, getNodeType(scope, node.left));
|
1277
|
+
const knownRight = knownType(scope, getNodeType(scope, node.right));
|
1278
|
+
|
1279
|
+
// todo: this should be dynamic but for now only static
|
1280
|
+
if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
|
1281
|
+
|
1217
1282
|
return TYPES.number;
|
1218
1283
|
|
1219
1284
|
// todo: string concat types
|
@@ -1238,7 +1303,7 @@ const getNodeType = (scope, node) => {
|
|
1238
1303
|
if (node.operator === '!') return TYPES.boolean;
|
1239
1304
|
if (node.operator === 'void') return TYPES.undefined;
|
1240
1305
|
if (node.operator === 'delete') return TYPES.boolean;
|
1241
|
-
if (node.operator === 'typeof') return
|
1306
|
+
if (node.operator === 'typeof') return Prefs.bytestring ? TYPES._bytestring : TYPES.string;
|
1242
1307
|
|
1243
1308
|
return TYPES.number;
|
1244
1309
|
}
|
@@ -1632,6 +1697,44 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1632
1697
|
idx = -1;
|
1633
1698
|
}
|
1634
1699
|
|
1700
|
+
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1701
|
+
const wasmOps = {
|
1702
|
+
// pointer, align, offset
|
1703
|
+
i32_load8_u: { imms: 2, args: 1 },
|
1704
|
+
// pointer, value, align, offset
|
1705
|
+
i32_store8: { imms: 2, args: 2 },
|
1706
|
+
// pointer, align, offset
|
1707
|
+
i64_load: { imms: 2, args: 1 },
|
1708
|
+
// a, b
|
1709
|
+
i64_shr_u: { imms: 0, args: 2 },
|
1710
|
+
// x
|
1711
|
+
i32_wrap_i64: { imms: 0, args: 1, },
|
1712
|
+
// x
|
1713
|
+
i64_extend_i32_u: { imms: 0, args: 1 },
|
1714
|
+
// a, b
|
1715
|
+
i64_and: { imms: 0, args: 2 },
|
1716
|
+
// val (todo: support >255)
|
1717
|
+
i64_const: { imms: 1, args: 0 },
|
1718
|
+
};
|
1719
|
+
|
1720
|
+
const opName = name.slice('__Porffor_wasm_'.length);
|
1721
|
+
|
1722
|
+
if (wasmOps[opName]) {
|
1723
|
+
const op = wasmOps[opName];
|
1724
|
+
|
1725
|
+
const argOut = [];
|
1726
|
+
for (let i = 0; i < op.args; i++) argOut.push(...generate(scope, decl.arguments[i]));
|
1727
|
+
|
1728
|
+
// literals only
|
1729
|
+
const imms = decl.arguments.slice(op.args).map(x => x.value);
|
1730
|
+
|
1731
|
+
return [
|
1732
|
+
...argOut,
|
1733
|
+
[ Opcodes[opName], ...imms ]
|
1734
|
+
];
|
1735
|
+
}
|
1736
|
+
}
|
1737
|
+
|
1635
1738
|
if (idx === undefined) {
|
1636
1739
|
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`);
|
1637
1740
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
|
@@ -1641,7 +1744,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1641
1744
|
|
1642
1745
|
const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
|
1643
1746
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1644
|
-
const
|
1747
|
+
const typedReturns = userFunc || builtinFuncs[name]?.typedReturns;
|
1645
1748
|
const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
|
1646
1749
|
|
1647
1750
|
let args = decl.arguments;
|
@@ -1658,14 +1761,20 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1658
1761
|
if (func && func.throws) scope.throws = true;
|
1659
1762
|
|
1660
1763
|
let out = [];
|
1661
|
-
for (
|
1764
|
+
for (let i = 0; i < args.length; i++) {
|
1765
|
+
const arg = args[i];
|
1662
1766
|
out = out.concat(generate(scope, arg));
|
1767
|
+
|
1768
|
+
if (builtinFuncs[name] && builtinFuncs[name].params[i] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1769
|
+
out.push(Opcodes.i32_to);
|
1770
|
+
}
|
1771
|
+
|
1663
1772
|
if (typedParams) out = out.concat(getNodeType(scope, arg));
|
1664
1773
|
}
|
1665
1774
|
|
1666
1775
|
out.push([ Opcodes.call, idx ]);
|
1667
1776
|
|
1668
|
-
if (!
|
1777
|
+
if (!typedReturns) {
|
1669
1778
|
// let type;
|
1670
1779
|
// if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
|
1671
1780
|
// if (internalConstrs[name]) type = internalConstrs[name].type;
|
@@ -1677,6 +1786,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1677
1786
|
// );
|
1678
1787
|
} else out.push(setLastType(scope));
|
1679
1788
|
|
1789
|
+
if (builtinFuncs[name] && builtinFuncs[name].returns[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
|
1790
|
+
out.push(Opcodes.i32_from);
|
1791
|
+
}
|
1792
|
+
|
1680
1793
|
return out;
|
1681
1794
|
};
|
1682
1795
|
|
@@ -1801,14 +1914,14 @@ const brTable = (input, bc, returns) => {
|
|
1801
1914
|
};
|
1802
1915
|
|
1803
1916
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
1804
|
-
if (!
|
1917
|
+
if (!Prefs.bytestring) delete bc[TYPES._bytestring];
|
1805
1918
|
|
1806
1919
|
const known = knownType(scope, type);
|
1807
1920
|
if (known != null) {
|
1808
1921
|
return bc[known] ?? bc.default;
|
1809
1922
|
}
|
1810
1923
|
|
1811
|
-
if (
|
1924
|
+
if (Prefs.typeswitchUseBrtable)
|
1812
1925
|
return brTable(type, bc, returns);
|
1813
1926
|
|
1814
1927
|
const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
|
@@ -1843,7 +1956,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
1843
1956
|
return out;
|
1844
1957
|
};
|
1845
1958
|
|
1846
|
-
const allocVar = (scope, name, global = false) => {
|
1959
|
+
const allocVar = (scope, name, global = false, type = true) => {
|
1847
1960
|
const target = global ? globals : scope.locals;
|
1848
1961
|
|
1849
1962
|
// already declared
|
@@ -1857,8 +1970,10 @@ const allocVar = (scope, name, global = false) => {
|
|
1857
1970
|
let idx = global ? globalInd++ : scope.localInd++;
|
1858
1971
|
target[name] = { idx, type: valtypeBinary };
|
1859
1972
|
|
1860
|
-
|
1861
|
-
|
1973
|
+
if (type) {
|
1974
|
+
let typeIdx = global ? globalInd++ : scope.localInd++;
|
1975
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
1976
|
+
}
|
1862
1977
|
|
1863
1978
|
return idx;
|
1864
1979
|
};
|
@@ -1878,6 +1993,7 @@ const typeAnnoToPorfType = x => {
|
|
1878
1993
|
|
1879
1994
|
switch (x) {
|
1880
1995
|
case 'i32':
|
1996
|
+
case 'i64':
|
1881
1997
|
return TYPES.number;
|
1882
1998
|
}
|
1883
1999
|
|
@@ -1901,7 +2017,7 @@ const extractTypeAnnotation = decl => {
|
|
1901
2017
|
const typeName = type;
|
1902
2018
|
type = typeAnnoToPorfType(type);
|
1903
2019
|
|
1904
|
-
if (type === TYPES._bytestring && !
|
2020
|
+
if (type === TYPES._bytestring && !Prefs.bytestring) type = TYPES.string;
|
1905
2021
|
|
1906
2022
|
// if (decl.name) console.log(decl.name, { type, elementType });
|
1907
2023
|
|
@@ -1936,7 +2052,7 @@ const generateVar = (scope, decl) => {
|
|
1936
2052
|
continue; // always ignore
|
1937
2053
|
}
|
1938
2054
|
|
1939
|
-
let idx = allocVar(scope, name, global);
|
2055
|
+
let idx = allocVar(scope, name, global, !x.id.typeAnnotation);
|
1940
2056
|
|
1941
2057
|
if (typedInput && x.id.typeAnnotation) {
|
1942
2058
|
addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
|
@@ -2533,6 +2649,9 @@ const generateThrow = (scope, decl) => {
|
|
2533
2649
|
let exceptId = exceptions.push({ constructor, message }) - 1;
|
2534
2650
|
let tagIdx = tags[0].idx;
|
2535
2651
|
|
2652
|
+
scope.exceptions ??= [];
|
2653
|
+
scope.exceptions.push(exceptId);
|
2654
|
+
|
2536
2655
|
// todo: write a description of how this works lol
|
2537
2656
|
|
2538
2657
|
return [
|
@@ -2577,7 +2696,7 @@ const generateAssignPat = (scope, decl) => {
|
|
2577
2696
|
};
|
2578
2697
|
|
2579
2698
|
let pages = new Map();
|
2580
|
-
const allocPage = (reason, type) => {
|
2699
|
+
const allocPage = (scope, reason, type) => {
|
2581
2700
|
if (pages.has(reason)) return pages.get(reason).ind;
|
2582
2701
|
|
2583
2702
|
if (reason.startsWith('array:')) pages.hasArray = true;
|
@@ -2588,16 +2707,20 @@ const allocPage = (reason, type) => {
|
|
2588
2707
|
const ind = pages.size;
|
2589
2708
|
pages.set(reason, { ind, type });
|
2590
2709
|
|
2591
|
-
|
2710
|
+
scope.pages ??= new Map();
|
2711
|
+
scope.pages.set(reason, { ind, type });
|
2712
|
+
|
2713
|
+
if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
|
2592
2714
|
|
2593
2715
|
return ind;
|
2594
2716
|
};
|
2595
2717
|
|
2718
|
+
// todo: add scope.pages
|
2596
2719
|
const freePage = reason => {
|
2597
2720
|
const { ind } = pages.get(reason);
|
2598
2721
|
pages.delete(reason);
|
2599
2722
|
|
2600
|
-
if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2723
|
+
if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
|
2601
2724
|
|
2602
2725
|
return ind;
|
2603
2726
|
};
|
@@ -2623,15 +2746,14 @@ const StoreOps = {
|
|
2623
2746
|
|
2624
2747
|
let data = [];
|
2625
2748
|
|
2626
|
-
const compileBytes = (val, itemType
|
2749
|
+
const compileBytes = (val, itemType) => {
|
2627
2750
|
// todo: this is a mess and needs confirming / ????
|
2628
2751
|
switch (itemType) {
|
2629
2752
|
case 'i8': return [ val % 256 ];
|
2630
|
-
case 'i16': return [ val % 256,
|
2631
|
-
|
2632
|
-
case 'i32':
|
2633
|
-
|
2634
|
-
return enforceFourBytes(signedLEB128(val));
|
2753
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2754
|
+
case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
|
2755
|
+
case 'i32': return [...new Uint8Array(new Int32Array([ val ]).buffer)];
|
2756
|
+
// todo: i64
|
2635
2757
|
|
2636
2758
|
case 'f64': return ieee754_binary64(val);
|
2637
2759
|
}
|
@@ -2655,7 +2777,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2655
2777
|
|
2656
2778
|
// todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
|
2657
2779
|
const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
|
2658
|
-
arrays.set(name, allocPage(`${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2780
|
+
arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
|
2659
2781
|
}
|
2660
2782
|
|
2661
2783
|
const pointer = arrays.get(name);
|
@@ -2666,19 +2788,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2666
2788
|
const valtype = itemTypeToValtype[itemType];
|
2667
2789
|
const length = elements.length;
|
2668
2790
|
|
2669
|
-
if (firstAssign && useRawElements) {
|
2670
|
-
|
2791
|
+
if (firstAssign && useRawElements && !Prefs.noData) {
|
2792
|
+
// if length is 0 memory/data will just be 0000... anyway
|
2793
|
+
if (length !== 0) {
|
2794
|
+
let bytes = compileBytes(length, 'i32');
|
2671
2795
|
|
2672
|
-
|
2673
|
-
|
2796
|
+
if (!initEmpty) for (let i = 0; i < length; i++) {
|
2797
|
+
if (elements[i] == null) continue;
|
2674
2798
|
|
2675
|
-
|
2676
|
-
|
2799
|
+
bytes.push(...compileBytes(elements[i], itemType));
|
2800
|
+
}
|
2677
2801
|
|
2678
|
-
|
2679
|
-
|
2680
|
-
|
2681
|
-
|
2802
|
+
const ind = data.push({
|
2803
|
+
offset: pointer,
|
2804
|
+
bytes
|
2805
|
+
}) - 1;
|
2806
|
+
|
2807
|
+
scope.data ??= [];
|
2808
|
+
scope.data.push(ind);
|
2809
|
+
}
|
2682
2810
|
|
2683
2811
|
// local value as pointer
|
2684
2812
|
out.push(...number(pointer));
|
@@ -2712,7 +2840,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
2712
2840
|
};
|
2713
2841
|
|
2714
2842
|
const byteStringable = str => {
|
2715
|
-
if (!
|
2843
|
+
if (!Prefs.bytestring) return false;
|
2716
2844
|
|
2717
2845
|
for (let i = 0; i < str.length; i++) {
|
2718
2846
|
if (str.charCodeAt(i) > 0xFF) return false;
|
@@ -2721,9 +2849,9 @@ const byteStringable = str => {
|
|
2721
2849
|
return true;
|
2722
2850
|
};
|
2723
2851
|
|
2724
|
-
const makeString = (scope, str, global = false, name = '$undeclared') => {
|
2852
|
+
const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
|
2725
2853
|
const rawElements = new Array(str.length);
|
2726
|
-
let byteStringable =
|
2854
|
+
let byteStringable = Prefs.bytestring;
|
2727
2855
|
for (let i = 0; i < str.length; i++) {
|
2728
2856
|
const c = str.charCodeAt(i);
|
2729
2857
|
rawElements[i] = c;
|
@@ -2731,6 +2859,8 @@ const makeString = (scope, str, global = false, name = '$undeclared') => {
|
|
2731
2859
|
if (byteStringable && c > 0xFF) byteStringable = false;
|
2732
2860
|
}
|
2733
2861
|
|
2862
|
+
if (byteStringable && forceBytestring === false) byteStringable = false;
|
2863
|
+
|
2734
2864
|
return makeArray(scope, {
|
2735
2865
|
rawElements
|
2736
2866
|
}, global, name, false, byteStringable ? 'i8' : 'i16')[0];
|
@@ -2882,7 +3012,7 @@ const objectHack = node => {
|
|
2882
3012
|
if (!objectName) return node;
|
2883
3013
|
|
2884
3014
|
const name = '__' + objectName + '_' + node.property.name;
|
2885
|
-
if (codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
3015
|
+
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
2886
3016
|
|
2887
3017
|
return {
|
2888
3018
|
type: 'Identifier',
|
@@ -2944,6 +3074,8 @@ const generateFunc = (scope, decl) => {
|
|
2944
3074
|
};
|
2945
3075
|
funcIndex[name] = func.index;
|
2946
3076
|
|
3077
|
+
if (name === 'main') func.gotLastType = true;
|
3078
|
+
|
2947
3079
|
// quick hack fixes
|
2948
3080
|
for (const inst of wasm) {
|
2949
3081
|
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
@@ -3109,7 +3241,7 @@ export default program => {
|
|
3109
3241
|
body: program.body
|
3110
3242
|
};
|
3111
3243
|
|
3112
|
-
if (
|
3244
|
+
if (Prefs.astLog) console.log(program.body.body);
|
3113
3245
|
|
3114
3246
|
generateFunc(scope, program);
|
3115
3247
|
|