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