Skip to content

Commit 0461911

Browse files
committed
feat: optimize string literal tostack behavior
String literals are widely used static data, and our GC algorithm does not recycle them (see https://github.com/AssemblyScript/assemblyscript/blob/7e2a62d9615d2ae02b593af87ee4920a3c57b0bd/std/assembly/rt/itcms.ts#L244). Recording the addresses of all string literals and preventing the emission of tostack code can effectively improve performance
1 parent 7e2a62d commit 0461911

File tree

74 files changed

+3213
-17715
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+3213
-17715
lines changed

src/compiler.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ import {
5959
isConstNegZero,
6060
isConstExpressionNaN,
6161
ensureType,
62-
createType
62+
createType,
63+
getConstValueInteger
6364
} from "./module";
6465

6566
import {
@@ -451,6 +452,8 @@ export class Compiler extends DiagnosticEmitter {
451452
memorySegments: MemorySegment[] = [];
452453
/** Map of already compiled static string segments. */
453454
stringSegments: Map<string,MemorySegment> = new Map();
455+
/** Set of static GC object offsets. tostack is unnecessary for them. */
456+
staticGcObjectOffsets: Map<i32, Set<i32>> = new Map();
454457
/** Function table being compiled. First elem is blank. */
455458
functionTable: Function[] = [];
456459
/** Arguments length helper global. */
@@ -1939,7 +1942,16 @@ export class Compiler extends DiagnosticEmitter {
19391942
stringSegment = this.addRuntimeMemorySegment(buf);
19401943
segments.set(stringValue, stringSegment);
19411944
}
1942-
return i64_add(stringSegment.offset, i64_new(totalOverhead));
1945+
let stringOffset = i64_add(stringSegment.offset, i64_new(totalOverhead));
1946+
let staticGcObjectOffsets = this.staticGcObjectOffsets;
1947+
if (staticGcObjectOffsets.has(i64_high(stringOffset))) {
1948+
staticGcObjectOffsets.get(i64_high(stringOffset))!.add(i64_low(stringOffset));
1949+
} else {
1950+
let s = new Set<i32>();
1951+
s.add(i64_low(stringOffset));
1952+
staticGcObjectOffsets.set(i64_high(stringOffset), s);
1953+
}
1954+
return stringOffset;
19431955
}
19441956

19451957
/** Writes a series of static values of the specified type to a buffer. */
@@ -6754,6 +6766,21 @@ export class Compiler extends DiagnosticEmitter {
67546766
stub.set(CommonFlags.Compiled);
67556767
}
67566768

6769+
private needToStack(expr: ExpressionRef): bool {
6770+
const precomp = this.module.runExpression(expr, ExpressionRunnerFlags.Default);
6771+
// cannot precompute, so must go to stack
6772+
if (precomp == 0) return true;
6773+
const value = getConstValueInteger(precomp, this.options.isWasm64);
6774+
// zero constant doesn't need to go to stack
6775+
if (i64_eq(value, i64_zero)) return false;
6776+
// static GC objects doesn't need to go to stack
6777+
let staticGcObjectOffsets = this.staticGcObjectOffsets;
6778+
if (staticGcObjectOffsets.has(i64_high(value))) {
6779+
if (staticGcObjectOffsets.get(i64_high(value))!.has(i64_low(value))) return false;
6780+
}
6781+
return true;
6782+
}
6783+
67576784
/** Marks managed call operands for the shadow stack. */
67586785
private operandsTostack(signature: Signature, operands: ExpressionRef[]): void {
67596786
if (!this.options.stackSize) return;
@@ -6763,8 +6790,7 @@ export class Compiler extends DiagnosticEmitter {
67636790
if (thisType) {
67646791
if (thisType.isManaged) {
67656792
let operand = operands[0];
6766-
let precomp = module.runExpression(operand, ExpressionRunnerFlags.Default);
6767-
if (!precomp || !isConstZero(precomp)) { // otherwise unnecessary
6793+
if (this.needToStack(operand)) {
67686794
operands[operandIndex] = module.tostack(operand);
67696795
}
67706796
}
@@ -6777,8 +6803,7 @@ export class Compiler extends DiagnosticEmitter {
67776803
let paramType = parameterTypes[parameterIndex];
67786804
if (paramType.isManaged) {
67796805
let operand = operands[operandIndex];
6780-
let precomp = module.runExpression(operand, ExpressionRunnerFlags.Default);
6781-
if (!precomp || !isConstZero(precomp)) { // otherwise unnecessary
6806+
if (this.needToStack(operand)) {
67826807
operands[operandIndex] = module.tostack(operand);
67836808
}
67846809
}

src/module.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3038,6 +3038,17 @@ export function getConstValueI64High(expr: ExpressionRef): i32 {
30383038
return binaryen._BinaryenConstGetValueI64High(expr);
30393039
}
30403040

3041+
export function getConstValueInteger(expr: ExpressionRef, isWasm64: bool): i64 {
3042+
let lo, hi = 0;
3043+
if (isWasm64) {
3044+
lo = getConstValueI64Low(expr);
3045+
hi = getConstValueI64High(expr);
3046+
} else {
3047+
lo = getConstValueI32(expr);
3048+
}
3049+
return i64_new(lo, hi);
3050+
}
3051+
30413052
export function getConstValueF32(expr: ExpressionRef): f32 {
30423053
return binaryen._BinaryenConstGetValueF32(expr);
30433054
}

tests/compiler/NonNullable.debug.wat

Lines changed: 3 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -357,28 +357,15 @@
357357
(local $0 i32)
358358
(local $1 i32)
359359
global.get $~lib/memory/__stack_pointer
360-
i32.const 12
360+
i32.const 8
361361
i32.sub
362362
global.set $~lib/memory/__stack_pointer
363363
call $~stack_check
364364
global.get $~lib/memory/__stack_pointer
365365
i64.const 0
366366
i64.store
367-
global.get $~lib/memory/__stack_pointer
368-
i32.const 0
369-
i32.store offset=8
370367
i32.const 32
371-
local.set $1
372-
global.get $~lib/memory/__stack_pointer
373-
local.get $1
374-
i32.store
375-
local.get $1
376368
i32.const 32
377-
local.set $1
378-
global.get $~lib/memory/__stack_pointer
379-
local.get $1
380-
i32.store offset=4
381-
local.get $1
382369
call $~lib/string/String.__eq
383370
i32.eqz
384371
if
@@ -390,17 +377,7 @@
390377
unreachable
391378
end
392379
i32.const 112
393-
local.set $1
394-
global.get $~lib/memory/__stack_pointer
395-
local.get $1
396-
i32.store
397-
local.get $1
398380
i32.const 112
399-
local.set $1
400-
global.get $~lib/memory/__stack_pointer
401-
local.get $1
402-
i32.store offset=4
403-
local.get $1
404381
call $~lib/string/String.__eq
405382
i32.eqz
406383
if
@@ -412,17 +389,7 @@
412389
unreachable
413390
end
414391
i32.const 144
415-
local.set $1
416-
global.get $~lib/memory/__stack_pointer
417-
local.get $1
418-
i32.store
419-
local.get $1
420392
i32.const 144
421-
local.set $1
422-
global.get $~lib/memory/__stack_pointer
423-
local.get $1
424-
i32.store offset=4
425-
local.get $1
426393
call $~lib/string/String.__eq
427394
i32.eqz
428395
if
@@ -436,7 +403,7 @@
436403
global.get $~lib/memory/__stack_pointer
437404
global.get $NonNullable/z
438405
local.tee $0
439-
i32.store offset=8
406+
i32.store offset=4
440407
local.get $0
441408
if (result i32)
442409
local.get $0
@@ -462,7 +429,7 @@
462429
local.get $1
463430
call $NonNullable/safetyCheck<~lib/string/String|null>
464431
global.get $~lib/memory/__stack_pointer
465-
i32.const 12
432+
i32.const 8
466433
i32.add
467434
global.set $~lib/memory/__stack_pointer
468435
)

tests/compiler/NonNullable.release.wat

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
(start $~start)
2323
(func $~start
2424
global.get $~lib/memory/__stack_pointer
25-
i32.const 12
25+
i32.const 8
2626
i32.sub
2727
global.set $~lib/memory/__stack_pointer
2828
block $folding-inner0
@@ -33,15 +33,6 @@
3333
global.get $~lib/memory/__stack_pointer
3434
i64.const 0
3535
i64.store
36-
global.get $~lib/memory/__stack_pointer
37-
i32.const 0
38-
i32.store offset=8
39-
global.get $~lib/memory/__stack_pointer
40-
i32.const 1056
41-
i32.store
42-
global.get $~lib/memory/__stack_pointer
43-
i32.const 1056
44-
i32.store offset=4
4536
i32.const 1056
4637
i32.const 1056
4738
call $~lib/string/String.__eq
@@ -54,12 +45,6 @@
5445
call $~lib/builtins/abort
5546
unreachable
5647
end
57-
global.get $~lib/memory/__stack_pointer
58-
i32.const 1136
59-
i32.store
60-
global.get $~lib/memory/__stack_pointer
61-
i32.const 1136
62-
i32.store offset=4
6348
i32.const 1136
6449
i32.const 1136
6550
call $~lib/string/String.__eq
@@ -72,12 +57,6 @@
7257
call $~lib/builtins/abort
7358
unreachable
7459
end
75-
global.get $~lib/memory/__stack_pointer
76-
i32.const 1168
77-
i32.store
78-
global.get $~lib/memory/__stack_pointer
79-
i32.const 1168
80-
i32.store offset=4
8160
i32.const 1168
8261
i32.const 1168
8362
call $~lib/string/String.__eq
@@ -92,7 +71,7 @@
9271
end
9372
global.get $~lib/memory/__stack_pointer
9473
i32.const 1248
95-
i32.store offset=8
74+
i32.store offset=4
9675
global.get $~lib/memory/__stack_pointer
9776
i32.const 1248
9877
i32.store
@@ -126,7 +105,7 @@
126105
i32.add
127106
global.set $~lib/memory/__stack_pointer
128107
global.get $~lib/memory/__stack_pointer
129-
i32.const 12
108+
i32.const 8
130109
i32.add
131110
global.set $~lib/memory/__stack_pointer
132111
return

tests/compiler/bindings/esm.debug.wat

Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,27 @@
124124
(export "functionFunction" (func $export:bindings/esm/functionFunction))
125125
(func $start:bindings/esm~anonymous|0
126126
)
127+
(func $start:bindings/esm
128+
i32.const 128
129+
i32.const 1
130+
f64.const 42
131+
f64.const 0
132+
f64.const 0
133+
f64.const 0
134+
f64.const 0
135+
call $~lib/builtins/trace
136+
i32.const 160
137+
call $~lib/bindings/dom/console.log
138+
global.get $~lib/bindings/dom/Math.E
139+
call $~lib/bindings/dom/Math.log
140+
drop
141+
global.get $bindings/esm/immutableGlobal
142+
drop
143+
global.get $bindings/esm/immutableGlobalNested
144+
drop
145+
call $bindings/esm/Date_getTimezoneOffset
146+
drop
147+
)
127148
(func $bindings/esm/plainFunction (param $a i32) (param $b i32) (result i32)
128149
local.get $a
129150
local.get $b
@@ -3081,50 +3102,6 @@
30813102
unreachable
30823103
end
30833104
)
3084-
(func $start:bindings/esm
3085-
(local $0 i32)
3086-
global.get $~lib/memory/__stack_pointer
3087-
i32.const 4
3088-
i32.sub
3089-
global.set $~lib/memory/__stack_pointer
3090-
call $~stack_check
3091-
global.get $~lib/memory/__stack_pointer
3092-
i32.const 0
3093-
i32.store
3094-
i32.const 128
3095-
local.set $0
3096-
global.get $~lib/memory/__stack_pointer
3097-
local.get $0
3098-
i32.store
3099-
local.get $0
3100-
i32.const 1
3101-
f64.const 42
3102-
f64.const 0
3103-
f64.const 0
3104-
f64.const 0
3105-
f64.const 0
3106-
call $~lib/builtins/trace
3107-
i32.const 160
3108-
local.set $0
3109-
global.get $~lib/memory/__stack_pointer
3110-
local.get $0
3111-
i32.store
3112-
local.get $0
3113-
call $~lib/bindings/dom/console.log
3114-
global.get $~lib/bindings/dom/Math.E
3115-
call $~lib/bindings/dom/Math.log
3116-
drop
3117-
global.get $bindings/esm/immutableGlobal
3118-
drop
3119-
global.get $bindings/esm/immutableGlobalNested
3120-
drop
3121-
call $bindings/esm/Date_getTimezoneOffset
3122-
drop
3123-
global.get $~lib/memory/__stack_pointer
3124-
i32.const 4
3125-
i32.add
3126-
global.set $~lib/memory/__stack_pointer
3127-
)
31283105
(func $bindings/esm/bufferFunction (param $a i32) (param $b i32) (result i32)
31293106
(local $aByteLength i32)
31303107
(local $bByteLength i32)

0 commit comments

Comments
 (0)