blob: 1a7f7135d49523a1e9780417389d473062227894 [file] [edit]
// 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;
w.FunctionType get functionType => fallback.functionType;
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, returnLabel)) return;
} catch (_) {}
b.reset();
fallback.generate(b, paramLocals, returnLabel);
} else {
if (!_tryGenerate(b, paramLocals, returnLabel)) {
throw StateError(
'Member $member ($reference) has @pragma("wasm:cfg") but failed to compile with CFG.',
);
}
}
}
bool _tryGenerate(
w.InstructionsBuilder b,
List<w.Local> paramLocals,
w.Label? returnLabel,
) {
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 = CfgToWasm(
translator,
functionType,
member,
graph,
b,
paramLocals,
returnLabel,
kind,
);
if (!cfgToWasm.canTranslate()) {
return false;
}
cfgToWasm.generate();
return true;
}
}