[dart2wasm] Special case async functions with simple bodies

When an async function directly returns a `const` value, a basic
literal, or implicit `null` (empty body), omit the state machine loop
and exception handling.

See the new test for the cases this simplifies.

Issue: https://github.com/dart-lang/sdk/issues/60433
Change-Id: I25c25cb42c02cc3a9155e01ce205f63b567a1008
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/419840
Reviewed-by: Martin Kustermann <kustermann@google.com>
diff --git a/pkg/dart2wasm/lib/async.dart b/pkg/dart2wasm/lib/async.dart
index 7cc64d6..a414483 100644
--- a/pkg/dart2wasm/lib/async.dart
+++ b/pkg/dart2wasm/lib/async.dart
@@ -191,6 +191,22 @@
   void generateInner(FunctionNode functionNode, Context? context) {
     // void Function(_AsyncSuspendState, Object?, Object?, StackTrace?)
 
+    // Special cases for function bodies with that directly return a constant
+    // expression or basic literal. These functions can't throw an exception,
+    // and they will have only one state so the `br_table` loop below can also
+    // be omitted.
+    //
+    // Note: these functions currently have two states instead of one, as we
+    // always generate a state for implicitly returning `null`. With #55939 we
+    // will be able to remove the implicit return state. After that we can check
+    // the function bodies to check whether we need exception handling, and
+    // special-case single-state state machines to omit the `br_table` loop.
+    final functionBody = functionNode.body!;
+    final simpleReturn = _getSimpleReturn(functionBody);
+    if (simpleReturn != null && _handleSimpleReturn(simpleReturn)) {
+      return;
+    }
+
     // Set up locals for contexts and `this`.
     thisLocal = null;
     Context? localContext = context;
@@ -255,7 +271,7 @@
         thisLocal,
         cloneContextFor: functionNode);
 
-    translateStatement(functionNode.body!);
+    translateStatement(functionBody);
 
     // Final state: return.
     emitTargetLabel(targets.last);
@@ -414,4 +430,48 @@
           b, _awaitValueLocal.type, translateType(awaitValueVar.type));
     });
   }
+
+  bool _handleSimpleReturn(Expression returnedExpression) {
+    if (returnedExpression is! BasicLiteral &&
+        returnedExpression is! ConstantExpression) {
+      return false;
+    }
+
+    b.local_get(_suspendStateLocal);
+    b.struct_get(
+        asyncSuspendStateInfo.struct, FieldIndex.asyncSuspendStateCompleter);
+    returnedExpression.accept1(
+      this,
+      asyncSuspendStateInfo.struct
+          .fields[FieldIndex.asyncSuspendStateCurrentReturnValue].type.unpacked,
+    );
+    call(translator.getFunctionEntry(translator.completerComplete.reference,
+        uncheckedEntry: true));
+    b.return_();
+    b.end(); // inner function
+    return true;
+  }
+}
+
+Expression? _getSimpleReturn(Statement functionBody) {
+  if (functionBody is ReturnStatement) {
+    if (functionBody.expression == null) {
+      return NullLiteral();
+    }
+    return functionBody.expression;
+  }
+
+  if (functionBody is Block) {
+    if (functionBody.statements.isEmpty) {
+      return NullLiteral();
+    }
+    if (functionBody.statements.length == 1) {
+      final statement = functionBody.statements.single;
+      if (statement is ReturnStatement) {
+        return statement.expression;
+      }
+    }
+  }
+
+  return null;
 }
diff --git a/tests/language/async/dart2wasm_const_optimization_test.dart b/tests/language/async/dart2wasm_const_optimization_test.dart
new file mode 100644
index 0000000..5711815
--- /dev/null
+++ b/tests/language/async/dart2wasm_const_optimization_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2025, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// dart2wasm special-cases async functions with some simple bodies. Check that
+// calling these functions still creates a microtask, and awaiting them yields
+// to the scheduler.
+
+import 'dart:async';
+
+import 'package:expect/expect.dart';
+
+f1() async => const ["a", "b", "c"];
+
+f2() async {
+  return const ["x", "y", "z"];
+}
+
+f3() async {}
+
+f4() async => "in f4";
+
+f5() async {
+  return "in f5";
+}
+
+checkScheduling(f, checkExpectedValue) async {
+  final List<int> log = [];
+  scheduleMicrotask(() {
+    log.add(1);
+  });
+  log.add(0);
+  checkExpectedValue(await f());
+  log.add(2);
+  Expect.listEquals([0, 1, 2], log);
+}
+
+void main() async {
+  checkScheduling(f1, (value) => Expect.listEquals(["a", "b", "c"], value));
+  checkScheduling(f2, (value) => Expect.listEquals(["x", "y", "z"], value));
+  checkScheduling(f3, (value) => Expect.equals(null, value));
+  checkScheduling(f4, (value) => Expect.equals("in f4", value));
+  checkScheduling(f5, (value) => Expect.equals("in f5", value));
+}