blob: bd119597fe0e8df309984b138421e655ea99ed2b [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 '../../cfg/passes/lower_irreducible_control_flow.dart';
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;
String? failureReason;
if (member is Procedure) {
if (member.function.asyncMarker != AsyncMarker.Sync) {
failureReason = 'async';
}
if (StaticIntrinsic.fromProcedure(translator.coreTypes, member) != null ||
MemberIntrinsic.fromProcedure(translator.coreTypes, member) != null) {
failureReason = 'an intrinsic';
}
if (member.isExternal) failureReason = 'external';
} else if (member is Field) {
if (!member.isInstanceMember) failureReason = 'static';
} else {
failureReason = 'not a procedure or field';
}
final hasCfgPragma = hasWasmCfgPragma(translator.coreTypes, member);
if (failureReason != null) {
if (hasCfgPragma) {
throw StateError(
'Member $member has @pragma("wasm:cfg") but is $failureReason.',
);
}
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 CfgMemberCodeGenerator implements CodeGenerator {
final AstCodeGenerator fallback;
final Reference reference;
CfgMemberCodeGenerator(this.fallback, this.reference);
Translator get translator => fallback.translator;
Member get member => fallback.enclosingMember;
EntryPoint get kind => reference.entryKind;
@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: reference.isGetter,
isSetter: reference.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(),
LowerIrreducibleControlFlow(),
ControlFlowOptimizations(),
]);
pipeline.run(graph);
final cfgToWasm = ProdCfgToWasm(
translator,
graph,
b,
paramLocals,
kind: kind,
);
if (!cfgToWasm.canTranslate()) {
return false;
}
cfgToWasm.generate();
return true;
}
}