[vm,dyn_modules] Pass additional libraries to the BytecodeGenerator.
In addition to passing in the libraries that should be visited to the bytecode generator, also pass the libraries which are needed by the component and either are not in the component or should not be visited (e.g., libraries from the platform when the platform is not included). This way, the bytecode generator can create appropriate LibraryIndexes to detect the use of `dart:` libraries which aren't already in VmTarget.extraIndexedLibraries, and thus in coreTypes.index (e.g., 'dart:ffi'). TEST=pkg/dart2bytecode Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-dyn-mac-debug-arm64-try Change-Id: Ic08f2022753950bf639c446c0b2594e1e269872a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/476760 Commit-Queue: Tess Strickland <sstrickl@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
a9d5b4d3ee
commit
b6c060a6ab
@@ -15,16 +15,17 @@ import 'package:kernel/target/targets.dart' show Target;
|
||||
import '../../vm/bin/kernel_service.dart' as kernel_service;
|
||||
|
||||
Uint8List _generateBytecode(
|
||||
Component component,
|
||||
List<Library> libraries,
|
||||
CoreTypes coreTypes,
|
||||
ClassHierarchy hierarchy,
|
||||
Target target,
|
||||
bool enableAsserts,
|
||||
) {
|
||||
Component component,
|
||||
List<Library> libraries,
|
||||
CoreTypes coreTypes,
|
||||
ClassHierarchy hierarchy,
|
||||
Target target,
|
||||
bool enableAsserts,
|
||||
{Set<Library> extraLoadedLibraries = const {}}) {
|
||||
final byteSink = new BytesSink();
|
||||
generateBytecode(component, byteSink,
|
||||
libraries: libraries,
|
||||
extraLoadedLibraries: extraLoadedLibraries,
|
||||
coreTypes: coreTypes,
|
||||
hierarchy: hierarchy,
|
||||
target: target,
|
||||
|
||||
@@ -65,14 +65,16 @@ void generateBytecode(
|
||||
required CoreTypes coreTypes,
|
||||
required ClassHierarchy hierarchy,
|
||||
required Target target,
|
||||
required Set<Library> extraLoadedLibraries,
|
||||
}) {
|
||||
Timeline.timeSync("generateBytecode", () {
|
||||
verifyBytecodeInstructionDeclarations();
|
||||
final typeEnvironment = TypeEnvironment(coreTypes, hierarchy);
|
||||
final pragmaParser = ConstantPragmaAnnotationParser(coreTypes, target);
|
||||
|
||||
final bytecodeGenerator = BytecodeGenerator(component, coreTypes, hierarchy,
|
||||
typeEnvironment, options, pragmaParser);
|
||||
final bytecodeGenerator = BytecodeGenerator(
|
||||
component, coreTypes, hierarchy, typeEnvironment, options, pragmaParser,
|
||||
libraries: libraries, extraLoadedLibraries: extraLoadedLibraries);
|
||||
for (Library library in libraries) {
|
||||
bytecodeGenerator.visitLibrary(library);
|
||||
}
|
||||
@@ -105,6 +107,8 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
final PragmaAnnotationParser pragmaParser;
|
||||
final RecognizedMethods recognizedMethods;
|
||||
final Map<Uri, Source> astUriToSource;
|
||||
final List<Library> libraries;
|
||||
final Set<Library> extraLoadedLibraries;
|
||||
final LibraryIndex ffiLibraryIndex;
|
||||
late StringTable stringTable;
|
||||
late ObjectTable objectTable;
|
||||
@@ -149,7 +153,9 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
ClassHierarchy hierarchy,
|
||||
TypeEnvironment typeEnvironment,
|
||||
BytecodeOptions options,
|
||||
PragmaAnnotationParser pragmaParser)
|
||||
PragmaAnnotationParser pragmaParser,
|
||||
{required List<Library> libraries,
|
||||
Set<Library> extraLoadedLibraries = const {}})
|
||||
: this._internal(
|
||||
component,
|
||||
coreTypes,
|
||||
@@ -157,6 +163,8 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
typeEnvironment,
|
||||
options,
|
||||
pragmaParser,
|
||||
libraries: libraries,
|
||||
extraLoadedLibraries: extraLoadedLibraries,
|
||||
StatefulStaticTypeContext.flat(typeEnvironment));
|
||||
|
||||
BytecodeGenerator._internal(
|
||||
@@ -166,10 +174,15 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
this.typeEnvironment,
|
||||
this.options,
|
||||
this.pragmaParser,
|
||||
this.staticTypeContext)
|
||||
this.staticTypeContext,
|
||||
{required this.libraries,
|
||||
required this.extraLoadedLibraries})
|
||||
: recognizedMethods = new RecognizedMethods(staticTypeContext),
|
||||
astUriToSource = component.uriToSource,
|
||||
ffiLibraryIndex = LibraryIndex(component, const ['dart:ffi']) {
|
||||
ffiLibraryIndex = coreTypes.index.containsLibrary('dart:ffi')
|
||||
? coreTypes.index
|
||||
: LibraryIndex.fromLibraries(
|
||||
{...libraries, ...extraLoadedLibraries}, const ['dart:ffi']) {
|
||||
bytecodeComponent = new Component(coreTypes);
|
||||
stringTable = bytecodeComponent.stringTable;
|
||||
objectTable = bytecodeComponent.objectTable;
|
||||
@@ -198,9 +211,9 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
|
||||
@override
|
||||
void visitClass(Class node) {
|
||||
isInDeeplyImmutableClass =
|
||||
pragmaParser.parsedPragmas<ParsedVmDeeplyImmutablePragma>(
|
||||
node.annotations).isNotEmpty;
|
||||
isInDeeplyImmutableClass = pragmaParser
|
||||
.parsedPragmas<ParsedVmDeeplyImmutablePragma>(node.annotations)
|
||||
.isNotEmpty;
|
||||
startMembers();
|
||||
visitList(node.constructors, this);
|
||||
visitList(node.procedures, this);
|
||||
@@ -1056,9 +1069,8 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
? ffiLibraryIndex.getTopLevelProcedure('dart:ffi', '_ffiCall')
|
||||
: null;
|
||||
|
||||
late Procedure ensureDeeplyImmutable =
|
||||
libraryIndex.getTopLevelProcedure('dart:_internal',
|
||||
'_ensureDeeplyImmutable');
|
||||
late Procedure ensureDeeplyImmutable = libraryIndex.getTopLevelProcedure(
|
||||
'dart:_internal', '_ensureDeeplyImmutable');
|
||||
|
||||
// Selector for implicit dynamic calls 'foo(...)' where
|
||||
// variable 'foo' has type 'dynamic'.
|
||||
@@ -1152,8 +1164,7 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
final int cpIndex = cp.addInstanceField(field);
|
||||
if (isInDeeplyImmutableClass) {
|
||||
// TODO(dartbug.com/61078): Use static type to avoid runtime check.
|
||||
_genDirectCall(
|
||||
ensureDeeplyImmutable, objectTable.getArgDescHandle(1), 1);
|
||||
_genDirectCall(ensureDeeplyImmutable, objectTable.getArgDescHandle(1), 1);
|
||||
}
|
||||
|
||||
asm.emitStoreFieldTOS(cpIndex);
|
||||
@@ -2515,13 +2526,15 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
currentLoopDepth = savedLoopDepth;
|
||||
asyncTryBlock = savedAsyncTryBlock;
|
||||
|
||||
bool capturesOnlyFinalNotLateVars =
|
||||
locals.capturesOnlyFinalNotLateVars;
|
||||
bool capturesOnlyFinalNotLateVars = locals.capturesOnlyFinalNotLateVars;
|
||||
|
||||
locals.leaveScope();
|
||||
|
||||
closure.code = new ClosureCode(asm.bytecode, asm.exceptionsTable,
|
||||
finalizeSourcePositions(), finalizeLocalVariables(),
|
||||
closure.code = new ClosureCode(
|
||||
asm.bytecode,
|
||||
asm.exceptionsTable,
|
||||
finalizeSourcePositions(),
|
||||
finalizeLocalVariables(),
|
||||
capturesOnlyFinalNotLateVars);
|
||||
|
||||
_popAssemblerState();
|
||||
|
||||
@@ -266,7 +266,8 @@ Future<int> runCompilerWithOptions({
|
||||
hierarchy: results.classHierarchy!,
|
||||
coreTypes: results.coreTypes!,
|
||||
options: bytecodeOptions,
|
||||
target: compilerOptions.target!);
|
||||
target: compilerOptions.target!,
|
||||
extraLoadedLibraries: results.loadedLibraries);
|
||||
await sink.close();
|
||||
if (bytecodeOptions.showBytecodeSizeStatistics) {
|
||||
BytecodeSizeStatistics.dump();
|
||||
|
||||
@@ -23,6 +23,7 @@ import 'package:kernel/class_hierarchy.dart';
|
||||
import 'package:kernel/kernel.dart';
|
||||
import 'package:kernel/target/targets.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm/kernel_front_end.dart' show createLoadedLibrariesSet;
|
||||
import 'package:vm/modular/target/vm.dart';
|
||||
|
||||
/// Environment define to update expectation files on failures.
|
||||
@@ -33,17 +34,18 @@ final String dartSdkPkgDir = Platform.script.resolve('../..').toFilePath();
|
||||
runTestCase(Uri source, {bool isClosureContextLoweringEnabled = false}) async {
|
||||
final target = VmTarget(TargetFlags(
|
||||
isClosureContextLoweringEnabled: isClosureContextLoweringEnabled));
|
||||
Component component =
|
||||
await compileTestCaseToKernelProgram(source, target: target);
|
||||
final result = await compileTestCaseToKernelProgram(source, target: target);
|
||||
|
||||
final mainLibrary = component.mainMethod!.enclosingLibrary;
|
||||
final coreTypes = CoreTypes(component);
|
||||
final hierarchy = ClassHierarchy(component, coreTypes);
|
||||
final mainLibrary = result.component.mainMethod!.enclosingLibrary;
|
||||
|
||||
final sink = ByteSink();
|
||||
generateBytecode(component, sink,
|
||||
final coreTypes = result.coreTypes ?? CoreTypes(result.component);
|
||||
final hierarchy =
|
||||
result.classHierarchy ?? ClassHierarchy(result.component, coreTypes);
|
||||
generateBytecode(result.component, sink,
|
||||
options: BytecodeOptions(),
|
||||
libraries: [mainLibrary],
|
||||
extraLoadedLibraries: result.loadedLibraries,
|
||||
coreTypes: coreTypes,
|
||||
hierarchy: hierarchy,
|
||||
target: target);
|
||||
@@ -58,25 +60,48 @@ runTestCase(Uri source, {bool isClosureContextLoweringEnabled = false}) async {
|
||||
compareResultWithExpectationsFile(source, actual);
|
||||
}
|
||||
|
||||
Future<Component> compileTestCaseToKernelProgram(Uri sourceUri,
|
||||
// Similar to CompilerResult from pkg/vm/bin/kernel_service.dart.
|
||||
class CompilerResult {
|
||||
final Component component;
|
||||
final Set<Library> loadedLibraries;
|
||||
final ClassHierarchy? classHierarchy;
|
||||
final CoreTypes? coreTypes;
|
||||
|
||||
CompilerResult(
|
||||
this.component,
|
||||
this.loadedLibraries,
|
||||
this.classHierarchy,
|
||||
this.coreTypes,
|
||||
);
|
||||
}
|
||||
|
||||
Future<CompilerResult> compileTestCaseToKernelProgram(Uri sourceUri,
|
||||
{required Target target}) async {
|
||||
final platformKernel =
|
||||
computePlatformBinariesLocation().resolve('vm_platform.dill');
|
||||
final options = CompilerOptions()
|
||||
..target = target
|
||||
..omitPlatform = true
|
||||
..additionalDills = <Uri>[platformKernel]
|
||||
..environmentDefines = {}
|
||||
..onDiagnostic = (CfeDiagnosticMessage message) {
|
||||
fail("Compilation error: ${message.plainTextFormatted.join('\n')}");
|
||||
};
|
||||
|
||||
final Component component =
|
||||
(await kernelForProgram(sourceUri, options))!.component!;
|
||||
final result = (await kernelForProgram(sourceUri, options))!;
|
||||
final Component component = result.component!;
|
||||
|
||||
// Make sure the library name is the same and does not depend on the order
|
||||
// of test cases.
|
||||
component.mainMethod!.enclosingLibrary.name = '#lib';
|
||||
return component;
|
||||
return CompilerResult(
|
||||
component,
|
||||
// Use the same calculation as SingleShotCompilerWrapper.
|
||||
createLoadedLibrariesSet(result.loadedComponents, result.sdkComponent,
|
||||
includePlatform: false),
|
||||
result.classHierarchy,
|
||||
result.coreTypes,
|
||||
);
|
||||
}
|
||||
|
||||
class ByteSink implements Sink<List<int>> {
|
||||
@@ -175,6 +200,7 @@ main() {
|
||||
'field_initializers.dart',
|
||||
'optional_params.dart',
|
||||
'bootstrapping.dart',
|
||||
'ffi.dart',
|
||||
};
|
||||
|
||||
group('gen-bytecode-with-closure-context-lowering', () {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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 'dart:ffi';
|
||||
|
||||
testVoidNoArg() {
|
||||
final pointer = Pointer<NativeFunction<Void Function()>>.fromAddress(
|
||||
0xdeadbeef,
|
||||
);
|
||||
final function = pointer.asFunction<void Function()>();
|
||||
function();
|
||||
}
|
||||
|
||||
testIntInt() {
|
||||
final pointer = Pointer<NativeFunction<Int32 Function(Int64)>>.fromAddress(
|
||||
0xdeadbeef,
|
||||
);
|
||||
final function = pointer.asFunction<int Function(int)>();
|
||||
return function(42);
|
||||
}
|
||||
|
||||
void main() {
|
||||
testVoidNoArg();
|
||||
testIntInt();
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
Bytecode
|
||||
Dynamic Module Entry Point: DART_SDK/pkg/dart2bytecode/testcases/ffi.dart::main
|
||||
Library 'DART_SDK/pkg/dart2bytecode/testcases/ffi.dart'
|
||||
name '#lib'
|
||||
script 'DART_SDK/pkg/dart2bytecode/testcases/ffi.dart'
|
||||
uses dart:ffi
|
||||
|
||||
Class '', script = 'DART_SDK/pkg/dart2bytecode/testcases/ffi.dart'
|
||||
|
||||
|
||||
Function 'testVoidNoArg', static, reflectable, debuggable
|
||||
parameters [] (required: 0)
|
||||
return-type dynamic
|
||||
|
||||
Bytecode {
|
||||
Entry 5
|
||||
CheckStack 0
|
||||
PushConstant CP#0
|
||||
PushConstant CP#1
|
||||
DirectCall CP#2, 2
|
||||
PopLocal r2
|
||||
AllocateContext 0, 1
|
||||
PopLocal r0
|
||||
Push r0
|
||||
Push r2
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#4
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
PopLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
LoadContextParent
|
||||
PopLocal r0
|
||||
PopLocal r3
|
||||
Push r3
|
||||
StoreLocal r4
|
||||
Push r4
|
||||
UncheckedClosureCall CP#9, 1
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ObjectRef < dart:ffi::NativeFunction < FunctionType () -> dart:ffi::Void > >
|
||||
[1] = ObjectRef const 3735928559
|
||||
[2] = DirectCall 'dart:ffi::Pointer::fromAddress (constructor)', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
[4] = ClosureFunction 0
|
||||
[5] = InstanceField dart:core::_Closure::_context (field)
|
||||
[6] = Reserved
|
||||
[7] = FfiCall
|
||||
[8] = EndClosureFunctionScope
|
||||
[9] = ObjectRef ArgDesc num-args 1, num-type-args 0, names []
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/ffi.dart::testVoidNoArg::'#ffiClosure0' annotations const List<dynamic> [const dart:core::pragma {dart:core::pragma::name (field): 'vm:ffi:call-closure', dart:core::pragma::options (field): const dart:ffi::_FfiCall < FunctionType () -> dart:ffi::Void > {dart:ffi::_FfiCall::isLeaf (field): const false}}]
|
||||
() -> void
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-5]
|
||||
LoadFieldTOS CP#5
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
FfiCall CP#7
|
||||
ReturnTOS
|
||||
}
|
||||
|
||||
|
||||
Function 'testIntInt', static, reflectable, debuggable
|
||||
parameters [] (required: 0)
|
||||
return-type dynamic
|
||||
|
||||
Bytecode {
|
||||
Entry 5
|
||||
CheckStack 0
|
||||
PushConstant CP#0
|
||||
PushConstant CP#1
|
||||
DirectCall CP#2, 2
|
||||
PopLocal r2
|
||||
AllocateContext 0, 1
|
||||
PopLocal r0
|
||||
Push r0
|
||||
Push r2
|
||||
StoreContextVar 0, 0
|
||||
PushConstant CP#4
|
||||
Push r0
|
||||
PushNull
|
||||
AllocateClosure
|
||||
PopLocal r4
|
||||
Push r4
|
||||
Push r0
|
||||
LoadContextParent
|
||||
PopLocal r0
|
||||
PopLocal r3
|
||||
Push r3
|
||||
StoreLocal r4
|
||||
PushInt 42
|
||||
Push r4
|
||||
UncheckedClosureCall CP#12, 2
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = ObjectRef < dart:ffi::NativeFunction < FunctionType (dart:ffi::Int64) -> dart:ffi::Int32 > >
|
||||
[1] = ObjectRef const 3735928559
|
||||
[2] = DirectCall 'dart:ffi::Pointer::fromAddress (constructor)', ArgDesc num-args 2, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
[4] = ClosureFunction 0
|
||||
[5] = InstanceField dart:core::_Closure::_context (field)
|
||||
[6] = Reserved
|
||||
[7] = Type dart:core::int
|
||||
[8] = ObjectRef 'arg1'
|
||||
[9] = SubtypeTestCache
|
||||
[10] = FfiCall
|
||||
[11] = EndClosureFunctionScope
|
||||
[12] = ObjectRef ArgDesc num-args 2, num-type-args 0, names []
|
||||
}
|
||||
Closure DART_SDK/pkg/dart2bytecode/testcases/ffi.dart::testIntInt::'#ffiClosure1' annotations const List<dynamic> [const dart:core::pragma {dart:core::pragma::name (field): 'vm:ffi:call-closure', dart:core::pragma::options (field): const dart:ffi::_FfiCall < FunctionType (dart:ffi::Int64) -> dart:ffi::Int32 > {dart:ffi::_FfiCall::isLeaf (field): const false}}]
|
||||
(dart:core::int arg1) -> dart:core::int
|
||||
ClosureCode {
|
||||
Entry 2
|
||||
Push FP[-6]
|
||||
LoadFieldTOS CP#5
|
||||
PopLocal r0
|
||||
CheckStack 0
|
||||
JumpIfUnchecked L1
|
||||
Push FP[-5]
|
||||
PushConstant CP#7
|
||||
PushNull
|
||||
PushNull
|
||||
PushConstant CP#8
|
||||
AssertAssignable 1, CP#9
|
||||
Drop1
|
||||
L1:
|
||||
PushNull
|
||||
Drop1
|
||||
Push FP[-5]
|
||||
Push r0
|
||||
LoadContextVar 0, 0
|
||||
FfiCall CP#10
|
||||
ReturnTOS
|
||||
}
|
||||
|
||||
|
||||
Function 'main', static, reflectable, debuggable
|
||||
parameters [] (required: 0)
|
||||
return-type void
|
||||
|
||||
Bytecode {
|
||||
Entry 0
|
||||
CheckStack 0
|
||||
DirectCall CP#0, 0
|
||||
Drop1
|
||||
DirectCall CP#2, 0
|
||||
Drop1
|
||||
PushNull
|
||||
ReturnTOS
|
||||
}
|
||||
ConstantPool {
|
||||
[0] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/ffi.dart::testVoidNoArg', ArgDesc num-args 0, num-type-args 0, names []
|
||||
[1] = Reserved
|
||||
[2] = DirectCall 'DART_SDK/pkg/dart2bytecode/testcases/ffi.dart::testIntInt', ArgDesc num-args 0, num-type-args 0, names []
|
||||
[3] = Reserved
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user