Skip to content

Commit 9090ce5

Browse files
authored
Stop propagating/inlining string constants (WebAssembly#6234)
This causes overhead atm since in VMs executing a string.const will actually allocate a string, and more copies means more allocations. For now, just do not add more. This required changes to two passes: SimplifyGlobals and Precompute.
1 parent de223c5 commit 9090ce5

File tree

4 files changed

+92
-9
lines changed

4 files changed

+92
-9
lines changed

src/passes/Precompute.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -799,9 +799,11 @@ struct Precompute
799799
if (type.isFunction()) {
800800
return true;
801801
}
802-
// We can emit a StringConst for a string constant.
802+
// We can emit a StringConst for a string constant in principle, but we do
803+
// not want to as that might cause more allocations to happen. See similar
804+
// code in SimplifyGlobals.
803805
if (type.isString()) {
804-
return true;
806+
return false;
805807
}
806808
// All other reference types cannot be precomputed. Even an immutable GC
807809
// reference is not currently something this pass can handle, as it will

src/passes/SimplifyGlobals.cpp

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ namespace wasm {
5252

5353
namespace {
5454

55+
// Checks if an expression is a constant, and one that we can copy without
56+
// downsides. This is the set of constant values that we will inline from
57+
// globals.
58+
bool isCopyableConstant(Expression* curr) {
59+
// Anything that is truly constant is suitable for us, *except* for string
60+
// constants, which in VMs may be implemented not as a constant but as an
61+
// allocation. We prefer to keep string.const in globals where any such
62+
// allocation only happens once (note that that makes them equivalent to
63+
// strings imported from JS, which would be in imported globals, which are
64+
// similarly not optimizable).
65+
// TODO: revisit this if/when VMs implement and optimize string.const.
66+
return Properties::isConstantExpression(curr) && !curr->is<StringConst>();
67+
}
68+
5569
struct GlobalInfo {
5670
// Whether the global is imported and exported.
5771
bool imported = false;
@@ -358,7 +372,7 @@ struct ConstantGlobalApplier
358372

359373
void visitExpression(Expression* curr) {
360374
if (auto* set = curr->dynCast<GlobalSet>()) {
361-
if (Properties::isConstantExpression(set->value)) {
375+
if (isCopyableConstant(set->value)) {
362376
currConstantGlobals[set->name] =
363377
getLiteralsFromConstExpression(set->value);
364378
} else {
@@ -369,7 +383,7 @@ struct ConstantGlobalApplier
369383
// Check if the global is known to be constant all the time.
370384
if (constantGlobals->count(get->name)) {
371385
auto* global = getModule()->getGlobal(get->name);
372-
assert(Properties::isConstantExpression(global->init));
386+
assert(isCopyableConstant(global->init));
373387
replaceCurrent(ExpressionManipulator::copy(global->init, *getModule()));
374388
replaced = true;
375389
return;
@@ -671,7 +685,7 @@ struct SimplifyGlobals : public Pass {
671685
// go, as well as applying them where possible.
672686
for (auto& global : module->globals) {
673687
if (!global->imported()) {
674-
if (Properties::isConstantExpression(global->init)) {
688+
if (isCopyableConstant(global->init)) {
675689
constantGlobals[global->name] =
676690
getLiteralsFromConstExpression(global->init);
677691
} else {
@@ -695,7 +709,7 @@ struct SimplifyGlobals : public Pass {
695709
NameSet constantGlobals;
696710
for (auto& global : module->globals) {
697711
if (!global->mutable_ && !global->imported() &&
698-
Properties::isConstantExpression(global->init)) {
712+
isCopyableConstant(global->init)) {
699713
constantGlobals.insert(global->name);
700714
}
701715
}

test/lit/passes/precompute-gc.wast

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,18 +1021,20 @@
10211021
;; CHECK-NEXT: (string.const "hello, world")
10221022
;; CHECK-NEXT: )
10231023
;; CHECK-NEXT: (call $strings
1024-
;; CHECK-NEXT: (string.const "hello, world")
1024+
;; CHECK-NEXT: (local.get $s)
10251025
;; CHECK-NEXT: )
10261026
;; CHECK-NEXT: (call $strings
1027-
;; CHECK-NEXT: (string.const "hello, world")
1027+
;; CHECK-NEXT: (local.get $s)
10281028
;; CHECK-NEXT: )
10291029
;; CHECK-NEXT: )
10301030
(func $strings (param $param (ref string))
10311031
(local $s (ref string))
10321032
(local.set $s
10331033
(string.const "hello, world")
10341034
)
1035-
;; The constant string should be propagated twice, to both of these calls.
1035+
;; The constant string could be propagated twice, to both of these calls, but
1036+
;; we do not do so as it might cause more allocations to happen.
1037+
;; TODO if VMs optimize that, handle it
10361038
(call $strings
10371039
(local.get $s)
10381040
)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
2+
;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up.
3+
4+
;; We do not "inline" strings from globals, as that might cause more
5+
;; allocations to happen. TODO if VMs optimize that, remove this
6+
7+
;; RUN: foreach %s %t wasm-opt --simplify-globals -all -S -o - | filecheck %s
8+
9+
;; Also test with -O3 --gufa to see that no other pass does this kind of thing,
10+
;; as extra coverage.
11+
12+
;; RUN: foreach %s %t wasm-opt -O3 --gufa -all -S -o - | filecheck %s --check-prefix=O3GUF
13+
14+
(module
15+
;; CHECK: (type $0 (func (result anyref)))
16+
17+
;; CHECK: (global $string stringref (string.const "one"))
18+
;; O3GUF: (type $0 (func (result anyref)))
19+
20+
;; O3GUF: (global $string (ref string) (string.const "one"))
21+
(global $string stringref (string.const "one"))
22+
23+
;; CHECK: (global $string-mut (mut stringref) (string.const "two"))
24+
;; O3GUF: (global $string-mut (mut (ref string)) (string.const "two"))
25+
(global $string-mut (mut stringref) (string.const "two"))
26+
27+
;; CHECK: (export "global" (func $global))
28+
;; O3GUF: (export "global" (func $global))
29+
(export "global" (func $global))
30+
31+
;; CHECK: (export "written" (func $written))
32+
;; O3GUF: (export "written" (func $written))
33+
(export "written" (func $written))
34+
35+
;; CHECK: (func $global (type $0) (result anyref)
36+
;; CHECK-NEXT: (global.get $string)
37+
;; CHECK-NEXT: )
38+
;; O3GUF: (func $global (type $0) (result anyref)
39+
;; O3GUF-NEXT: (global.get $string)
40+
;; O3GUF-NEXT: )
41+
(func $global (result anyref)
42+
;; This should not turn into "one".
43+
(global.get $string)
44+
)
45+
46+
;; CHECK: (func $written (type $0) (result anyref)
47+
;; CHECK-NEXT: (global.set $string-mut
48+
;; CHECK-NEXT: (string.const "three")
49+
;; CHECK-NEXT: )
50+
;; CHECK-NEXT: (global.get $string-mut)
51+
;; CHECK-NEXT: )
52+
;; O3GUF: (func $written (type $0) (result anyref)
53+
;; O3GUF-NEXT: (global.set $string-mut
54+
;; O3GUF-NEXT: (string.const "three")
55+
;; O3GUF-NEXT: )
56+
;; O3GUF-NEXT: (global.get $string-mut)
57+
;; O3GUF-NEXT: )
58+
(func $written (result anyref)
59+
(global.set $string-mut
60+
(string.const "three")
61+
)
62+
;; This should not turn into "three".
63+
(global.get $string-mut)
64+
)
65+
)

0 commit comments

Comments
 (0)