Skip to content

Optimize parameter usage based on whole world knowledge #40488

Open
@mkustermann

Description

@mkustermann

We should

  • Remove parameters if they are never passed
  • Remove parameters they are never used
  • Remove parameters if TFA inferred the value to be constant
  • Make parameters mandatory if they are passed on every call site
  • Make parameters mandatory if we believe it benefits code size (and re-write call sites to pass the default value): We can have a heuristic for doing this.

Update:

  • Do the same for type arguments (if caller passes always same value, ...)

if certain conditions are met (e.g. we know all call sites, ...)

Here's a concrete example: We should turn this

main() {
  final l = <Base>[A(), B(), C()];
  l[0].foo(alwaysPassed: 'a', constant: 10);
  l[1].foo(alwaysPassed: 'b');
  l[2].foo(alwaysPassed: 'c', unused: bar());
}
abstract class Base {
  foo({alwaysPassed, constant: 10, unused});
}
class A extends Base {
  foo({alwaysPassed, constant: 10, unused}) =>
      print('A($constant $alwaysPassed)');
}
class B extends Base {
  foo({alwaysPassed, constant: 10, unused}) =>
      print('B($constant $alwaysPassed)');
}
class C extends Base {
  foo({alwaysPassed, constant: 10, unused}) =>
      print('C($constant $alwaysPassed)');
}
@pragma('vm:never-inline')
bar() {}

into this

main() {
  final l = <Base>[A(), B(), C()];
  l[0].foo('a');
  l[1].foo('b');

  final tmp = l[2];
  bar();
  tmp.foo('c');
}
abstract class Base {
  foo(alwaysPassed);
}
class A extends Base {
  foo(alwaysPassed) => print('A(${10} $alwaysPassed)');
}
class B extends Base {
  foo(alwaysPassed) => print('B(${10} $alwaysPassed)');
}
class C extends Base {
  foo(alwaysPassed) => print('C(${10} $alwaysPassed)');
}
@pragma('vm:never-inline')
bar() {}

That would reduce

*** BEGIN CFG
After SerializeGraph
==== file:///.../test.dart_A_foo
B0[graph]:0 {
      v0 <- Constant(#null) T{Null?}
      v1 <- Constant(#<optimized out>) T{_OneByteString}
      v8 <- Constant(#1) [1, 1] T{_Smi}
      v11 <- Constant(#true) T{bool}
      v22 <- Constant(#0) [0, 0] T{_Smi}
      v23 <- Constant(#2) [2, 2] T{_Smi}
      v26 <- Constant(#3) [3, 3] T{_Smi}
      v31 <- Constant(#alwaysPassed) T{_OneByteString}
      v42 <- Constant(#constant) T{_OneByteString}
      v43 <- Constant(#10) [10, 10] T{_Smi}
      v52 <- Constant(#unused) T{_OneByteString}
      v55 <- Constant(#5) [5, 5] T{_Smi}
      v58 <- Constant(#A() T{_OneByteString}
      v59 <- Constant(# ) T{_OneByteString}
      v60 <- Constant(#4) [4, 4] T{_Smi}
      v61 <- Constant(#)) T{_OneByteString}
}
B1[function entry]:2 {
      v2 <- SpecialParameter(ArgDescriptor) T{_ImmutableList}
}
  0:     v4 <- LoadField(v2 . ArgumentsDescriptor.positional_count {final}) [0, 4611686018427387903] T{_Smi}
  1:     v6 <- LoadField(v2 . ArgumentsDescriptor.count {final}) [0, 4611686018427387903] T{_Smi}
    Branch if RelationalOp:6(<=, v8, v4) T{bool} goto (3, 4)
B3[target]:12
    Branch if RelationalOp:16(<=, v4, v8) T{bool} goto (5, 6)
B5[target]:22
    v18 <- BinarySmiOp:30(- [tr], v6, v8) [-1, 4611686018427387902] T{_Smi}
  2:     v29 <- LoadIndexed(v2, v26) T{*?}
    Branch if StrictCompare:36(===, v29, v31) goto (7, 8)
B7[target]:40
  3:     v86 <- LoadIndexed(v2, v60) T{*?}
    v88 <- BinarySmiOp:48(- [tr], v6, v86) [-4611686018427387903, 4611686018427387903] T{_Smi}
    v90 <- LoadIndexedUnsafe(rbp[v88 + 8]) T{*?}
    goto:52 B9
B8[target]:42
    goto:54 B9
B9[join]:44 pred(B7, B8) {
      v32 <- phi(v90, v0) alive T{*?}
      v34 <- phi(v8, v22) alive [0, 1] T{_Smi}
}
    v94 <- BinarySmiOp:56(<< [tr], v34, v8) [0, 2] T{_Smi}
    v38 <- BinarySmiOp:58(+ [tr], v94, v26) [3, 5] T{_Smi}
  4:     v40 <- LoadIndexed(v2, v38) T{*?}
    Branch if StrictCompare:60(===, v40, v42) goto (10, 11)
B10[target]:64
    v82 <- BinarySmiOp:74(+ [tr], v34, v8) [1, 2] T{_Smi}
    goto:76 B12
B11[target]:66
    goto:78 B12
B12[join]:68 pred(B10, B11) {
      v44 <- phi(v82, v34) alive [0, 2] T{_Smi}
}
    v96 <- BinarySmiOp:80(<< [tr], v44, v8) [0, 4] T{_Smi}
    v48 <- BinarySmiOp:82(+ [tr], v96, v26) [3, 7] T{_Smi}
  5:     v50 <- LoadIndexed(v2, v48) T{*?}
    Branch if StrictCompare:84(===, v50, v52) goto (13, 14)
B13[target]:88
    v72 <- BinarySmiOp:98(+ [tr], v44, v8) [1, 3] T{_Smi}
    goto:100 B15
B14[target]:90
    goto:102 B15
B15[join]:92 pred(B13, B14) {
      v53 <- phi(v72, v44) alive [0, 3] T{_Smi}
}
    Branch if StrictCompare:104(===, v18, v53) goto (18, 17)
B18[target]:108
    CheckStackOverflow:118(stack=0, loop=0)
    v56 <- CreateArray:122(v0, v55) T{_List}
  6:     StoreIndexed(v56, v22, v58)
  7:     StoreIndexed(v56, v8, v43)
  8:     StoreIndexed(v56, v23, v59)
  9:     StoreIndexed(v56, v26, v32)
 10:     StoreIndexed(v56, v60, v61)
    v62 <- StringInterpolate:124(v56) T{String}
    StaticCall:126( print<0> v62, result_type = T{Null?})
    Return:130(v0)
B17[target]:110
    goto:112 B2
B6[target]:24
    goto:28 B2
B4[target]:14
    goto:26 B2
B2[join]:4 pred(B4, B6, B17)
    TailCall(CallClosureNoSuchMethod(v2))
*** END CFG

to

*** BEGIN CFG
==== file:///.../test.dart_A_foo
B0[graph]:0 {
      v0 <- Constant(#null) T{Null?}
      v1 <- Constant(#<optimized out>) T{_OneByteString}
      v4 <- Constant(#5) [5, 5] T{_Smi}
      v7 <- Constant(#0) [0, 0] T{_Smi}
      v8 <- Constant(#A() T{_OneByteString}
      v9 <- Constant(#1) [1, 1] T{_Smi}
      v10 <- Constant(#10) [10, 10] T{_Smi}
      v11 <- Constant(#2) [2, 2] T{_Smi}
      v12 <- Constant(# ) T{_OneByteString}
      v13 <- Constant(#3) [3, 3] T{_Smi}
      v14 <- Constant(#4) [4, 4] T{_Smi}
      v15 <- Constant(#)) T{_OneByteString}
}
B1[function entry]:2 {
      v2 <- Parameter(0) T{A}
      v3 <- Parameter(1) T{_OneByteString}
}
    CheckStackOverflow:8(stack=0, loop=0)
    v5 <- CreateArray:12(v0, v4) T{_List}
  0:     StoreIndexed(v5, v7, v8)
  1:     StoreIndexed(v5, v9, v10)
  2:     StoreIndexed(v5, v11, v12)
  3:     StoreIndexed(v5, v13, v3)
  4:     StoreIndexed(v5, v14, v15)
    v16 <- StringInterpolate:14(v5) T{String}
    StaticCall:16( print<0> v16, result_type = T{Null?})
    Return:20(v0)
*** END CFG

Given that flutter uses optional parameters pervasively this could give significant code size savings, and potential performance improvements.

@sjindel-google Can you take a look at this?

Metadata

Metadata

Labels

area-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.type-performanceIssue relates to performance or code sizevm-aot-code-sizeRelated to improvements in AOT code sizevm-tfa

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions