porffor 0.22.5 → 0.22.6
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/compiler/builtins/_internal_object.ts +45 -0
- package/compiler/builtins/reflect.ts +34 -0
- package/compiler/codegen.js +43 -11
- package/compiler/generated_builtins.js +18 -0
- package/compiler/havoc.js +25 -0
- package/compiler/pgo.js +33 -4
- package/package.json +1 -1
- package/runner/index.js +1 -1
@@ -269,6 +269,51 @@ local.set ${err}`;
|
|
269
269
|
0, 12);
|
270
270
|
};
|
271
271
|
|
272
|
+
export const __Porffor_object_delete = (_this: object, key: any): boolean => {
|
273
|
+
const entryPtr: i32 = __Porffor_object_lookup(_this, key);
|
274
|
+
if (entryPtr == -1) {
|
275
|
+
// not found, stop
|
276
|
+
return true;
|
277
|
+
}
|
278
|
+
|
279
|
+
const tail: i32 = Porffor.wasm.i32.load16_u(entryPtr, 0, 12);
|
280
|
+
if (!(tail & 0b0010)) {
|
281
|
+
// not configurable
|
282
|
+
// todo: throw in strict mode
|
283
|
+
return false;
|
284
|
+
}
|
285
|
+
|
286
|
+
const ind: i32 = (entryPtr - Porffor.wasm`local.get ${_this}`) / 14;
|
287
|
+
|
288
|
+
// decrement size
|
289
|
+
let size: i32 = Porffor.wasm.i32.load(_this, 0, 0);
|
290
|
+
Porffor.wasm.i32.store(_this, --size, 0, 0);
|
291
|
+
|
292
|
+
if (size > ind) {
|
293
|
+
// offset all elements after by -1 ind
|
294
|
+
Porffor.wasm`
|
295
|
+
;; dst = entryPtr
|
296
|
+
local.get ${entryPtr}
|
297
|
+
|
298
|
+
;; src = entryPtr + 14 (+ 1 entry)
|
299
|
+
local.get ${entryPtr}
|
300
|
+
i32.const 14
|
301
|
+
i32.add
|
302
|
+
|
303
|
+
;; size = (size - ind) * 14
|
304
|
+
local.get ${size}
|
305
|
+
local.get ${ind}
|
306
|
+
i32.sub
|
307
|
+
i32.const 14
|
308
|
+
i32.mul
|
309
|
+
|
310
|
+
memory.copy 0 0`;
|
311
|
+
}
|
312
|
+
|
313
|
+
return true;
|
314
|
+
};
|
315
|
+
|
316
|
+
|
272
317
|
export const __Porffor_object_isEnumerable = (entryPtr: i32): boolean => {
|
273
318
|
const out: boolean = Porffor.wasm.i32.load8_u(entryPtr, 0, 12) & 0b0100;
|
274
319
|
return out;
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import type {} from './porffor.d.ts';
|
2
|
+
|
3
|
+
// todo: support receiver
|
4
|
+
export const __Reflect_get = (target: any, property: any) => {
|
5
|
+
if (!Porffor.object.isObject(target)) throw new TypeError('Target is a non-object');
|
6
|
+
|
7
|
+
const p: any = ecma262.ToPropertyKey(property);
|
8
|
+
|
9
|
+
const t: i32 = Porffor.rawType(target);
|
10
|
+
if (t == Porffor.TYPES.object) {
|
11
|
+
return Porffor.object.get(target, p);
|
12
|
+
}
|
13
|
+
|
14
|
+
const keys: any[] = Object.keys(target);
|
15
|
+
const idx: i32 = __Array_prototype_indexOf(keys, p);
|
16
|
+
if (idx == -1) return undefined;
|
17
|
+
|
18
|
+
const vals: any[] = Object.values(target);
|
19
|
+
return vals[idx];
|
20
|
+
};
|
21
|
+
|
22
|
+
export const __Reflect_has = (target: any, property: any) => {
|
23
|
+
if (!Porffor.object.isObject(target)) throw new TypeError('Target is a non-object');
|
24
|
+
|
25
|
+
const p: any = ecma262.ToPropertyKey(property);
|
26
|
+
|
27
|
+
const t: i32 = Porffor.rawType(target);
|
28
|
+
if (t == Porffor.TYPES.object) {
|
29
|
+
return Porffor.object.lookup(target, p) != -1;
|
30
|
+
}
|
31
|
+
|
32
|
+
const keys: any[] = Object.keys(target);
|
33
|
+
return __Array_prototype_includes(keys, p);
|
34
|
+
};
|
package/compiler/codegen.js
CHANGED
@@ -2843,6 +2843,15 @@ const generateVar = (scope, decl) => {
|
|
2843
2843
|
return out;
|
2844
2844
|
};
|
2845
2845
|
|
2846
|
+
const getMemberProperty = decl => {
|
2847
|
+
if (decl.computed) return decl.property;
|
2848
|
+
|
2849
|
+
return {
|
2850
|
+
type: 'Literal',
|
2851
|
+
value: decl.property.name
|
2852
|
+
};
|
2853
|
+
};
|
2854
|
+
|
2846
2855
|
// todo: optimize this func for valueUnused
|
2847
2856
|
const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
2848
2857
|
const { type, name } = decl.left;
|
@@ -2897,10 +2906,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2897
2906
|
const pointerTmp = localTmp(scope, '#member_setter_ptr_tmp', Valtype.i32);
|
2898
2907
|
|
2899
2908
|
const object = decl.left.object;
|
2900
|
-
const property = decl.left
|
2901
|
-
type: 'Literal',
|
2902
|
-
value: decl.left.property.name
|
2903
|
-
};
|
2909
|
+
const property = getMemberProperty(decl.left);
|
2904
2910
|
|
2905
2911
|
// todo/perf: use i32 object (and prop?) locals
|
2906
2912
|
const objectWasm = [ [ Opcodes.local_get, localTmp(scope, '#member_obj') ] ];
|
@@ -3249,6 +3255,25 @@ const generateUnary = (scope, decl) => {
|
|
3249
3255
|
}
|
3250
3256
|
|
3251
3257
|
case 'delete': {
|
3258
|
+
if (decl.argument.type === 'MemberExpression') {
|
3259
|
+
const object = decl.argument.object;
|
3260
|
+
const property = getMemberProperty(decl.argument);
|
3261
|
+
|
3262
|
+
return [
|
3263
|
+
...generate(scope, object),
|
3264
|
+
Opcodes.i32_to_u,
|
3265
|
+
...getNodeType(scope, object),
|
3266
|
+
|
3267
|
+
...generate(scope, property),
|
3268
|
+
...getNodeType(scope, property),
|
3269
|
+
...toPropertyKey(scope, true),
|
3270
|
+
|
3271
|
+
[ Opcodes.call, ...unsignedLEB128(includeBuiltin(scope, '__Porffor_object_delete').index) ],
|
3272
|
+
[ Opcodes.drop ],
|
3273
|
+
Opcodes.i32_from_u
|
3274
|
+
];
|
3275
|
+
}
|
3276
|
+
|
3252
3277
|
let toReturn = true, toGenerate = true;
|
3253
3278
|
|
3254
3279
|
if (decl.argument.type === 'Identifier') {
|
@@ -3256,6 +3281,7 @@ const generateUnary = (scope, decl) => {
|
|
3256
3281
|
|
3257
3282
|
// if ReferenceError (undeclared var), ignore and return true. otherwise false
|
3258
3283
|
if (!out[1]) {
|
3284
|
+
// todo: throw in strict mode
|
3259
3285
|
// exists
|
3260
3286
|
toReturn = false;
|
3261
3287
|
} else {
|
@@ -4654,20 +4680,29 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
4654
4680
|
return number(0);
|
4655
4681
|
}
|
4656
4682
|
|
4683
|
+
const useTmp = !Prefs.lengthNoTmp;
|
4684
|
+
const tmp = useTmp && localTmp(scope, '#length_tmp', Valtype.i32);
|
4657
4685
|
return [
|
4658
|
-
...
|
4659
|
-
|
4686
|
+
...(useTmp ? [
|
4687
|
+
...out,
|
4688
|
+
[ Opcodes.local_set, tmp ],
|
4689
|
+
] : []),
|
4660
4690
|
|
4661
4691
|
...getNodeType(scope, decl.object),
|
4662
4692
|
...number(TYPE_FLAGS.length, Valtype.i32),
|
4663
4693
|
[ Opcodes.i32_and ],
|
4664
4694
|
[ Opcodes.if, valtypeBinary ],
|
4665
|
-
[ Opcodes.local_get,
|
4695
|
+
...(useTmp ? [ [ Opcodes.local_get, tmp ] ] : out),
|
4666
4696
|
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
4667
4697
|
Opcodes.i32_from_u,
|
4668
4698
|
|
4669
4699
|
...setLastType(scope, TYPES.number),
|
4670
4700
|
[ Opcodes.else ],
|
4701
|
+
...(useTmp ? [] : [
|
4702
|
+
...out,
|
4703
|
+
[ Opcodes.drop ]
|
4704
|
+
]),
|
4705
|
+
|
4671
4706
|
...number(0),
|
4672
4707
|
...setLastType(scope, TYPES.undefined),
|
4673
4708
|
[ Opcodes.end ]
|
@@ -4703,10 +4738,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
4703
4738
|
}
|
4704
4739
|
|
4705
4740
|
const object = decl.object;
|
4706
|
-
const property = decl
|
4707
|
-
type: 'Literal',
|
4708
|
-
value: decl.property.name
|
4709
|
-
};
|
4741
|
+
const property = getMemberProperty(decl);
|
4710
4742
|
|
4711
4743
|
// todo/perf: use i32 object (and prop?) locals
|
4712
4744
|
const objectWasm = [ [ Opcodes.local_get, localTmp(scope, '#member_obj') ] ];
|
@@ -56,6 +56,12 @@ export const BuiltinFuncs = function() {
|
|
56
56
|
returns: [127,127], typedReturns: 1,
|
57
57
|
locals: [127,127,127,127,127,127,127,127], localNames: ["_this","_this#type","key","key#type","value","value#type","flags","flags#type","entryPtr","#last_type","#logicinner_tmp","#typeswitch_tmp","size","tail","err","logictmp"],
|
58
58
|
};
|
59
|
+
this.__Porffor_object_delete = {
|
60
|
+
wasm: (scope, {builtin}) => [[32,0],[65,7],[32,2],[32,3],[16, ...builtin('__Porffor_object_lookup')],[26],[34,4],[65,127],[70],[4,64],[65,1],[65,2],[15],[11],[32,4],[47,0,12],[34,6],[65,2],[113],[69],[4,64],[65,0],[65,2],[15],[11],[32,4],[32,0],[107],[65,14],[109],[33,7],[32,0],[40,0,0],[33,8],[32,0],[32,8],[65,1],[107],[34,8],[54,0,0],[32,8],[32,7],[74],[4,64],[32,4],[32,4],[65,14],[106],[32,8],[32,7],[107],[65,14],[108],[252,10,0,0],[11],[65,1],[65,2],[15]],
|
61
|
+
params: [127,127,127,127], typedParams: 1,
|
62
|
+
returns: [127,127], typedReturns: 1,
|
63
|
+
locals: [127,127,127,127,127], localNames: ["_this","_this#type","key","key#type","entryPtr","#last_type","tail","ind","size"],
|
64
|
+
};
|
59
65
|
this.__Porffor_object_isEnumerable = {
|
60
66
|
wasm: (scope, {}) => [[32,0],[45,0,12],[65,4],[113],[34,2],[65,2],[15]],
|
61
67
|
params: [127,127], typedParams: 1,
|
@@ -1835,6 +1841,18 @@ export const BuiltinFuncs = function() {
|
|
1835
1841
|
returns: [124,127], typedReturns: 1,
|
1836
1842
|
locals: [], localNames: ["_this","_this#type"],
|
1837
1843
|
};
|
1844
|
+
this.__Reflect_get = {
|
1845
|
+
wasm: (scope, {builtin,internalThrow}) => [[32,0],[252,2],[32,1],[16, ...builtin('__Porffor_object_isObject')],[33,4],[183],[33,5],[32,4],[33,6],[2,124],[32,6],[65,195,0],[70],[4,64,"TYPESWITCH|String"],[32,5],[252,3],[40,1,0],[69],[184],[12,1],[11],[32,6],[65,195,1],[70],[4,64,"TYPESWITCH|ByteString"],[32,5],[252,3],[40,1,0],[69],[184],[12,1],[11],[32,5],[68,0,0,0,0,0,0,0,0],[97],[184],[11,"TYPESWITCH_end"],[252,3],[4,64],...internalThrow(scope, 'TypeError', `Target is a non-object`),[11],[32,2],[32,3],[16, ...builtin('__ecma262_ToPropertyKey')],[33,8],[33,7],[32,0],[32,1],[16, ...builtin('__Porffor_rawType')],[34,9],[68,0,0,0,0,0,0,28,64],[97],[4,64],[32,0],[252,2],[32,1],[32,7],[252,2],[32,8],[16, ...builtin('__Porffor_object_get')],[34,4],[15],[11],[32,0],[32,1],[16, ...builtin('__Object_keys')],[26],[34,10],[65,208,0],[32,7],[32,8],[68,0,0,0,0,0,0,0,0],[65,128,1],[16, ...builtin('__Array_prototype_indexOf')],[26],[34,11],[68,0,0,0,0,0,0,240,191],[97],[4,64],[68,0,0,0,0,0,0,0,0],[65,128,1],[15],[11],[32,0],[32,1],[16, ...builtin('__Object_values')],[26],[34,12],[33,13],[32,11],[34,14],[252,3],[65,9],[108],[32,13],[252,3],[106],[34,15],[43,0,4],[32,15],[45,0,12],[34,4],[15]],
|
1846
|
+
params: [124,127,124,127], typedParams: 1,
|
1847
|
+
returns: [124,127], typedReturns: 1,
|
1848
|
+
locals: [127,124,127,124,127,124,124,124,124,124,124,127,127], localNames: ["target","target#type","property","property#type","#last_type","#logicinner_tmp","#typeswitch_tmp","p","p#type","t","keys","idx","vals","#member_obj","#member_prop","#loadArray_offset","#swap"],
|
1849
|
+
};
|
1850
|
+
this.__Reflect_has = {
|
1851
|
+
wasm: (scope, {builtin,internalThrow}) => [[32,0],[252,2],[32,1],[16, ...builtin('__Porffor_object_isObject')],[33,4],[183],[33,5],[32,4],[33,6],[2,124],[32,6],[65,195,0],[70],[4,64,"TYPESWITCH|String"],[32,5],[252,3],[40,1,0],[69],[184],[12,1],[11],[32,6],[65,195,1],[70],[4,64,"TYPESWITCH|ByteString"],[32,5],[252,3],[40,1,0],[69],[184],[12,1],[11],[32,5],[68,0,0,0,0,0,0,0,0],[97],[184],[11,"TYPESWITCH_end"],[252,3],[4,64],...internalThrow(scope, 'TypeError', `Target is a non-object`),[11],[32,2],[32,3],[16, ...builtin('__ecma262_ToPropertyKey')],[33,8],[33,7],[32,0],[32,1],[16, ...builtin('__Porffor_rawType')],[34,9],[68,0,0,0,0,0,0,28,64],[97],[4,64],[32,0],[252,2],[32,1],[32,7],[252,2],[32,8],[16, ...builtin('__Porffor_object_lookup')],[33,4],[183],[68,0,0,0,0,0,0,240,191],[98],[184],[65,2],[15],[11],[32,0],[32,1],[16, ...builtin('__Object_keys')],[26],[34,10],[65,208,0],[32,7],[32,8],[68,0,0,0,0,0,0,0,0],[65,128,1],[16, ...builtin('__Array_prototype_includes')],[34,4],[15]],
|
1852
|
+
params: [124,127,124,127], typedParams: 1,
|
1853
|
+
returns: [124,127], typedReturns: 1,
|
1854
|
+
locals: [127,124,127,124,127,124,124], localNames: ["target","target#type","property","property#type","#last_type","#logicinner_tmp","#typeswitch_tmp","p","p#type","t","keys"],
|
1855
|
+
};
|
1838
1856
|
this.__Porffor_set_read = {
|
1839
1857
|
wasm: (scope, {}) => [[32,2],[252,3],[65,9],[108],[32,0],[252,3],[106],[34,4],[43,0,4],[32,4],[45,0,12],[15]],
|
1840
1858
|
params: [124,127,124,127], typedParams: 1,
|
package/compiler/havoc.js
CHANGED
@@ -90,4 +90,29 @@ export const f64ToI32s = (func, targets) => {
|
|
90
90
|
}
|
91
91
|
}
|
92
92
|
}
|
93
|
+
};
|
94
|
+
|
95
|
+
export const withLocalDomains = (func, targets, domains) => {
|
96
|
+
const { wasm } = func;
|
97
|
+
|
98
|
+
// update wasm
|
99
|
+
for (let i = 0; i < wasm.length; i++) {
|
100
|
+
const op = wasm[i];
|
101
|
+
const opcode = op[0];
|
102
|
+
const idx = op[1];
|
103
|
+
|
104
|
+
let target;
|
105
|
+
if ((opcode === Opcodes.local_get || opcode === Opcodes.local_tee) &&
|
106
|
+
(target = targets.indexOf(idx)) !== -1) {
|
107
|
+
const n1 = wasm[i + 1];
|
108
|
+
if (n1?.[0] !== Opcodes.i32_const && n1?.[0] !== Opcodes.f64_const) continue;
|
109
|
+
|
110
|
+
const n2 = wasm[i + 2];
|
111
|
+
if (!n2?.[0]) continue;
|
112
|
+
|
113
|
+
switch (n2[0]) {
|
114
|
+
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
93
118
|
};
|
package/compiler/pgo.js
CHANGED
@@ -10,8 +10,13 @@ export const setup = () => {
|
|
10
10
|
importedFuncs[importedFuncs.profile2].params = [ Valtype.i32, valtypeBinary ];
|
11
11
|
|
12
12
|
// enable these prefs by default for pgo
|
13
|
-
|
14
|
-
|
13
|
+
for (const x of [
|
14
|
+
'typeswitchUniqueTmp', // use unique tmps for typeswitches
|
15
|
+
'lengthNoTmp', // use duplicated inline code instead of tmp for .length
|
16
|
+
'cyclone', // enable cyclone pre-evaler
|
17
|
+
]) {
|
18
|
+
Prefs[x] = Prefs[x] === false ? false : true;
|
19
|
+
}
|
15
20
|
};
|
16
21
|
|
17
22
|
export const run = obj => {
|
@@ -158,8 +163,24 @@ export const run = obj => {
|
|
158
163
|
return true;
|
159
164
|
});
|
160
165
|
|
166
|
+
const domains = localData[i].map((x, j) => {
|
167
|
+
if (j < func.params.length) return false; // param
|
168
|
+
if (consistents[j] !== false) return false; // already consistent
|
169
|
+
if (x.length < 2) return false; // <2 samples
|
170
|
+
|
171
|
+
let min = Infinity, max = -Infinity;
|
172
|
+
for (const y of x) {
|
173
|
+
if (y < min) min = y;
|
174
|
+
if (y > max) max = y;
|
175
|
+
}
|
176
|
+
|
177
|
+
counts[2]++;
|
178
|
+
return [ min, max ];
|
179
|
+
});
|
180
|
+
|
161
181
|
func.consistents = consistents;
|
162
182
|
func.integerOnlyF64s = integerOnlyF64s;
|
183
|
+
func.domains = domains;
|
163
184
|
|
164
185
|
log += ` ${func.name}: identified ${counts[0]}/${total} locals as consistent${Prefs.verbosePgo ? ':' : ''}\n`;
|
165
186
|
if (Prefs.verbosePgo) {
|
@@ -174,12 +195,20 @@ export const run = obj => {
|
|
174
195
|
if (localValues[j].type !== Valtype.f64) continue;
|
175
196
|
log += ` ${integerOnlyF64s[j] ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m\n`;
|
176
197
|
}
|
198
|
+
}
|
199
|
+
|
200
|
+
log += ` ${func.name}: identified ${counts[2]}/${total} local non-consistent domains${Prefs.verbosePgo ? ':' : ''}\n`;
|
201
|
+
if (Prefs.verbosePgo) {
|
202
|
+
for (let j = func.params.length; j < localData[i].length; j++) {
|
203
|
+
if (domains[j] === false) continue;
|
204
|
+
log += ` ${domains[j] !== false ? '\u001b[92m' : '\u001b[91m'}${localKeys[j]}\u001b[0m: ${domains[j] !== false ? `${domains[j][0]} ≤ x ≤ ${domains[j][1]}` : ''}\n`;
|
205
|
+
}
|
177
206
|
|
178
207
|
log += '\n';
|
179
208
|
}
|
180
209
|
}
|
181
210
|
|
182
|
-
time(2, 'processed PGO data' + log);
|
211
|
+
time(2, 'processed PGO data\n' + log);
|
183
212
|
time(3, 'optimizing using PGO data...');
|
184
213
|
|
185
214
|
log = '';
|
@@ -216,5 +245,5 @@ export const run = obj => {
|
|
216
245
|
if (targets.length > 0) Havoc.localsToConsts(wasmFunc, targets, consts, { localKeys: x.localKeys });
|
217
246
|
}
|
218
247
|
|
219
|
-
time(3, 'optimized using PGO data' + log);
|
248
|
+
time(3, 'optimized using PGO data\n' + log);
|
220
249
|
};
|
package/package.json
CHANGED