| // Copyright (c) 2026, 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. | |
| import 'package:cfg/front_end/ast_to_ir.dart'; | |
| import 'package:cfg/front_end/computed_scopes.dart'; | |
| import 'package:cfg/ir/ssa_computation.dart'; | |
| import 'package:cfg/passes/constant_propagation.dart'; | |
| import 'package:cfg/passes/control_flow_optimizations.dart'; | |
| import 'package:cfg/passes/pass.dart'; | |
| import 'package:cfg/passes/simplification.dart'; | |
| import 'package:cfg/passes/value_numbering.dart'; | |
| import 'package:kernel/ast.dart'; | |
| import 'package:wasm_builder/wasm_builder.dart' as w; | |
| import '../../code_generator.dart'; | |
| import '../../intrinsics.dart'; | |
| import '../../reference_extensions.dart'; | |
| import '../../translator.dart'; | |
| import '../../util.dart'; | |
| import 'to_wasm.dart'; | |
| bool mayUseCfgToCompileMember(Translator translator, Member member) { | |
| if (!translator.options.useCfg) return false; | |
| if (member is! Procedure) return false; | |
| if (member.function.asyncMarker != AsyncMarker.Sync) return false; | |
| final hasCfgPragma = hasWasmCfgPragma(translator.coreTypes, member); | |
| if (member.isExternal || | |
| StaticIntrinsic.fromProcedure(translator.coreTypes, member) != null || | |
| MemberIntrinsic.fromProcedure(translator.coreTypes, member) != null) { | |
| if (hasCfgPragma) { | |
| throw StateError( | |
| 'Member $member has @pragma("wasm:cfg") but is ' | |
| '${member.isExternal ? 'external' : 'an intrinsic'}.', | |
| ); | |
| } | |
| return false; | |
| } | |
| // Avoid compiling platform libraries (`dart:*`) with CFG as the CFG | |
| // pipeline is still under development and only supports a subset of | |
| // constructs, unless explicitly requested via `@pragma('wasm:cfg')`. | |
| if (member.enclosingLibrary.importUri.isScheme('dart') && !hasCfgPragma) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| class CfgProcedureCodeGenerator implements CodeGenerator { | |
| final SynchronousProcedureCodeGenerator fallback; | |
| CfgProcedureCodeGenerator(this.fallback); | |
| Translator get translator => fallback.translator; | |
| Procedure get member => fallback.member; | |
| Reference get reference => fallback.reference; | |
| EntryPoint get kind => fallback.kind; | |
| @override | |
| void generate( | |
| w.InstructionsBuilder b, | |
| List<w.Local> paramLocals, | |
| w.Label? returnLabel, | |
| ) { | |
| assert(returnLabel == null); | |
| assert(translator.options.useCfg); | |
| final allowFallback = !hasWasmCfgPragma(translator.coreTypes, member); | |
| if (allowFallback) { | |
| try { | |
| if (_tryGenerate(b, paramLocals)) return; | |
| } catch (_) {} | |
| translator.cfgLog?.appendFailed(member); | |
| b.reset(); | |
| fallback.generate(b, paramLocals, returnLabel); | |
| } else { | |
| if (!_tryGenerate(b, paramLocals)) { | |
| throw StateError( | |
| 'Member $member ($reference) has @pragma("wasm:cfg") but failed to compile with CFG.', | |
| ); | |
| } | |
| } | |
| } | |
| bool _tryGenerate(w.InstructionsBuilder b, List<w.Local> paramLocals) { | |
| final cFunction = translator.functionRegistry.getFunction( | |
| member, | |
| isGetter: member.isGetter, | |
| isSetter: member.isSetter, | |
| ); | |
| final graph = AstToIr( | |
| cFunction, | |
| translator.functionRegistry, | |
| translator.recognizedMethods, | |
| onLocalFunction: (_) {}, | |
| enableAsserts: translator.options.enableAsserts, | |
| typeParametersStyle: .separateFunctionAndClassTypeParameters, | |
| scopes: ComputedScopes( | |
| member, | |
| enableAsserts: translator.options.enableAsserts, | |
| ), | |
| ).buildFlowGraph(); | |
| final pipeline = Pipeline([ | |
| SSAComputation(), | |
| ValueNumbering(simplification: Simplification()), | |
| ConstantPropagation(), | |
| ControlFlowOptimizations(), | |
| ]); | |
| pipeline.run(graph); | |
| final cfgToWasm = ProdCfgToWasm( | |
| translator, | |
| graph, | |
| b, | |
| paramLocals, | |
| kind: kind, | |
| ); | |
| if (!cfgToWasm.canTranslate()) { | |
| return false; | |
| } | |
| cfgToWasm.generate(); | |
| return true; | |
| } | |
| } |