Skip to content

Missed optimization opportunity in TFA in when an unused class is referenced in a try-catch type test #53800

Open
@osa1

Description

@osa1

Extracted from the test language/exception/throw2_test:

import "package:expect/expect.dart";

abstract class TestException {
  String getMessage();
}

class MyException implements TestException {
  const MyException([String message = ""]) : message_ = message;

  @override
  String getMessage() {
    return message_;
  }

  final String message_;
}

class MyException2 implements TestException {
  const MyException2([String message = ""]) : message_ = message;

  @override
  String getMessage() {
    return message_;
  }

  final String message_;
}

class MyException3 implements TestException {
  const MyException3([String message = ""]) : message_ = message;

  @override
  String getMessage() {
    return message_;
  }

  final String message_;
}

class Helper {
  static int f1(int i) {
    try {
      int j;
      j = func();
    } on MyException3 catch (exception) {
      i = 100;
      print(exception.getMessage());
    } on MyException2 catch (exception) {
      try {
        i = func2();
        i = 200;
      } on TestException catch (exception) {
        i = 50;
      }
      print(exception.getMessage());
    } on MyException catch (exception) {
      i = func2();
      print(exception.getMessage());
    } finally {
      i = i + 800;
    }
    return i;
  }

  static int func() {
    int i = 0;
    while (i < 10) {
      i++;
    }
    if (i > 0) {
      throw new MyException2("Test for exception being thrown");
    }
    return i;
  }

  static int func2() {
    int i = 0;
    while (i < 10) {
      i++;
    }
    if (i > 0) {
      throw new MyException2("Test for exception being thrown");
    }
    return i;
  }
}


void main() {
  Expect.equals(850, Helper.f1(1));
}

Here MyException3 is never allocated, but TFA doesn't drop the catch block for it. Relevant parts of the kernel:

[@vm.unboxing-info.metadata=(i)->i]    static method f1([@vm.inferred-arg-type.metadata=dart.core::_Smi (value: 1)] core::int i) → core::int {
      try
        try {
          core::int j;
          j = test::Helper::func();
        }
        on test::MyException3 catch(final test::MyException3 exception) {
          i = 100;
          block {
            [@vm.inferred-type.metadata=! (skip check)] exception.{test::MyException3::getMessage}(){() → core::String};
          } =>throw "Attempt to execute code removed by Dart AOT compiler (TFA)";
        }

As a result the class also can't be dropped, though TFA turns it into an abstract class with the getMessage also turned into an abstract method:

  abstract class MyException3 extends core::Object implements test::TestException /*hasConstConstructor*/  {
[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasNonThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:6,getterSelectorId:7]    abstract method getMessage() → core::String;
  }

I suspect, the use of MyException3 in on MyException3 catch ... is treated the same way as e.g. List<MyException3> list = [], however unlike in the list type, the type on MyException3 ... does not need a runtime representation of the type MyException3 to exist, if it doesn't already exist it means the on ... can just be dropped. Similar to x is MyException3, which should be compiled to false (instead of making MyException3 available in runtime).

cc @alexmarkov @mkustermann

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3A lower priority bug or feature requestarea-vmUse area-vm for VM related issues, including code coverage, and the AOT and JIT backends.triagedIssue has been triaged by sub teamvm-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