mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 08:51:21 +00:00
Revert Reland '[vm/ffi] Implement FFI callbacks on AOT for ELF and Asm snapshots (excl. blobs).' as it breaks flutter profile execution on ios 32-bit platform.
Fixes https://github.com/flutter/flutter/issues/40114 Change-Id: If8d71e9c19c2e794d29f7ecbacb87457890a2fd5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116883 Reviewed-by: Alexander Markov <alexmarkov@google.com> Commit-Queue: Alexander Aprelev <aam@google.com>
This commit is contained in:
parent
1d01d91c33
commit
0c481a196c
|
@ -40,23 +40,19 @@ export '../fasta/fasta_codes.dart'
|
||||||
show
|
show
|
||||||
LocatedMessage,
|
LocatedMessage,
|
||||||
messageBytecodeLimitExceededTooManyArguments,
|
messageBytecodeLimitExceededTooManyArguments,
|
||||||
messageFfiExceptionalReturnNull,
|
|
||||||
messageFfiExpectedConstant,
|
|
||||||
noLength,
|
noLength,
|
||||||
templateFfiDartTypeMismatch,
|
|
||||||
templateFfiExpectedExceptionalReturn,
|
|
||||||
templateFfiExpectedNoExceptionalReturn,
|
|
||||||
templateFfiExtendsOrImplementsSealedClass,
|
|
||||||
templateFfiFieldAnnotation,
|
templateFfiFieldAnnotation,
|
||||||
templateFfiFieldInitializer,
|
|
||||||
templateFfiFieldNoAnnotation,
|
templateFfiFieldNoAnnotation,
|
||||||
templateFfiNotStatic,
|
templateFfiNotStatic,
|
||||||
templateFfiStructGeneric,
|
|
||||||
templateFfiTypeInvalid,
|
templateFfiTypeInvalid,
|
||||||
templateFfiTypeMismatch,
|
templateFfiTypeMismatch,
|
||||||
templateFfiTypeUnsized,
|
templateFfiTypeUnsized,
|
||||||
templateFfiWrongStructInheritance,
|
templateFfiFieldInitializer,
|
||||||
templateIllegalRecursiveType;
|
templateIllegalRecursiveType,
|
||||||
|
templateFfiDartTypeMismatch,
|
||||||
|
templateFfiExtendsOrImplementsSealedClass,
|
||||||
|
templateFfiStructGeneric,
|
||||||
|
templateFfiWrongStructInheritance;
|
||||||
|
|
||||||
export '../fasta/hybrid_file_system.dart' show HybridFileSystem;
|
export '../fasta/hybrid_file_system.dart' show HybridFileSystem;
|
||||||
|
|
||||||
|
|
|
@ -3754,81 +3754,6 @@ Message _withArgumentsFfiDartTypeMismatch(DartType _type, DartType _type2) {
|
||||||
arguments: {'type': _type, 'type2': _type2});
|
arguments: {'type': _type, 'type2': _type2});
|
||||||
}
|
}
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
const Code<Null> codeFfiExceptionalReturnNull = messageFfiExceptionalReturnNull;
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
const MessageCode messageFfiExceptionalReturnNull = const MessageCode(
|
|
||||||
"FfiExceptionalReturnNull",
|
|
||||||
message: r"""Exceptional return value must not be null.""");
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
const Code<Null> codeFfiExpectedConstant = messageFfiExpectedConstant;
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
const MessageCode messageFfiExpectedConstant = const MessageCode(
|
|
||||||
"FfiExpectedConstant",
|
|
||||||
message: r"""Exceptional return value must be a constant.""");
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
const Template<
|
|
||||||
Message Function(
|
|
||||||
DartType
|
|
||||||
_type)> templateFfiExpectedExceptionalReturn = const Template<
|
|
||||||
Message Function(DartType _type)>(
|
|
||||||
messageTemplate:
|
|
||||||
r"""Expected an exceptional return value for a native callback returning '#type'.""",
|
|
||||||
withArguments: _withArgumentsFfiExpectedExceptionalReturn);
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
const Code<Message Function(DartType _type)> codeFfiExpectedExceptionalReturn =
|
|
||||||
const Code<Message Function(DartType _type)>(
|
|
||||||
"FfiExpectedExceptionalReturn",
|
|
||||||
templateFfiExpectedExceptionalReturn,
|
|
||||||
);
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
Message _withArgumentsFfiExpectedExceptionalReturn(DartType _type) {
|
|
||||||
TypeLabeler labeler = new TypeLabeler();
|
|
||||||
List<Object> typeParts = labeler.labelType(_type);
|
|
||||||
String type = typeParts.join();
|
|
||||||
return new Message(codeFfiExpectedExceptionalReturn,
|
|
||||||
message:
|
|
||||||
"""Expected an exceptional return value for a native callback returning '${type}'.""" +
|
|
||||||
labeler.originMessages,
|
|
||||||
arguments: {'type': _type});
|
|
||||||
}
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
const Template<
|
|
||||||
Message Function(
|
|
||||||
DartType
|
|
||||||
_type)> templateFfiExpectedNoExceptionalReturn = const Template<
|
|
||||||
Message Function(DartType _type)>(
|
|
||||||
messageTemplate:
|
|
||||||
r"""Exceptional return value cannot be provided for a native callback returning '#type'.""",
|
|
||||||
withArguments: _withArgumentsFfiExpectedNoExceptionalReturn);
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
const Code<Message Function(DartType _type)>
|
|
||||||
codeFfiExpectedNoExceptionalReturn =
|
|
||||||
const Code<Message Function(DartType _type)>(
|
|
||||||
"FfiExpectedNoExceptionalReturn",
|
|
||||||
templateFfiExpectedNoExceptionalReturn,
|
|
||||||
);
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
|
||||||
Message _withArgumentsFfiExpectedNoExceptionalReturn(DartType _type) {
|
|
||||||
TypeLabeler labeler = new TypeLabeler();
|
|
||||||
List<Object> typeParts = labeler.labelType(_type);
|
|
||||||
String type = typeParts.join();
|
|
||||||
return new Message(codeFfiExpectedNoExceptionalReturn,
|
|
||||||
message:
|
|
||||||
"""Exceptional return value cannot be provided for a native callback returning '${type}'.""" +
|
|
||||||
labeler.originMessages,
|
|
||||||
arguments: {'type': _type});
|
|
||||||
}
|
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||||
const Template<Message Function(String name)>
|
const Template<Message Function(String name)>
|
||||||
templateFfiExtendsOrImplementsSealedClass =
|
templateFfiExtendsOrImplementsSealedClass =
|
||||||
|
@ -3859,7 +3784,7 @@ const Template<
|
||||||
Message Function(String name)> templateFfiFieldAnnotation = const Template<
|
Message Function(String name)> templateFfiFieldAnnotation = const Template<
|
||||||
Message Function(String name)>(
|
Message Function(String name)>(
|
||||||
messageTemplate:
|
messageTemplate:
|
||||||
r"""Field '#name' requires exactly one annotation to declare its native type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields.""",
|
r"""Field '#name' requires exactly one annotation to declare its C++ type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields.""",
|
||||||
withArguments: _withArgumentsFfiFieldAnnotation);
|
withArguments: _withArgumentsFfiFieldAnnotation);
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||||
|
@ -3875,7 +3800,7 @@ Message _withArgumentsFfiFieldAnnotation(String name) {
|
||||||
name = demangleMixinApplicationName(name);
|
name = demangleMixinApplicationName(name);
|
||||||
return new Message(codeFfiFieldAnnotation,
|
return new Message(codeFfiFieldAnnotation,
|
||||||
message:
|
message:
|
||||||
"""Field '${name}' requires exactly one annotation to declare its native type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields.""",
|
"""Field '${name}' requires exactly one annotation to declare its C++ type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields.""",
|
||||||
arguments: {'name': name});
|
arguments: {'name': name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3911,7 +3836,7 @@ const Template<
|
||||||
name)> templateFfiFieldNoAnnotation = const Template<
|
name)> templateFfiFieldNoAnnotation = const Template<
|
||||||
Message Function(String name)>(
|
Message Function(String name)>(
|
||||||
messageTemplate:
|
messageTemplate:
|
||||||
r"""Field '#name' requires no annotation to declare its native type, it is a Pointer which is represented by the same type in Dart and native code.""",
|
r"""Field '#name' requires no annotation to declare its C++ type, it is a Pointer which is represented by the same type in Dart and C++.""",
|
||||||
withArguments: _withArgumentsFfiFieldNoAnnotation);
|
withArguments: _withArgumentsFfiFieldNoAnnotation);
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||||
|
@ -3927,7 +3852,7 @@ Message _withArgumentsFfiFieldNoAnnotation(String name) {
|
||||||
name = demangleMixinApplicationName(name);
|
name = demangleMixinApplicationName(name);
|
||||||
return new Message(codeFfiFieldNoAnnotation,
|
return new Message(codeFfiFieldNoAnnotation,
|
||||||
message:
|
message:
|
||||||
"""Field '${name}' requires no annotation to declare its native type, it is a Pointer which is represented by the same type in Dart and native code.""",
|
"""Field '${name}' requires no annotation to declare its C++ type, it is a Pointer which is represented by the same type in Dart and C++.""",
|
||||||
arguments: {'name': name});
|
arguments: {'name': name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3936,7 +3861,7 @@ const Template<
|
||||||
Message Function(String name)> templateFfiNotStatic = const Template<
|
Message Function(String name)> templateFfiNotStatic = const Template<
|
||||||
Message Function(String name)>(
|
Message Function(String name)>(
|
||||||
messageTemplate:
|
messageTemplate:
|
||||||
r"""#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code.""",
|
r"""#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from c.""",
|
||||||
withArguments: _withArgumentsFfiNotStatic);
|
withArguments: _withArgumentsFfiNotStatic);
|
||||||
|
|
||||||
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
|
||||||
|
@ -3952,7 +3877,7 @@ Message _withArgumentsFfiNotStatic(String name) {
|
||||||
name = demangleMixinApplicationName(name);
|
name = demangleMixinApplicationName(name);
|
||||||
return new Message(codeFfiNotStatic,
|
return new Message(codeFfiNotStatic,
|
||||||
message:
|
message:
|
||||||
"""${name} expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code.""",
|
"""${name} expects a static function as parameter. dart:ffi only supports calling static Dart functions from c.""",
|
||||||
arguments: {'name': name});
|
arguments: {'name': name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,10 +265,6 @@ FfiDartTypeMismatch/analyzerCode: Fail
|
||||||
FfiExtendsOrImplementsSealedClass/analyzerCode: Fail
|
FfiExtendsOrImplementsSealedClass/analyzerCode: Fail
|
||||||
FfiStructGeneric/analyzerCode: Fail
|
FfiStructGeneric/analyzerCode: Fail
|
||||||
FfiWrongStructInheritance/analyzerCode: Fail
|
FfiWrongStructInheritance/analyzerCode: Fail
|
||||||
FfiExpectedExceptionalReturn/analyzerCode: Fail
|
|
||||||
FfiExpectedNoExceptionalReturn/analyzerCode: Fail
|
|
||||||
FfiExpectedConstant/analyzerCode: Fail
|
|
||||||
FfiExceptionalReturnNull/analyzerCode: Fail
|
|
||||||
FieldInitializedOutsideDeclaringClass/part_wrapped_script1: Fail
|
FieldInitializedOutsideDeclaringClass/part_wrapped_script1: Fail
|
||||||
FieldInitializedOutsideDeclaringClass/script1: Fail
|
FieldInitializedOutsideDeclaringClass/script1: Fail
|
||||||
FieldInitializerOutsideConstructor/part_wrapped_script1: Fail
|
FieldInitializerOutsideConstructor/part_wrapped_script1: Fail
|
||||||
|
|
|
@ -3495,17 +3495,17 @@ FfiTypeUnsized:
|
||||||
|
|
||||||
FfiFieldAnnotation:
|
FfiFieldAnnotation:
|
||||||
# Used by dart:ffi
|
# Used by dart:ffi
|
||||||
template: "Field '#name' requires exactly one annotation to declare its native type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields."
|
template: "Field '#name' requires exactly one annotation to declare its C++ type, which cannot be Void. dart:ffi Structs cannot have regular Dart fields."
|
||||||
external: test/ffi_test.dart
|
external: test/ffi_test.dart
|
||||||
|
|
||||||
FfiFieldNoAnnotation:
|
FfiFieldNoAnnotation:
|
||||||
# Used by dart:ffi
|
# Used by dart:ffi
|
||||||
template: "Field '#name' requires no annotation to declare its native type, it is a Pointer which is represented by the same type in Dart and native code."
|
template: "Field '#name' requires no annotation to declare its C++ type, it is a Pointer which is represented by the same type in Dart and C++."
|
||||||
external: test/ffi_test.dart
|
external: test/ffi_test.dart
|
||||||
|
|
||||||
FfiNotStatic:
|
FfiNotStatic:
|
||||||
# Used by dart:ffi
|
# Used by dart:ffi
|
||||||
template: "#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from native code."
|
template: "#name expects a static function as parameter. dart:ffi only supports calling static Dart functions from c."
|
||||||
external: test/ffi_test.dart
|
external: test/ffi_test.dart
|
||||||
|
|
||||||
FfiFieldInitializer:
|
FfiFieldInitializer:
|
||||||
|
@ -3533,26 +3533,6 @@ FfiDartTypeMismatch:
|
||||||
template: "Expected '#type' to be a subtype of '#type2'."
|
template: "Expected '#type' to be a subtype of '#type2'."
|
||||||
external: test/ffi_test.dart
|
external: test/ffi_test.dart
|
||||||
|
|
||||||
FfiExpectedExceptionalReturn:
|
|
||||||
# Used by dart:ffi
|
|
||||||
template: "Expected an exceptional return value for a native callback returning '#type'."
|
|
||||||
external: test/ffi_test.dart
|
|
||||||
|
|
||||||
FfiExpectedNoExceptionalReturn:
|
|
||||||
# Used by dart:ffi
|
|
||||||
template: "Exceptional return value cannot be provided for a native callback returning '#type'."
|
|
||||||
external: test/ffi_test.dart
|
|
||||||
|
|
||||||
FfiExpectedConstant:
|
|
||||||
# Used by dart:ffi
|
|
||||||
template: "Exceptional return value must be a constant."
|
|
||||||
external: test/ffi_test.dart
|
|
||||||
|
|
||||||
FfiExceptionalReturnNull:
|
|
||||||
# Used by dart:ffi
|
|
||||||
template: "Exceptional return value must not be null."
|
|
||||||
external: test/ffi_test.dart
|
|
||||||
|
|
||||||
SpreadTypeMismatch:
|
SpreadTypeMismatch:
|
||||||
template: "Unexpected type '#type' of a spread. Expected 'dynamic' or an Iterable."
|
template: "Unexpected type '#type' of a spread. Expected 'dynamic' or an Iterable."
|
||||||
script:
|
script:
|
||||||
|
|
|
@ -941,7 +941,6 @@ examples
|
||||||
exceeded
|
exceeded
|
||||||
except
|
except
|
||||||
exception
|
exception
|
||||||
exceptional
|
|
||||||
exceptions
|
exceptions
|
||||||
exclamation
|
exclamation
|
||||||
exclude
|
exclude
|
||||||
|
|
|
@ -186,8 +186,6 @@ class FfiTransformer extends Transformer {
|
||||||
final Constructor structFromPointer;
|
final Constructor structFromPointer;
|
||||||
final Procedure libraryLookupMethod;
|
final Procedure libraryLookupMethod;
|
||||||
final Procedure abiMethod;
|
final Procedure abiMethod;
|
||||||
final Procedure pointerFromFunctionProcedure;
|
|
||||||
final Procedure nativeCallbackFunctionProcedure;
|
|
||||||
|
|
||||||
/// Classes corresponding to [NativeType], indexed by [NativeType].
|
/// Classes corresponding to [NativeType], indexed by [NativeType].
|
||||||
final List<Class> nativeTypesClasses;
|
final List<Class> nativeTypesClasses;
|
||||||
|
@ -222,10 +220,6 @@ class FfiTransformer extends Transformer {
|
||||||
libraryLookupMethod =
|
libraryLookupMethod =
|
||||||
index.getMember('dart:ffi', 'DynamicLibrary', 'lookup'),
|
index.getMember('dart:ffi', 'DynamicLibrary', 'lookup'),
|
||||||
abiMethod = index.getTopLevelMember('dart:ffi', '_abi'),
|
abiMethod = index.getTopLevelMember('dart:ffi', '_abi'),
|
||||||
pointerFromFunctionProcedure =
|
|
||||||
index.getTopLevelMember('dart:ffi', '_pointerFromFunction'),
|
|
||||||
nativeCallbackFunctionProcedure =
|
|
||||||
index.getTopLevelMember('dart:ffi', '_nativeCallbackFunction'),
|
|
||||||
nativeTypesClasses = nativeTypeClassNames
|
nativeTypesClasses = nativeTypeClassNames
|
||||||
.map((name) => index.getClass('dart:ffi', name))
|
.map((name) => index.getClass('dart:ffi', name))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
|
@ -6,16 +6,12 @@ library vm.transformations.ffi_use_sites;
|
||||||
|
|
||||||
import 'package:front_end/src/api_unstable/vm.dart'
|
import 'package:front_end/src/api_unstable/vm.dart'
|
||||||
show
|
show
|
||||||
messageFfiExceptionalReturnNull,
|
|
||||||
messageFfiExpectedConstant,
|
|
||||||
templateFfiDartTypeMismatch,
|
|
||||||
templateFfiExpectedExceptionalReturn,
|
|
||||||
templateFfiExpectedNoExceptionalReturn,
|
|
||||||
templateFfiExtendsOrImplementsSealedClass,
|
|
||||||
templateFfiNotStatic,
|
|
||||||
templateFfiTypeInvalid,
|
templateFfiTypeInvalid,
|
||||||
templateFfiTypeMismatch,
|
templateFfiTypeMismatch,
|
||||||
templateFfiTypeUnsized;
|
templateFfiDartTypeMismatch,
|
||||||
|
templateFfiTypeUnsized,
|
||||||
|
templateFfiNotStatic,
|
||||||
|
templateFfiExtendsOrImplementsSealedClass;
|
||||||
|
|
||||||
import 'package:kernel/ast.dart';
|
import 'package:kernel/ast.dart';
|
||||||
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
||||||
|
@ -59,12 +55,7 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
final Map<Field, Procedure> replacedGetters;
|
final Map<Field, Procedure> replacedGetters;
|
||||||
final Map<Field, Procedure> replacedSetters;
|
final Map<Field, Procedure> replacedSetters;
|
||||||
|
|
||||||
Library currentLibrary;
|
bool isFfiLibrary;
|
||||||
bool get isFfiLibrary => currentLibrary == ffiLibrary;
|
|
||||||
|
|
||||||
// Used to create private top-level fields with unique names for each
|
|
||||||
// callback.
|
|
||||||
int callbackCount = 0;
|
|
||||||
|
|
||||||
_FfiUseSiteTransformer(
|
_FfiUseSiteTransformer(
|
||||||
LibraryIndex index,
|
LibraryIndex index,
|
||||||
|
@ -77,8 +68,7 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TreeNode visitLibrary(Library node) {
|
TreeNode visitLibrary(Library node) {
|
||||||
currentLibrary = node;
|
isFfiLibrary = node == ffiLibrary;
|
||||||
callbackCount = 0;
|
|
||||||
return super.visitLibrary(node);
|
return super.visitLibrary(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,11 +78,6 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
try {
|
try {
|
||||||
_ensureNotExtendsOrImplementsSealedClass(node);
|
_ensureNotExtendsOrImplementsSealedClass(node);
|
||||||
return super.visitClass(node);
|
return super.visitClass(node);
|
||||||
} on _FfiStaticTypeError {
|
|
||||||
// It's OK to swallow the exception because the diagnostics issued will
|
|
||||||
// cause compilation to fail. By continuing, we can report more
|
|
||||||
// diagnostics before compilation ends.
|
|
||||||
return super.visitClass(node);
|
|
||||||
} finally {
|
} finally {
|
||||||
env.thisType = null;
|
env.thisType = null;
|
||||||
}
|
}
|
||||||
|
@ -130,70 +115,19 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
Member target = node.target;
|
Member target = node.target;
|
||||||
try {
|
try {
|
||||||
if (target == fromFunctionMethod) {
|
if (target == fromFunctionMethod) {
|
||||||
final DartType nativeType =
|
DartType nativeType =
|
||||||
InterfaceType(nativeFunctionClass, [node.arguments.types[0]]);
|
InterfaceType(nativeFunctionClass, [node.arguments.types[0]]);
|
||||||
final Expression func = node.arguments.positional[0];
|
Expression func = node.arguments.positional[0];
|
||||||
final DartType dartType = func.getStaticType(env);
|
DartType dartType = func.getStaticType(env);
|
||||||
|
|
||||||
_ensureIsStaticFunction(func);
|
|
||||||
|
|
||||||
|
_ensureIsStatic(func);
|
||||||
// TODO(36730): Allow passing/returning structs by value.
|
// TODO(36730): Allow passing/returning structs by value.
|
||||||
_ensureNativeTypeValid(nativeType, node);
|
_ensureNativeTypeValid(nativeType, node);
|
||||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||||
|
|
||||||
// Check `exceptionalReturn`'s type.
|
// Check `exceptionalReturn`'s type.
|
||||||
final FunctionType funcType = dartType;
|
final FunctionType funcType = dartType;
|
||||||
final NativeType expectedReturn = getType(
|
|
||||||
((node.arguments.types[0] as FunctionType).returnType
|
|
||||||
as InterfaceType)
|
|
||||||
.classNode);
|
|
||||||
|
|
||||||
if (expectedReturn == NativeType.kVoid ||
|
|
||||||
expectedReturn == NativeType.kPointer) {
|
|
||||||
if (node.arguments.positional.length > 1) {
|
|
||||||
diagnosticReporter.report(
|
|
||||||
templateFfiExpectedNoExceptionalReturn
|
|
||||||
.withArguments(funcType.returnType),
|
|
||||||
node.fileOffset,
|
|
||||||
1,
|
|
||||||
node.location.file);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
node.arguments.positional.add(NullLiteral()..parent = node);
|
|
||||||
} else {
|
|
||||||
// The exceptional return value is not optional for other return
|
|
||||||
// types.
|
|
||||||
if (node.arguments.positional.length < 2) {
|
|
||||||
diagnosticReporter.report(
|
|
||||||
templateFfiExpectedExceptionalReturn
|
|
||||||
.withArguments(funcType.returnType),
|
|
||||||
node.fileOffset,
|
|
||||||
1,
|
|
||||||
node.location.file);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Expression exceptionalReturn = node.arguments.positional[1];
|
final Expression exceptionalReturn = node.arguments.positional[1];
|
||||||
|
|
||||||
// The exceptional return value must be a constant so that it be
|
|
||||||
// referenced by precompiled trampoline's object pool.
|
|
||||||
if (exceptionalReturn is! BasicLiteral &&
|
|
||||||
!(exceptionalReturn is ConstantExpression &&
|
|
||||||
exceptionalReturn.constant is PrimitiveConstant)) {
|
|
||||||
diagnosticReporter.report(messageFfiExpectedConstant,
|
|
||||||
node.fileOffset, 1, node.location.file);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Moreover it may not be null.
|
|
||||||
if (exceptionalReturn is NullLiteral ||
|
|
||||||
(exceptionalReturn is ConstantExpression &&
|
|
||||||
exceptionalReturn.constant is NullConstant)) {
|
|
||||||
diagnosticReporter.report(messageFfiExceptionalReturnNull,
|
|
||||||
node.fileOffset, 1, node.location.file);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
final DartType returnType = exceptionalReturn.getStaticType(env);
|
final DartType returnType = exceptionalReturn.getStaticType(env);
|
||||||
|
|
||||||
if (!env.isSubtypeOf(returnType, funcType.returnType)) {
|
if (!env.isSubtypeOf(returnType, funcType.returnType)) {
|
||||||
|
@ -203,16 +137,9 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
exceptionalReturn.fileOffset,
|
exceptionalReturn.fileOffset,
|
||||||
1,
|
1,
|
||||||
exceptionalReturn.location.file);
|
exceptionalReturn.location.file);
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _replaceFromFunction(node);
|
} on _FfiStaticTypeError {}
|
||||||
}
|
|
||||||
} on _FfiStaticTypeError {
|
|
||||||
// It's OK to swallow the exception because the diagnostics issued will
|
|
||||||
// cause compilation to fail. By continuing, we can report more
|
|
||||||
// diagnostics before compilation ends.
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -224,10 +151,10 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
// 'lookupFunction' are constants, so by inlining the call to 'asFunction' at
|
// 'lookupFunction' are constants, so by inlining the call to 'asFunction' at
|
||||||
// the call-site, we ensure that there are no generic calls to 'asFunction'.
|
// the call-site, we ensure that there are no generic calls to 'asFunction'.
|
||||||
//
|
//
|
||||||
// We will not detect dynamic invocations of 'asFunction' and
|
// We will not detect dynamic invocations of 'asFunction' -- these are handled
|
||||||
// 'lookupFunction': these are handled by the stubs in 'ffi_patch.dart' and
|
// by the stub in 'dynamic_library_patch.dart'. Dynamic invocations of
|
||||||
// 'dynamic_library_patch.dart'. Dynamic invocations of 'lookupFunction' (and
|
// 'lookupFunction' (and 'asFunction') are not legal and throw a runtime
|
||||||
// 'asFunction') are not legal and throw a runtime exception.
|
// exception.
|
||||||
Expression _replaceLookupFunction(MethodInvocation node) {
|
Expression _replaceLookupFunction(MethodInvocation node) {
|
||||||
// The generated code looks like:
|
// The generated code looks like:
|
||||||
//
|
//
|
||||||
|
@ -249,39 +176,6 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
Arguments([lookupResult], types: [dartSignature, nativeSignature]));
|
Arguments([lookupResult], types: [dartSignature, nativeSignature]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to rewrite calls to 'fromFunction' into two calls, representing the
|
|
||||||
// compile-time and run-time aspects of creating the closure:
|
|
||||||
//
|
|
||||||
// final dynamic _#ffiCallback0 = Pointer.fromFunction<T>(f, e) =>
|
|
||||||
// _pointerFromFunction<NativeFunction<T>>(
|
|
||||||
// _nativeCallbackFunction<T>(f, e));
|
|
||||||
//
|
|
||||||
// ... _#ffiCallback0 ...
|
|
||||||
//
|
|
||||||
// We must implement this as a Kernel rewrite because <T> must be a
|
|
||||||
// compile-time constant to any invocation of '_nativeCallbackFunction'.
|
|
||||||
//
|
|
||||||
// Creating this closure requires a runtime call, so we save the result in a
|
|
||||||
// synthetic top-level field to avoid recomputing it.
|
|
||||||
Expression _replaceFromFunction(StaticInvocation node) {
|
|
||||||
final nativeFunctionType =
|
|
||||||
InterfaceType(nativeFunctionClass, node.arguments.types);
|
|
||||||
final Field field = Field(
|
|
||||||
Name("_#ffiCallback${callbackCount++}", currentLibrary),
|
|
||||||
type: InterfaceType(pointerClass, [nativeFunctionType]),
|
|
||||||
initializer: StaticInvocation(
|
|
||||||
pointerFromFunctionProcedure,
|
|
||||||
Arguments([
|
|
||||||
StaticInvocation(nativeCallbackFunctionProcedure, node.arguments)
|
|
||||||
], types: [
|
|
||||||
nativeFunctionType
|
|
||||||
])),
|
|
||||||
isStatic: true,
|
|
||||||
isFinal: true);
|
|
||||||
currentLibrary.addMember(field);
|
|
||||||
return StaticGet(field);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
visitMethodInvocation(MethodInvocation node) {
|
visitMethodInvocation(MethodInvocation node) {
|
||||||
super.visitMethodInvocation(node);
|
super.visitMethodInvocation(node);
|
||||||
|
@ -339,11 +233,7 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
_ensureNativeTypeSized(nativeType, node, target.name);
|
_ensureNativeTypeSized(nativeType, node, target.name);
|
||||||
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
_ensureNativeTypeToDartType(nativeType, dartType, node);
|
||||||
}
|
}
|
||||||
} on _FfiStaticTypeError {
|
} on _FfiStaticTypeError {}
|
||||||
// It's OK to swallow the exception because the diagnostics issued will
|
|
||||||
// cause compilation to fail. By continuing, we can report more
|
|
||||||
// diagnostics before compilation ends.
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -432,11 +322,8 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _ensureIsStaticFunction(Expression node) {
|
void _ensureIsStatic(Expression node) {
|
||||||
if ((node is StaticGet && node.target is Procedure) ||
|
if (!_isStatic(node)) {
|
||||||
(node is ConstantExpression && node.constant is TearOffConstant)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
diagnosticReporter.report(
|
diagnosticReporter.report(
|
||||||
templateFfiNotStatic.withArguments(fromFunctionMethod.name.name),
|
templateFfiNotStatic.withArguments(fromFunctionMethod.name.name),
|
||||||
node.fileOffset,
|
node.fileOffset,
|
||||||
|
@ -444,6 +331,14 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
node.location.file);
|
node.location.file);
|
||||||
throw _FfiStaticTypeError();
|
throw _FfiStaticTypeError();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isStatic(Expression node) {
|
||||||
|
if (node is StaticGet) {
|
||||||
|
return node.target is Procedure;
|
||||||
|
}
|
||||||
|
return node is ConstantExpression;
|
||||||
|
}
|
||||||
|
|
||||||
Class _extendsOrImplementsSealedClass(Class klass) {
|
Class _extendsOrImplementsSealedClass(Class klass) {
|
||||||
final Class superClass = klass.supertype?.classNode;
|
final Class superClass = klass.supertype?.classNode;
|
||||||
|
@ -473,7 +368,6 @@ class _FfiUseSiteTransformer extends FfiTransformer {
|
||||||
klass.fileOffset,
|
klass.fileOffset,
|
||||||
1,
|
1,
|
||||||
klass.location.file);
|
klass.location.file);
|
||||||
throw _FfiStaticTypeError();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -759,7 +759,7 @@ DART_EXPORT int TestThrowExceptionDouble(double (*fn)()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DART_EXPORT int TestThrowExceptionPointer(void* (*fn)()) {
|
DART_EXPORT int TestThrowExceptionPointer(void* (*fn)()) {
|
||||||
CHECK_EQ(fn(), nullptr);
|
CHECK_EQ(fn(), reinterpret_cast<void*>(42));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -429,6 +429,88 @@ DEFINE_NATIVE_ENTRY(Ffi_sizeOf, 1, 0) {
|
||||||
return Integer::New(SizeOf(type_arg));
|
return Integer::New(SizeOf(type_arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(DART_PRECOMPILER) && \
|
||||||
|
!defined(TARGET_ARCH_DBC)
|
||||||
|
// Generates assembly to trampoline from native code into Dart.
|
||||||
|
static uword CompileNativeCallback(const Function& c_signature,
|
||||||
|
const Function& dart_target,
|
||||||
|
const Instance& exceptional_return) {
|
||||||
|
Thread* const thread = Thread::Current();
|
||||||
|
|
||||||
|
uword entry_point = 0;
|
||||||
|
const int32_t callback_id = thread->AllocateFfiCallbackId(&entry_point);
|
||||||
|
ASSERT(NativeCallbackTrampolines::Enabled() == (entry_point != 0));
|
||||||
|
|
||||||
|
// Create a new Function named 'FfiCallback' and stick it in the 'dart:ffi'
|
||||||
|
// library. Note that these functions will never be invoked by Dart, so it
|
||||||
|
// doesn't matter that they all have the same name.
|
||||||
|
Zone* const Z = thread->zone();
|
||||||
|
const String& name = String::Handle(Symbols::New(thread, "FfiCallback"));
|
||||||
|
const Library& lib = Library::Handle(Z, Library::FfiLibrary());
|
||||||
|
const Class& owner_class = Class::Handle(Z, lib.toplevel_class());
|
||||||
|
const Function& function =
|
||||||
|
Function::Handle(Z, Function::New(name, RawFunction::kFfiTrampoline,
|
||||||
|
/*is_static=*/true,
|
||||||
|
/*is_const=*/false,
|
||||||
|
/*is_abstract=*/false,
|
||||||
|
/*is_external=*/false,
|
||||||
|
/*is_native=*/false, owner_class,
|
||||||
|
TokenPosition::kMinSource));
|
||||||
|
function.set_is_debuggable(false);
|
||||||
|
|
||||||
|
// Set callback-specific fields which the flow-graph builder needs to generate
|
||||||
|
// the body.
|
||||||
|
function.SetFfiCSignature(c_signature);
|
||||||
|
function.SetFfiCallbackId(callback_id);
|
||||||
|
function.SetFfiCallbackTarget(dart_target);
|
||||||
|
|
||||||
|
// We require that the exceptional return value for functions returning 'Void'
|
||||||
|
// must be 'null', since native code should not look at the result.
|
||||||
|
if (compiler::ffi::NativeTypeIsVoid(
|
||||||
|
AbstractType::Handle(c_signature.result_type())) &&
|
||||||
|
!exceptional_return.IsNull()) {
|
||||||
|
Exceptions::ThrowUnsupportedError(
|
||||||
|
"Only 'null' may be used as the exceptional return value for a "
|
||||||
|
"callback returning void.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to load the exceptional return value as a constant in the generated
|
||||||
|
// function. This means we need to ensure that it's in old space and has no
|
||||||
|
// (transitively) mutable fields. This is done by checking (asserting) that
|
||||||
|
// it's a built-in FFI class, whose fields are all immutable, or a
|
||||||
|
// user-defined Pointer class, which has no fields.
|
||||||
|
//
|
||||||
|
// TODO(36730): We'll need to extend this when we support passing/returning
|
||||||
|
// structs by value.
|
||||||
|
ASSERT(exceptional_return.IsNull() || exceptional_return.IsNumber() ||
|
||||||
|
exceptional_return.IsPointer());
|
||||||
|
if (!exceptional_return.IsSmi() && exceptional_return.IsNew()) {
|
||||||
|
function.SetFfiCallbackExceptionalReturn(
|
||||||
|
Instance::Handle(exceptional_return.CopyShallowToOldSpace(thread)));
|
||||||
|
} else {
|
||||||
|
function.SetFfiCallbackExceptionalReturn(exceptional_return);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We compile the callback immediately because we need to return a pointer to
|
||||||
|
// the entry-point. Native calls do not use patching like Dart calls, so we
|
||||||
|
// cannot compile it lazily.
|
||||||
|
const Object& result =
|
||||||
|
Object::Handle(Z, Compiler::CompileOptimizedFunction(thread, function));
|
||||||
|
if (result.IsError()) {
|
||||||
|
Exceptions::PropagateError(Error::Cast(result));
|
||||||
|
}
|
||||||
|
ASSERT(result.IsCode());
|
||||||
|
const Code& code = Code::Cast(result);
|
||||||
|
|
||||||
|
thread->SetFfiCallbackCode(callback_id, code);
|
||||||
|
|
||||||
|
if (entry_point != 0) {
|
||||||
|
return entry_point;
|
||||||
|
} else {
|
||||||
|
return code.EntryPoint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Static invocations to this method are translated directly in streaming FGB
|
// Static invocations to this method are translated directly in streaming FGB
|
||||||
// and bytecode FGB. However, we can still reach this entrypoint in the bytecode
|
// and bytecode FGB. However, we can still reach this entrypoint in the bytecode
|
||||||
|
@ -463,6 +545,80 @@ DEFINE_NATIVE_ENTRY(Ffi_asFunctionInternal, 2, 1) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFINE_NATIVE_ENTRY(Ffi_fromFunction, 1, 2) {
|
||||||
|
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER) || \
|
||||||
|
defined(TARGET_ARCH_DBC)
|
||||||
|
// https://github.com/dart-lang/sdk/issues/37295
|
||||||
|
// FFI is supported, but callbacks are not.
|
||||||
|
Exceptions::ThrowUnsupportedError(
|
||||||
|
"FFI callbacks are not yet supported in AOT or on DBC.");
|
||||||
|
#else
|
||||||
|
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
||||||
|
GET_NON_NULL_NATIVE_ARGUMENT(Closure, closure, arguments->NativeArgAt(0));
|
||||||
|
GET_NON_NULL_NATIVE_ARGUMENT(Instance, exceptional_return,
|
||||||
|
arguments->NativeArgAt(1));
|
||||||
|
|
||||||
|
if (!type_arg.IsInstantiated() || !type_arg.IsFunctionType()) {
|
||||||
|
// TODO(35902): Remove this when dynamic invocations of fromFunction are
|
||||||
|
// prohibited.
|
||||||
|
Exceptions::ThrowUnsupportedError(
|
||||||
|
"Type argument to fromFunction must an instantiated function type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const Function& native_signature =
|
||||||
|
Function::Handle(Type::Cast(type_arg).signature());
|
||||||
|
Function& func = Function::Handle(closure.function());
|
||||||
|
TypeArguments& type_args = TypeArguments::Handle(zone);
|
||||||
|
type_args = TypeArguments::New(1);
|
||||||
|
type_args.SetTypeAt(Pointer::kNativeTypeArgPos, type_arg);
|
||||||
|
type_args = type_args.Canonicalize();
|
||||||
|
|
||||||
|
Class& native_function_class =
|
||||||
|
Class::Handle(isolate->class_table()->At(kFfiNativeFunctionCid));
|
||||||
|
const auto& error =
|
||||||
|
Error::Handle(native_function_class.EnsureIsFinalized(Thread::Current()));
|
||||||
|
if (!error.IsNull()) {
|
||||||
|
Exceptions::PropagateError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type& native_function_type = Type::Handle(
|
||||||
|
Type::New(native_function_class, type_args, TokenPosition::kNoSource));
|
||||||
|
native_function_type ^=
|
||||||
|
ClassFinalizer::FinalizeType(Class::Handle(), native_function_type);
|
||||||
|
native_function_type ^= native_function_type.Canonicalize();
|
||||||
|
|
||||||
|
// The FE verifies that the target of a 'fromFunction' is a static method, so
|
||||||
|
// the value we see here must be a static tearoff. See ffi_use_sites.dart for
|
||||||
|
// details.
|
||||||
|
//
|
||||||
|
// TODO(36748): Define hot-reload semantics of native callbacks. We may need
|
||||||
|
// to look up the target by name.
|
||||||
|
ASSERT(func.IsImplicitClosureFunction());
|
||||||
|
func = func.parent_function();
|
||||||
|
ASSERT(func.is_static());
|
||||||
|
|
||||||
|
const AbstractType& return_type =
|
||||||
|
AbstractType::Handle(native_signature.result_type());
|
||||||
|
if (compiler::ffi::NativeTypeIsVoid(return_type)) {
|
||||||
|
if (!exceptional_return.IsNull()) {
|
||||||
|
const String& error = String::Handle(
|
||||||
|
String::NewFormatted("Exceptional return argument to 'fromFunction' "
|
||||||
|
"must be null for functions returning void."));
|
||||||
|
Exceptions::ThrowArgumentError(error);
|
||||||
|
}
|
||||||
|
} else if (!compiler::ffi::NativeTypeIsPointer(return_type) &&
|
||||||
|
exceptional_return.IsNull()) {
|
||||||
|
const String& error = String::Handle(String::NewFormatted(
|
||||||
|
"Exceptional return argument to 'fromFunction' must not be null."));
|
||||||
|
Exceptions::ThrowArgumentError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pointer::New(
|
||||||
|
native_function_type,
|
||||||
|
CompileNativeCallback(native_signature, func, exceptional_return));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_NATIVE_ENTRY(Ffi_asExternalTypedData, 0, 2) {
|
DEFINE_NATIVE_ENTRY(Ffi_asExternalTypedData, 0, 2) {
|
||||||
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
|
||||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, count, arguments->NativeArgAt(1));
|
GET_NON_NULL_NATIVE_ARGUMENT(Integer, count, arguments->NativeArgAt(1));
|
||||||
|
@ -536,8 +692,8 @@ DEFINE_NATIVE_ENTRY(Ffi_asExternalTypedData, 0, 2) {
|
||||||
|
|
||||||
const auto& typed_data_class =
|
const auto& typed_data_class =
|
||||||
Class::Handle(zone, isolate->class_table()->At(cid));
|
Class::Handle(zone, isolate->class_table()->At(cid));
|
||||||
const auto& error =
|
const auto& error = Error::Handle(
|
||||||
Error::Handle(zone, typed_data_class.EnsureIsFinalized(thread));
|
zone, typed_data_class.EnsureIsFinalized(Thread::Current()));
|
||||||
if (!error.IsNull()) {
|
if (!error.IsNull()) {
|
||||||
Exceptions::PropagateError(error);
|
Exceptions::PropagateError(error);
|
||||||
}
|
}
|
||||||
|
@ -547,91 +703,6 @@ DEFINE_NATIVE_ENTRY(Ffi_asExternalTypedData, 0, 2) {
|
||||||
Heap::kNew);
|
Heap::kNew);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_NATIVE_ENTRY(Ffi_nativeCallbackFunction, 1, 2) {
|
|
||||||
#if defined(TARGET_ARCH_DBC)
|
|
||||||
Exceptions::ThrowUnsupportedError(
|
|
||||||
"FFI callbacks are not yet supported on DBC.");
|
|
||||||
#elif defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
|
|
||||||
// Calls to this function are removed by the flow-graph builder in AOT.
|
|
||||||
// See StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction().
|
|
||||||
UNREACHABLE();
|
|
||||||
#else
|
|
||||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
|
||||||
GET_NON_NULL_NATIVE_ARGUMENT(Closure, closure, arguments->NativeArgAt(0));
|
|
||||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, exceptional_return,
|
|
||||||
arguments->NativeArgAt(1));
|
|
||||||
|
|
||||||
ASSERT(type_arg.IsInstantiated() && type_arg.IsFunctionType());
|
|
||||||
const Function& native_signature =
|
|
||||||
Function::Handle(zone, Type::Cast(type_arg).signature());
|
|
||||||
Function& func = Function::Handle(zone, closure.function());
|
|
||||||
|
|
||||||
// The FE verifies that the target of a 'fromFunction' is a static method, so
|
|
||||||
// the value we see here must be a static tearoff. See ffi_use_sites.dart for
|
|
||||||
// details.
|
|
||||||
//
|
|
||||||
// TODO(36748): Define hot-reload semantics of native callbacks. We may need
|
|
||||||
// to look up the target by name.
|
|
||||||
ASSERT(func.IsImplicitClosureFunction());
|
|
||||||
func = func.parent_function();
|
|
||||||
ASSERT(func.is_static());
|
|
||||||
|
|
||||||
// We are returning an object which is not an Instance here. This is only OK
|
|
||||||
// because we know that the result will be passed directly to
|
|
||||||
// _pointerFromFunction and will not leak out into user code.
|
|
||||||
arguments->SetReturn(
|
|
||||||
Function::Handle(zone, compiler::ffi::NativeCallbackFunction(
|
|
||||||
native_signature, func, exceptional_return)));
|
|
||||||
|
|
||||||
// Because we have already set the return value.
|
|
||||||
return Object::sentinel().raw();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFINE_NATIVE_ENTRY(Ffi_pointerFromFunction, 1, 1) {
|
|
||||||
GET_NATIVE_TYPE_ARGUMENT(type_arg, arguments->NativeTypeArgAt(0));
|
|
||||||
const Function& function =
|
|
||||||
Function::CheckedHandle(zone, arguments->NativeArg0());
|
|
||||||
|
|
||||||
Code& code = Code::Handle(zone);
|
|
||||||
|
|
||||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
|
||||||
code = function.CurrentCode();
|
|
||||||
|
|
||||||
// Blobs snapshots don't support BSS-relative relocations required by native
|
|
||||||
// callbacks (yet). Issue an error if the code has an unpatched relocation.
|
|
||||||
if (!code.VerifyBSSRelocations()) {
|
|
||||||
Exceptions::ThrowUnsupportedError(
|
|
||||||
"FFI callbacks are not yet supported in blobs snapshots. Please use "
|
|
||||||
"ELF or Assembly snapshots instead.");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
// We compile the callback immediately because we need to return a pointer to
|
|
||||||
// the entry-point. Native calls do not use patching like Dart calls, so we
|
|
||||||
// cannot compile it lazily.
|
|
||||||
const Object& result = Object::Handle(
|
|
||||||
zone, Compiler::CompileOptimizedFunction(thread, function));
|
|
||||||
if (result.IsError()) {
|
|
||||||
Exceptions::PropagateError(Error::Cast(result));
|
|
||||||
}
|
|
||||||
ASSERT(result.IsCode());
|
|
||||||
code ^= result.raw();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ASSERT(!code.IsNull());
|
|
||||||
thread->SetFfiCallbackCode(function.FfiCallbackId(), code);
|
|
||||||
|
|
||||||
uword entry_point = code.EntryPoint();
|
|
||||||
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(TARGET_ARCH_DBC)
|
|
||||||
if (NativeCallbackTrampolines::Enabled()) {
|
|
||||||
entry_point = isolate->native_callback_trampolines()->TrampolineForId(
|
|
||||||
function.FfiCallbackId());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return Pointer::New(type_arg, entry_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(TARGET_ARCH_DBC)
|
#if defined(TARGET_ARCH_DBC)
|
||||||
|
|
||||||
void FfiMarshalledArguments::SetFunctionAddress(uint64_t value) const {
|
void FfiMarshalledArguments::SetFunctionAddress(uint64_t value) const {
|
||||||
|
|
|
@ -23,25 +23,6 @@ DS _asFunctionInternal<DS extends Function, NS extends Function>(
|
||||||
dynamic _asExternalTypedData(Pointer ptr, int count)
|
dynamic _asExternalTypedData(Pointer ptr, int count)
|
||||||
native "Ffi_asExternalTypedData";
|
native "Ffi_asExternalTypedData";
|
||||||
|
|
||||||
// Returns a Function object for a native callback.
|
|
||||||
//
|
|
||||||
// Calls to [Pointer.fromFunction] are re-written by the FE into calls to this
|
|
||||||
// method + _pointerFromFunction. All three arguments must be constants.
|
|
||||||
//
|
|
||||||
// In AOT we evaluate calls to this function during precompilation and replace
|
|
||||||
// them with Constant instruction referencing the callback trampoline, to ensure
|
|
||||||
// that it will be precompiled.
|
|
||||||
//
|
|
||||||
// In all JIT modes we call a native runtime entry. We *cannot* use the IL
|
|
||||||
// implementation, since that would pull the callback trampoline into JIT
|
|
||||||
// snapshots. The callback trampolines can only be serialized into AOT snapshots
|
|
||||||
// because they embed the addresses of runtime routines in JIT mode.
|
|
||||||
Object _nativeCallbackFunction<NS extends Function>(Function target,
|
|
||||||
Object exceptionalReturn) native "Ffi_nativeCallbackFunction";
|
|
||||||
|
|
||||||
Pointer<NS> _pointerFromFunction<NS extends NativeFunction>(Object function)
|
|
||||||
native "Ffi_pointerFromFunction";
|
|
||||||
|
|
||||||
@patch
|
@patch
|
||||||
@pragma("vm:entry-point")
|
@pragma("vm:entry-point")
|
||||||
class Pointer<T extends NativeType> {
|
class Pointer<T extends NativeType> {
|
||||||
|
@ -51,18 +32,10 @@ class Pointer<T extends NativeType> {
|
||||||
@patch
|
@patch
|
||||||
factory Pointer.fromAddress(int ptr) => _fromAddress(ptr);
|
factory Pointer.fromAddress(int ptr) => _fromAddress(ptr);
|
||||||
|
|
||||||
// All static calls to this method are replaced by the FE into
|
|
||||||
// _nativeCallbackFunction + _pointerFromFunction.
|
|
||||||
//
|
|
||||||
// We still need to throw an error on a dynamic invocations, invocations
|
|
||||||
// through tearoffs or reflective calls.
|
|
||||||
@patch
|
@patch
|
||||||
static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
||||||
@DartRepresentationOf("T") Function f,
|
@DartRepresentationOf("T") Function f,
|
||||||
[Object exceptionalReturn]) {
|
Object exceptionalReturn) native "Ffi_fromFunction";
|
||||||
throw UnsupportedError(
|
|
||||||
"Pointer.fromFunction cannot be called dynamically.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(sjindel): When NNBD is available, we should change `value` to be
|
// TODO(sjindel): When NNBD is available, we should change `value` to be
|
||||||
// non-null.
|
// non-null.
|
||||||
|
|
|
@ -381,8 +381,7 @@ namespace dart {
|
||||||
V(Ffi_cast, 1) \
|
V(Ffi_cast, 1) \
|
||||||
V(Ffi_sizeOf, 0) \
|
V(Ffi_sizeOf, 0) \
|
||||||
V(Ffi_asFunctionInternal, 1) \
|
V(Ffi_asFunctionInternal, 1) \
|
||||||
V(Ffi_nativeCallbackFunction, 2) \
|
V(Ffi_fromFunction, 2) \
|
||||||
V(Ffi_pointerFromFunction, 1) \
|
|
||||||
V(Ffi_dl_open, 1) \
|
V(Ffi_dl_open, 1) \
|
||||||
V(Ffi_dl_lookup, 2) \
|
V(Ffi_dl_lookup, 2) \
|
||||||
V(Ffi_dl_getHandle, 1) \
|
V(Ffi_dl_getHandle, 1) \
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright (c) 2019, 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.
|
|
||||||
|
|
||||||
#include <vm/bss_relocs.h>
|
|
||||||
#include <vm/runtime_entry.h>
|
|
||||||
#include <vm/thread.h>
|
|
||||||
|
|
||||||
namespace dart {
|
|
||||||
|
|
||||||
void BSS::Initialize(Thread* current, uword* bss_start) {
|
|
||||||
bss_start[BSS::RelocationIndex(
|
|
||||||
BSS::Relocation::DRT_GetThreadForNativeCallback)] =
|
|
||||||
reinterpret_cast<uword>(DLRT_GetThreadForNativeCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dart
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright (c) 2019, 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.
|
|
||||||
|
|
||||||
#ifndef RUNTIME_VM_BSS_RELOCS_H_
|
|
||||||
#define RUNTIME_VM_BSS_RELOCS_H_
|
|
||||||
|
|
||||||
#include <platform/allocation.h>
|
|
||||||
|
|
||||||
namespace dart {
|
|
||||||
class Thread;
|
|
||||||
|
|
||||||
class BSS : public AllStatic {
|
|
||||||
public:
|
|
||||||
enum class Relocation : intptr_t {
|
|
||||||
DRT_GetThreadForNativeCallback = 0,
|
|
||||||
NumRelocations = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
static intptr_t RelocationIndex(Relocation reloc) {
|
|
||||||
return static_cast<intptr_t>(reloc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Initialize(Thread* current, uword* bss);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace dart
|
|
||||||
|
|
||||||
#endif // RUNTIME_VM_BSS_RELOCS_H_
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#include "platform/assert.h"
|
#include "platform/assert.h"
|
||||||
#include "vm/bootstrap.h"
|
#include "vm/bootstrap.h"
|
||||||
#include "vm/bss_relocs.h"
|
|
||||||
#include "vm/class_id.h"
|
#include "vm/class_id.h"
|
||||||
#include "vm/code_observers.h"
|
#include "vm/code_observers.h"
|
||||||
#include "vm/compiler/backend/code_statistics.h"
|
#include "vm/compiler/backend/code_statistics.h"
|
||||||
|
@ -801,12 +800,9 @@ class FfiTrampolineDataSerializationCluster : public SerializationCluster {
|
||||||
AutoTraceObject(data);
|
AutoTraceObject(data);
|
||||||
WriteFromTo(data);
|
WriteFromTo(data);
|
||||||
|
|
||||||
if (s->kind() == Snapshot::kFullAOT) {
|
// TODO(37295): FFI callbacks shouldn't be written to a snapshot. They
|
||||||
s->WriteUnsigned(data->ptr()->callback_id_);
|
// should only be referenced by the callback registry in Thread.
|
||||||
} else {
|
ASSERT(data->ptr()->callback_id_ == 0);
|
||||||
// FFI callbacks can only be written to AOT snapshots.
|
|
||||||
ASSERT(data->ptr()->callback_target_ == Object::null());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -838,8 +834,7 @@ class FfiTrampolineDataDeserializationCluster : public DeserializationCluster {
|
||||||
Deserializer::InitializeHeader(data, kFfiTrampolineDataCid,
|
Deserializer::InitializeHeader(data, kFfiTrampolineDataCid,
|
||||||
FfiTrampolineData::InstanceSize());
|
FfiTrampolineData::InstanceSize());
|
||||||
ReadFromTo(data);
|
ReadFromTo(data);
|
||||||
data->ptr()->callback_id_ =
|
data->ptr()->callback_id_ = 0;
|
||||||
d->kind() == Snapshot::kFullAOT ? d->ReadUnsigned() : 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -5752,17 +5747,6 @@ RawApiError* FullSnapshotReader::ReadIsolateSnapshot() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize symbols in the BSS, if present.
|
|
||||||
ASSERT(Snapshot::IncludesCode(kind_));
|
|
||||||
Image image(instructions_image_);
|
|
||||||
if (image.bss_offset() != 0) {
|
|
||||||
// The const cast is safe because we're translating from the start of the
|
|
||||||
// instructions (read-only) to the start of the BSS (read-write).
|
|
||||||
uword* const bss_start = const_cast<uword*>(reinterpret_cast<const uword*>(
|
|
||||||
instructions_image_ + image.bss_offset()));
|
|
||||||
BSS::Initialize(thread_, bss_start);
|
|
||||||
}
|
|
||||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
|
||||||
return ApiError::null();
|
return ApiError::null();
|
||||||
|
|
|
@ -15,13 +15,10 @@ void DescriptorList::AddDescriptor(RawPcDescriptors::Kind kind,
|
||||||
TokenPosition token_pos,
|
TokenPosition token_pos,
|
||||||
intptr_t try_index) {
|
intptr_t try_index) {
|
||||||
ASSERT((kind == RawPcDescriptors::kRuntimeCall) ||
|
ASSERT((kind == RawPcDescriptors::kRuntimeCall) ||
|
||||||
(kind == RawPcDescriptors::kBSSRelocation) ||
|
|
||||||
(kind == RawPcDescriptors::kOther) || (deopt_id != DeoptId::kNone));
|
(kind == RawPcDescriptors::kOther) || (deopt_id != DeoptId::kNone));
|
||||||
|
|
||||||
// When precompiling, we only use pc descriptors for exceptions and
|
// When precompiling, we only use pc descriptors for exceptions.
|
||||||
// relocations.
|
if (!FLAG_precompiled_mode || try_index != -1) {
|
||||||
if (!FLAG_precompiled_mode || try_index != -1 ||
|
|
||||||
kind == RawPcDescriptors::kBSSRelocation) {
|
|
||||||
int32_t merged_kind_try =
|
int32_t merged_kind_try =
|
||||||
RawPcDescriptors::MergedKindTry::Encode(kind, try_index);
|
RawPcDescriptors::MergedKindTry::Encode(kind, try_index);
|
||||||
|
|
||||||
|
|
|
@ -30,31 +30,6 @@ DEFINE_FLAG(bool, use_far_branches, false, "Enable far branches for ARM.");
|
||||||
|
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
|
|
||||||
AssemblerBase::~AssemblerBase() {}
|
|
||||||
|
|
||||||
intptr_t AssemblerBase::InsertAlignedRelocation(BSS::Relocation reloc) {
|
|
||||||
// We cannot put a relocation at the very start (it's not a valid
|
|
||||||
// instruction)!
|
|
||||||
ASSERT(CodeSize() != 0);
|
|
||||||
|
|
||||||
// Align to a target word boundary.
|
|
||||||
const intptr_t offset =
|
|
||||||
Utils::RoundUp(CodeSize(), compiler::target::kWordSize);
|
|
||||||
|
|
||||||
while (CodeSize() < offset) {
|
|
||||||
Breakpoint();
|
|
||||||
}
|
|
||||||
ASSERT(CodeSize() == offset);
|
|
||||||
|
|
||||||
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
|
|
||||||
buffer_.Emit<compiler::target::word>(BSS::RelocationIndex(reloc) *
|
|
||||||
compiler::target::kWordSize);
|
|
||||||
|
|
||||||
ASSERT(CodeSize() == (offset + compiler::target::kWordSize));
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uword NewContents(intptr_t capacity) {
|
static uword NewContents(intptr_t capacity) {
|
||||||
Zone* zone = Thread::Current()->zone();
|
Zone* zone = Thread::Current()->zone();
|
||||||
uword result = zone->AllocUnsafe(capacity);
|
uword result = zone->AllocUnsafe(capacity);
|
||||||
|
|
|
@ -305,7 +305,7 @@ class AssemblerBase : public StackResource {
|
||||||
prologue_offset_(-1),
|
prologue_offset_(-1),
|
||||||
has_single_entry_point_(true),
|
has_single_entry_point_(true),
|
||||||
object_pool_builder_(object_pool_builder) {}
|
object_pool_builder_(object_pool_builder) {}
|
||||||
virtual ~AssemblerBase();
|
virtual ~AssemblerBase() {}
|
||||||
|
|
||||||
intptr_t CodeSize() const { return buffer_.Size(); }
|
intptr_t CodeSize() const { return buffer_.Size(); }
|
||||||
|
|
||||||
|
@ -320,10 +320,6 @@ class AssemblerBase : public StackResource {
|
||||||
void Comment(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
|
void Comment(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
|
||||||
static bool EmittingComments();
|
static bool EmittingComments();
|
||||||
|
|
||||||
virtual void Breakpoint() = 0;
|
|
||||||
|
|
||||||
intptr_t InsertAlignedRelocation(BSS::Relocation reloc);
|
|
||||||
|
|
||||||
void Unimplemented(const char* message);
|
void Unimplemented(const char* message);
|
||||||
void Untested(const char* message);
|
void Untested(const char* message);
|
||||||
void Unreachable(const char* message);
|
void Unreachable(const char* message);
|
||||||
|
|
|
@ -392,7 +392,7 @@ class Assembler : public AssemblerBase {
|
||||||
#endif // TESTING || DEBUG
|
#endif // TESTING || DEBUG
|
||||||
|
|
||||||
// Debugging and bringup support.
|
// Debugging and bringup support.
|
||||||
void Breakpoint() override { bkpt(0); }
|
void Breakpoint() { bkpt(0); }
|
||||||
void Stop(const char* message) override;
|
void Stop(const char* message) override;
|
||||||
|
|
||||||
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
||||||
|
|
|
@ -485,7 +485,7 @@ class Assembler : public AssemblerBase {
|
||||||
void set_use_far_branches(bool b) { use_far_branches_ = b; }
|
void set_use_far_branches(bool b) { use_far_branches_ = b; }
|
||||||
|
|
||||||
// Debugging and bringup support.
|
// Debugging and bringup support.
|
||||||
void Breakpoint() override { brk(0); }
|
void Breakpoint() { brk(0); }
|
||||||
void Stop(const char* message) override;
|
void Stop(const char* message) override;
|
||||||
|
|
||||||
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
||||||
|
|
|
@ -43,7 +43,6 @@ class Assembler : public AssemblerBase {
|
||||||
void MonomorphicCheckedEntryAOT() {}
|
void MonomorphicCheckedEntryAOT() {}
|
||||||
|
|
||||||
// Debugging and bringup support.
|
// Debugging and bringup support.
|
||||||
void Breakpoint() override { Stop("Breakpoint!"); }
|
|
||||||
void Stop(const char* message) override;
|
void Stop(const char* message) override;
|
||||||
|
|
||||||
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
||||||
|
|
|
@ -835,7 +835,7 @@ class Assembler : public AssemblerBase {
|
||||||
Register temp);
|
Register temp);
|
||||||
|
|
||||||
// Debugging and bringup support.
|
// Debugging and bringup support.
|
||||||
void Breakpoint() override { int3(); }
|
void Breakpoint() { int3(); }
|
||||||
void Stop(const char* message) override;
|
void Stop(const char* message) override;
|
||||||
|
|
||||||
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
||||||
|
|
|
@ -936,7 +936,7 @@ class Assembler : public AssemblerBase {
|
||||||
void GenerateUnRelocatedPcRelativeCall(intptr_t offset_into_target = 0);
|
void GenerateUnRelocatedPcRelativeCall(intptr_t offset_into_target = 0);
|
||||||
|
|
||||||
// Debugging and bringup support.
|
// Debugging and bringup support.
|
||||||
void Breakpoint() override { int3(); }
|
void Breakpoint() { int3(); }
|
||||||
void Stop(const char* message) override;
|
void Stop(const char* message) override;
|
||||||
|
|
||||||
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
static void InitializeMemoryWithBreakpoints(uword data, intptr_t length);
|
||||||
|
|
|
@ -242,13 +242,6 @@ bool FlowGraphCompiler::CanOSRFunction() const {
|
||||||
return isolate()->use_osr() && CanOptimizeFunction() && !is_optimizing();
|
return isolate()->use_osr() && CanOptimizeFunction() && !is_optimizing();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlowGraphCompiler::InsertBSSRelocation(BSS::Relocation reloc) {
|
|
||||||
const intptr_t offset = assembler()->InsertAlignedRelocation(reloc);
|
|
||||||
AddDescriptor(RawPcDescriptors::kBSSRelocation, /*pc_offset=*/offset,
|
|
||||||
/*deopt_id=*/DeoptId::kNone, TokenPosition::kNoSource,
|
|
||||||
/*try_index=*/-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FlowGraphCompiler::ForceSlowPathForStackOverflow() const {
|
bool FlowGraphCompiler::ForceSlowPathForStackOverflow() const {
|
||||||
#if !defined(PRODUCT)
|
#if !defined(PRODUCT)
|
||||||
if ((FLAG_stacktrace_every > 0) || (FLAG_deoptimize_every > 0) ||
|
if ((FLAG_stacktrace_every > 0) || (FLAG_deoptimize_every > 0) ||
|
||||||
|
@ -1408,9 +1401,7 @@ void FlowGraphCompiler::GenerateStaticCall(intptr_t deopt_id,
|
||||||
: ic_data.arguments_descriptor());
|
: ic_data.arguments_descriptor());
|
||||||
ASSERT(ArgumentsDescriptor(arguments_descriptor).TypeArgsLen() ==
|
ASSERT(ArgumentsDescriptor(arguments_descriptor).TypeArgsLen() ==
|
||||||
args_info.type_args_len);
|
args_info.type_args_len);
|
||||||
// Force-optimized functions lack the deopt info which allows patching of
|
if (is_optimizing() && !ForcedOptimization()) {
|
||||||
// optimized static calls.
|
|
||||||
if (is_optimizing() && (!ForcedOptimization() || FLAG_precompiled_mode)) {
|
|
||||||
EmitOptimizedStaticCall(function, arguments_descriptor,
|
EmitOptimizedStaticCall(function, arguments_descriptor,
|
||||||
args_info.count_with_type_args, deopt_id, token_pos,
|
args_info.count_with_type_args, deopt_id, token_pos,
|
||||||
locs, entry_kind);
|
locs, entry_kind);
|
||||||
|
|
|
@ -428,8 +428,6 @@ class FlowGraphCompiler : public ValueObject {
|
||||||
bool CanOSRFunction() const;
|
bool CanOSRFunction() const;
|
||||||
bool is_optimizing() const { return is_optimizing_; }
|
bool is_optimizing() const { return is_optimizing_; }
|
||||||
|
|
||||||
void InsertBSSRelocation(BSS::Relocation reloc);
|
|
||||||
|
|
||||||
// The function was fully intrinsified, so the body is unreachable.
|
// The function was fully intrinsified, so the body is unreachable.
|
||||||
//
|
//
|
||||||
// We still need to compile the body in unoptimized mode because the
|
// We still need to compile the body in unoptimized mode because the
|
||||||
|
|
|
@ -1137,6 +1137,10 @@ void NativeEntryInstr::SaveArgument(FlowGraphCompiler* compiler,
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
|
if (FLAG_precompiled_mode) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
// Constant pool cannot be used until we enter the actual Dart frame.
|
// Constant pool cannot be used until we enter the actual Dart frame.
|
||||||
__ set_constant_pool_allowed(false);
|
__ set_constant_pool_allowed(false);
|
||||||
|
|
||||||
|
@ -1161,41 +1165,8 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
|
|
||||||
// Load the thread object. If we were called by a trampoline, the thread is
|
// Load the thread object. If we were called by a trampoline, the thread is
|
||||||
// already loaded.
|
// already loaded.
|
||||||
if (FLAG_precompiled_mode) {
|
//
|
||||||
compiler::Label skip_reloc;
|
// TODO(35765): Fix linking issue on AOT.
|
||||||
__ b(&skip_reloc);
|
|
||||||
compiler->InsertBSSRelocation(
|
|
||||||
BSS::Relocation::DRT_GetThreadForNativeCallback);
|
|
||||||
__ Bind(&skip_reloc);
|
|
||||||
|
|
||||||
// For historical reasons, the PC on ARM points 8 bytes (two instructions)
|
|
||||||
// past the current instruction.
|
|
||||||
__ sub(
|
|
||||||
R0, PC,
|
|
||||||
compiler::Operand(Instr::kPCReadOffset + compiler::target::kWordSize));
|
|
||||||
|
|
||||||
// R0 holds the address of the relocation.
|
|
||||||
__ ldr(R1, compiler::Address(R0));
|
|
||||||
|
|
||||||
// R1 holds the relocation itself: R0 - bss_start.
|
|
||||||
// R0 = R0 + (bss_start - R0) = bss_start
|
|
||||||
__ add(R0, R0, compiler::Operand(R1));
|
|
||||||
|
|
||||||
// R0 holds the start of the BSS section.
|
|
||||||
// Load the "get-thread" routine: *bss_start.
|
|
||||||
__ ldr(R1, compiler::Address(R0));
|
|
||||||
} else if (!NativeCallbackTrampolines::Enabled()) {
|
|
||||||
// In JIT mode, we can just paste the address of the runtime entry into the
|
|
||||||
// generated code directly. This is not a problem since we don't save
|
|
||||||
// callbacks into JIT snapshots.
|
|
||||||
ASSERT(kWordSize == compiler::target::kWordSize);
|
|
||||||
__ LoadImmediate(
|
|
||||||
R1, static_cast<compiler::target::uword>(
|
|
||||||
reinterpret_cast<uword>(DLRT_GetThreadForNativeCallback)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the thread object. If we were called by a trampoline, the thread is
|
|
||||||
// already loaded.
|
|
||||||
if (!NativeCallbackTrampolines::Enabled()) {
|
if (!NativeCallbackTrampolines::Enabled()) {
|
||||||
// Create another frame to align the frame before continuing in "native"
|
// Create another frame to align the frame before continuing in "native"
|
||||||
// code.
|
// code.
|
||||||
|
@ -1203,6 +1174,8 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
__ ReserveAlignedFrameSpace(0);
|
__ ReserveAlignedFrameSpace(0);
|
||||||
|
|
||||||
__ LoadImmediate(R0, callback_id_);
|
__ LoadImmediate(R0, callback_id_);
|
||||||
|
__ LoadImmediate(
|
||||||
|
R1, reinterpret_cast<int64_t>(DLRT_GetThreadForNativeCallback));
|
||||||
__ blx(R1);
|
__ blx(R1);
|
||||||
__ mov(THR, compiler::Operand(R0));
|
__ mov(THR, compiler::Operand(R0));
|
||||||
|
|
||||||
|
@ -6981,8 +6954,7 @@ LocationSummary* BitCastInstr::MakeLocationSummary(Zone* zone, bool opt) const {
|
||||||
break;
|
break;
|
||||||
case kUnboxedFloat:
|
case kUnboxedFloat:
|
||||||
case kUnboxedDouble:
|
case kUnboxedDouble:
|
||||||
// Choose an FPU register with corresponding D and S registers.
|
summary->set_in(0, Location::RequiresFpuRegister());
|
||||||
summary->set_in(0, Location::FpuRegisterLocation(Q0));
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
@ -6998,8 +6970,7 @@ LocationSummary* BitCastInstr::MakeLocationSummary(Zone* zone, bool opt) const {
|
||||||
break;
|
break;
|
||||||
case kUnboxedFloat:
|
case kUnboxedFloat:
|
||||||
case kUnboxedDouble:
|
case kUnboxedDouble:
|
||||||
// Choose an FPU register with corresponding D and S registers.
|
summary->set_out(0, Location::RequiresFpuRegister());
|
||||||
summary->set_out(0, Location::FpuRegisterLocation(Q0));
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
|
|
@ -1020,6 +1020,10 @@ void NativeEntryInstr::SaveArgument(FlowGraphCompiler* compiler,
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
|
if (FLAG_precompiled_mode) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
// Constant pool cannot be used until we enter the actual Dart frame.
|
// Constant pool cannot be used until we enter the actual Dart frame.
|
||||||
__ set_constant_pool_allowed(false);
|
__ set_constant_pool_allowed(false);
|
||||||
|
|
||||||
|
@ -1048,33 +1052,8 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
|
|
||||||
// Load the thread object. If we were called by a trampoline, the thread is
|
// Load the thread object. If we were called by a trampoline, the thread is
|
||||||
// already loaded.
|
// already loaded.
|
||||||
if (FLAG_precompiled_mode) {
|
//
|
||||||
compiler::Label skip_reloc;
|
// TODO(35765): Fix linking issue on AOT.
|
||||||
__ b(&skip_reloc);
|
|
||||||
compiler->InsertBSSRelocation(
|
|
||||||
BSS::Relocation::DRT_GetThreadForNativeCallback);
|
|
||||||
__ Bind(&skip_reloc);
|
|
||||||
|
|
||||||
__ adr(R0, compiler::Immediate(-compiler::target::kWordSize));
|
|
||||||
|
|
||||||
// R0 holds the address of the relocation.
|
|
||||||
__ ldr(R1, compiler::Address(R0));
|
|
||||||
|
|
||||||
// R1 holds the relocation itself: R0 - bss_start.
|
|
||||||
// R0 = R0 + (bss_start - R0) = bss_start
|
|
||||||
__ add(R0, R0, compiler::Operand(R1));
|
|
||||||
|
|
||||||
// R0 holds the start of the BSS section.
|
|
||||||
// Load the "get-thread" routine: *bss_start.
|
|
||||||
__ ldr(R1, compiler::Address(R0));
|
|
||||||
} else if (!NativeCallbackTrampolines::Enabled()) {
|
|
||||||
// In JIT mode, we can just paste the address of the runtime entry into the
|
|
||||||
// generated code directly. This is not a problem since we don't save
|
|
||||||
// callbacks into JIT snapshots.
|
|
||||||
__ LoadImmediate(
|
|
||||||
R1, reinterpret_cast<int64_t>(DLRT_GetThreadForNativeCallback));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NativeCallbackTrampolines::Enabled()) {
|
if (!NativeCallbackTrampolines::Enabled()) {
|
||||||
// Create another frame to align the frame before continuing in "native"
|
// Create another frame to align the frame before continuing in "native"
|
||||||
// code.
|
// code.
|
||||||
|
@ -1082,6 +1061,8 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
__ ReserveAlignedFrameSpace(0);
|
__ ReserveAlignedFrameSpace(0);
|
||||||
|
|
||||||
__ LoadImmediate(R0, callback_id_);
|
__ LoadImmediate(R0, callback_id_);
|
||||||
|
__ LoadImmediate(
|
||||||
|
R1, reinterpret_cast<int64_t>(DLRT_GetThreadForNativeCallback));
|
||||||
__ blr(R1);
|
__ blr(R1);
|
||||||
__ mov(THR, R0);
|
__ mov(THR, R0);
|
||||||
|
|
||||||
|
|
|
@ -1036,16 +1036,15 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
__ pushl(EDI);
|
__ pushl(EDI);
|
||||||
|
|
||||||
// Load the thread object.
|
// Load the thread object.
|
||||||
//
|
// Linking in AOT is not relevant here since we don't support AOT for IA32.
|
||||||
// Create another frame to align the frame before continuing in "native" code.
|
// Create another frame to align the frame before continuing in "native" code.
|
||||||
// If we were called by a trampoline, it has already loaded the thread.
|
// If we were called by a trampoline, it has already loaded the thread.
|
||||||
ASSERT(!FLAG_precompiled_mode); // No relocation for AOT linking.
|
|
||||||
if (!NativeCallbackTrampolines::Enabled()) {
|
if (!NativeCallbackTrampolines::Enabled()) {
|
||||||
__ EnterFrame(0);
|
__ EnterFrame(0);
|
||||||
__ ReserveAlignedFrameSpace(compiler::target::kWordSize);
|
__ ReserveAlignedFrameSpace(compiler::target::kWordSize);
|
||||||
|
|
||||||
__ movl(compiler::Address(SPREG, 0), compiler::Immediate(callback_id_));
|
__ movl(compiler::Address(SPREG, 0), compiler::Immediate(callback_id_));
|
||||||
__ movl(EAX, compiler::Immediate(reinterpret_cast<intptr_t>(
|
__ movl(EAX, compiler::Immediate(reinterpret_cast<int64_t>(
|
||||||
DLRT_GetThreadForNativeCallback)));
|
DLRT_GetThreadForNativeCallback)));
|
||||||
__ call(EAX);
|
__ call(EAX);
|
||||||
__ movl(THR, EAX);
|
__ movl(THR, EAX);
|
||||||
|
|
|
@ -1024,6 +1024,10 @@ void NativeEntryInstr::SaveArgument(FlowGraphCompiler* compiler,
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
|
if (FLAG_precompiled_mode) {
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
__ Bind(compiler->GetJumpLabel(this));
|
__ Bind(compiler->GetJumpLabel(this));
|
||||||
|
|
||||||
// Create a dummy frame holding the pushed arguments. This simplifies
|
// Create a dummy frame holding the pushed arguments. This simplifies
|
||||||
|
@ -1051,38 +1055,9 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
__ PushRegisters(CallingConventions::kCalleeSaveCpuRegisters,
|
__ PushRegisters(CallingConventions::kCalleeSaveCpuRegisters,
|
||||||
CallingConventions::kCalleeSaveXmmRegisters);
|
CallingConventions::kCalleeSaveXmmRegisters);
|
||||||
|
|
||||||
// Load the address of DLRT_GetThreadForNativeCallback without using Thread.
|
// Load the thread object.
|
||||||
if (FLAG_precompiled_mode) {
|
// TODO(35765): Fix linking issue on AOT.
|
||||||
compiler::Label skip_reloc;
|
//
|
||||||
__ jmp(&skip_reloc);
|
|
||||||
compiler->InsertBSSRelocation(
|
|
||||||
BSS::Relocation::DRT_GetThreadForNativeCallback);
|
|
||||||
const intptr_t reloc_end = __ CodeSize();
|
|
||||||
__ Bind(&skip_reloc);
|
|
||||||
|
|
||||||
const intptr_t kLeaqLength = 7;
|
|
||||||
__ leaq(RAX, compiler::Address::AddressRIPRelative(
|
|
||||||
-kLeaqLength - compiler::target::kWordSize));
|
|
||||||
ASSERT((__ CodeSize() - reloc_end) == kLeaqLength);
|
|
||||||
|
|
||||||
// RAX holds the address of the relocation.
|
|
||||||
__ movq(RCX, compiler::Address(RAX, 0));
|
|
||||||
|
|
||||||
// RCX holds the relocation itself: RAX - bss_start.
|
|
||||||
// RAX = RAX + (bss_start - RAX) = bss_start
|
|
||||||
__ addq(RAX, RCX);
|
|
||||||
|
|
||||||
// RAX holds the start of the BSS section.
|
|
||||||
// Load the "get-thread" routine: *bss_start.
|
|
||||||
__ movq(RAX, compiler::Address(RAX, 0));
|
|
||||||
} else if (!NativeCallbackTrampolines::Enabled()) {
|
|
||||||
// In JIT mode, we can just paste the address of the runtime entry into the
|
|
||||||
// generated code directly. This is not a problem since we don't save
|
|
||||||
// callbacks into JIT snapshots.
|
|
||||||
__ movq(RAX, compiler::Immediate(reinterpret_cast<intptr_t>(
|
|
||||||
DLRT_GetThreadForNativeCallback)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create another frame to align the frame before continuing in "native" code.
|
// Create another frame to align the frame before continuing in "native" code.
|
||||||
// If we were called by a trampoline, it has already loaded the thread.
|
// If we were called by a trampoline, it has already loaded the thread.
|
||||||
if (!NativeCallbackTrampolines::Enabled()) {
|
if (!NativeCallbackTrampolines::Enabled()) {
|
||||||
|
@ -1091,6 +1066,8 @@ void NativeEntryInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
|
||||||
|
|
||||||
COMPILE_ASSERT(RAX != CallingConventions::kArg1Reg);
|
COMPILE_ASSERT(RAX != CallingConventions::kArg1Reg);
|
||||||
__ movq(CallingConventions::kArg1Reg, compiler::Immediate(callback_id_));
|
__ movq(CallingConventions::kArg1Reg, compiler::Immediate(callback_id_));
|
||||||
|
__ movq(RAX, compiler::Immediate(reinterpret_cast<int64_t>(
|
||||||
|
DLRT_GetThreadForNativeCallback)));
|
||||||
__ CallCFunction(RAX);
|
__ CallCFunction(RAX);
|
||||||
__ movq(THR, RAX);
|
__ movq(THR, RAX);
|
||||||
|
|
||||||
|
|
|
@ -210,58 +210,6 @@ Representation ResultRepresentationBase(const Function& signature) {
|
||||||
|
|
||||||
#if !defined(TARGET_ARCH_DBC)
|
#if !defined(TARGET_ARCH_DBC)
|
||||||
|
|
||||||
RawFunction* NativeCallbackFunction(const Function& c_signature,
|
|
||||||
const Function& dart_target,
|
|
||||||
const Instance& exceptional_return) {
|
|
||||||
Thread* const thread = Thread::Current();
|
|
||||||
const int32_t callback_id = thread->AllocateFfiCallbackId();
|
|
||||||
|
|
||||||
// Create a new Function named '<target>_FfiCallback' and stick it in the
|
|
||||||
// 'dart:ffi' library. Note that these functions will never be invoked by
|
|
||||||
// Dart, so they have may have duplicate names.
|
|
||||||
Zone* const zone = thread->zone();
|
|
||||||
const auto& name = String::Handle(
|
|
||||||
zone, Symbols::FromConcat(thread, Symbols::FfiCallback(),
|
|
||||||
String::Handle(zone, dart_target.name())));
|
|
||||||
const Library& lib = Library::Handle(zone, Library::FfiLibrary());
|
|
||||||
const Class& owner_class = Class::Handle(zone, lib.toplevel_class());
|
|
||||||
const Function& function =
|
|
||||||
Function::Handle(zone, Function::New(name, RawFunction::kFfiTrampoline,
|
|
||||||
/*is_static=*/true,
|
|
||||||
/*is_const=*/false,
|
|
||||||
/*is_abstract=*/false,
|
|
||||||
/*is_external=*/false,
|
|
||||||
/*is_native=*/false, owner_class,
|
|
||||||
TokenPosition::kNoSource));
|
|
||||||
function.set_is_debuggable(false);
|
|
||||||
|
|
||||||
// Set callback-specific fields which the flow-graph builder needs to generate
|
|
||||||
// the body.
|
|
||||||
function.SetFfiCSignature(c_signature);
|
|
||||||
function.SetFfiCallbackId(callback_id);
|
|
||||||
function.SetFfiCallbackTarget(dart_target);
|
|
||||||
|
|
||||||
// We need to load the exceptional return value as a constant in the generated
|
|
||||||
// function. Even though the FE ensures that it is a constant, it could still
|
|
||||||
// be a literal allocated in new space. We need to copy it into old space in
|
|
||||||
// that case.
|
|
||||||
//
|
|
||||||
// Exceptional return values currently cannot be pointers because we don't
|
|
||||||
// have constant pointers.
|
|
||||||
//
|
|
||||||
// TODO(36730): We'll need to extend this when we support passing/returning
|
|
||||||
// structs by value.
|
|
||||||
ASSERT(exceptional_return.IsNull() || exceptional_return.IsNumber());
|
|
||||||
if (!exceptional_return.IsSmi() && exceptional_return.IsNew()) {
|
|
||||||
function.SetFfiCallbackExceptionalReturn(Instance::Handle(
|
|
||||||
zone, exceptional_return.CopyShallowToOldSpace(thread)));
|
|
||||||
} else {
|
|
||||||
function.SetFfiCallbackExceptionalReturn(exceptional_return);
|
|
||||||
}
|
|
||||||
|
|
||||||
return function.raw();
|
|
||||||
}
|
|
||||||
|
|
||||||
ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
||||||
const Function& signature) {
|
const Function& signature) {
|
||||||
return ArgumentRepresentationsBase<CallingConventions>(signature);
|
return ArgumentRepresentationsBase<CallingConventions>(signature);
|
||||||
|
@ -702,6 +650,27 @@ Representation FfiSignatureDescriptor::ResultRepresentation() const {
|
||||||
|
|
||||||
#endif // defined(TARGET_ARCH_DBC)
|
#endif // defined(TARGET_ARCH_DBC)
|
||||||
|
|
||||||
|
bool IsAsFunctionInternal(Zone* zone, Isolate* isolate, const Function& func) {
|
||||||
|
Object& asFunctionInternal =
|
||||||
|
Object::Handle(zone, isolate->object_store()->ffi_as_function_internal());
|
||||||
|
if (asFunctionInternal.raw() == Object::null()) {
|
||||||
|
// Cache the reference.
|
||||||
|
const Library& ffi =
|
||||||
|
Library::Handle(zone, isolate->object_store()->ffi_library());
|
||||||
|
asFunctionInternal =
|
||||||
|
ffi.LookupFunctionAllowPrivate(Symbols::AsFunctionInternal());
|
||||||
|
// Cannot assert that 'asFunctionInternal' is found because it may have been
|
||||||
|
// tree-shaken.
|
||||||
|
if (asFunctionInternal.IsNull()) {
|
||||||
|
// Set the entry in the object store to a sentinel so we don't try to look
|
||||||
|
// it up again.
|
||||||
|
asFunctionInternal = Object::sentinel().raw();
|
||||||
|
}
|
||||||
|
isolate->object_store()->set_ffi_as_function_internal(asFunctionInternal);
|
||||||
|
}
|
||||||
|
return func.raw() == asFunctionInternal.raw();
|
||||||
|
}
|
||||||
|
|
||||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
|
||||||
} // namespace ffi
|
} // namespace ffi
|
||||||
|
|
|
@ -56,10 +56,6 @@ RawFunction* TrampolineFunction(const Function& dart_signature,
|
||||||
|
|
||||||
#if !defined(TARGET_ARCH_DBC)
|
#if !defined(TARGET_ARCH_DBC)
|
||||||
|
|
||||||
RawFunction* NativeCallbackFunction(const Function& c_signature,
|
|
||||||
const Function& dart_target,
|
|
||||||
const Instance& exceptional_return);
|
|
||||||
|
|
||||||
// Unboxed representations of the arguments to a C signature function.
|
// Unboxed representations of the arguments to a C signature function.
|
||||||
ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
ZoneGrowableArray<Representation>* ArgumentRepresentations(
|
||||||
const Function& signature);
|
const Function& signature);
|
||||||
|
@ -149,8 +145,11 @@ class CallbackArgumentTranslator : public ValueObject {
|
||||||
intptr_t argument_slots_used_ = 0;
|
intptr_t argument_slots_used_ = 0;
|
||||||
intptr_t argument_slots_required_ = 0;
|
intptr_t argument_slots_required_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // defined(TARGET_ARCH_DBC)
|
#endif // defined(TARGET_ARCH_DBC)
|
||||||
|
|
||||||
|
bool IsAsFunctionInternal(Zone* zone, Isolate* isolate, const Function& func);
|
||||||
|
|
||||||
} // namespace ffi
|
} // namespace ffi
|
||||||
|
|
||||||
} // namespace compiler
|
} // namespace compiler
|
||||||
|
|
|
@ -807,14 +807,9 @@ void BytecodeFlowGraphBuilder::BuildDirectCall() {
|
||||||
const Function& target = Function::Cast(ConstantAt(DecodeOperandD()).value());
|
const Function& target = Function::Cast(ConstantAt(DecodeOperandD()).value());
|
||||||
const intptr_t argc = DecodeOperandF().value();
|
const intptr_t argc = DecodeOperandF().value();
|
||||||
|
|
||||||
const auto recognized_kind = MethodRecognizer::RecognizeKind(target);
|
if (compiler::ffi::IsAsFunctionInternal(Z, isolate(), target)) {
|
||||||
if (recognized_kind == MethodRecognizer::kFfiAsFunctionInternal) {
|
|
||||||
BuildFfiAsFunction();
|
BuildFfiAsFunction();
|
||||||
return;
|
return;
|
||||||
} else if (FLAG_precompiled_mode &&
|
|
||||||
recognized_kind == MethodRecognizer::kFfiNativeCallbackFunction) {
|
|
||||||
BuildFfiNativeCallbackFunction();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recognize identical() call.
|
// Recognize identical() call.
|
||||||
|
@ -831,7 +826,7 @@ void BytecodeFlowGraphBuilder::BuildDirectCall() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FLAG_causal_async_stacks &&
|
if (!FLAG_causal_async_stacks &&
|
||||||
recognized_kind == MethodRecognizer::kAsyncStackTraceHelper) {
|
target.recognized_kind() == MethodRecognizer::kAsyncStackTraceHelper) {
|
||||||
ASSERT(argc == 1);
|
ASSERT(argc == 1);
|
||||||
// Drop the ignored parameter to _asyncStackTraceHelper(:async_op).
|
// Drop the ignored parameter to _asyncStackTraceHelper(:async_op).
|
||||||
code_ += B->Drop();
|
code_ += B->Drop();
|
||||||
|
@ -839,7 +834,7 @@ void BytecodeFlowGraphBuilder::BuildDirectCall() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recognized_kind == MethodRecognizer::kStringBaseInterpolate) {
|
if (target.recognized_kind() == MethodRecognizer::kStringBaseInterpolate) {
|
||||||
ASSERT(argc == 1);
|
ASSERT(argc == 1);
|
||||||
code_ += B->StringInterpolate(position_);
|
code_ += B->StringInterpolate(position_);
|
||||||
return;
|
return;
|
||||||
|
@ -1829,45 +1824,6 @@ void BytecodeFlowGraphBuilder::BuildFfiAsFunction() {
|
||||||
code_ += B->BuildFfiAsFunctionInternalCall(type_args);
|
code_ += B->BuildFfiAsFunctionInternalCall(type_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Builds graph for a call to 'dart:ffi::_nativeCallbackFunction'.
|
|
||||||
// The call-site must look like this (guaranteed by the FE which inserts it):
|
|
||||||
//
|
|
||||||
// _nativeCallbackFunction<NativeSignatureType>(target, exceptionalReturn)
|
|
||||||
//
|
|
||||||
// Therefore the stack shall look like:
|
|
||||||
//
|
|
||||||
// <exceptional return value> => ensured (by FE) to be a constant
|
|
||||||
// <target> => closure, ensured (by FE) to be a (non-partially-instantiated)
|
|
||||||
// static tearoff
|
|
||||||
// <type args> => [NativeSignatureType]
|
|
||||||
void BytecodeFlowGraphBuilder::BuildFfiNativeCallbackFunction() {
|
|
||||||
#if defined(TARGET_ARCH_DBC)
|
|
||||||
UNREACHABLE();
|
|
||||||
#else
|
|
||||||
const TypeArguments& type_args =
|
|
||||||
TypeArguments::Cast(B->Peek(/*depth=*/2)->AsConstant()->value());
|
|
||||||
ASSERT(type_args.IsInstantiated() && type_args.Length() == 1);
|
|
||||||
const Function& native_sig = Function::Handle(
|
|
||||||
Z, Type::Cast(AbstractType::Handle(Z, type_args.TypeAt(0))).signature());
|
|
||||||
|
|
||||||
const Closure& target_closure =
|
|
||||||
Closure::Cast(B->Peek(/*depth=*/1)->AsConstant()->value());
|
|
||||||
ASSERT(!target_closure.IsNull());
|
|
||||||
Function& target = Function::Handle(Z, target_closure.function());
|
|
||||||
ASSERT(!target.IsNull() && target.IsImplicitClosureFunction());
|
|
||||||
target = target.parent_function();
|
|
||||||
|
|
||||||
const Instance& exceptional_return =
|
|
||||||
Instance::Cast(B->Peek(/*depth=*/0)->AsConstant()->value());
|
|
||||||
|
|
||||||
const Function& result =
|
|
||||||
Function::ZoneHandle(Z, compiler::ffi::NativeCallbackFunction(
|
|
||||||
native_sig, target, exceptional_return));
|
|
||||||
code_ += B->Constant(result);
|
|
||||||
code_ += B->DropTempsPreserveTop(3);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void BytecodeFlowGraphBuilder::BuildDebugStepCheck() {
|
void BytecodeFlowGraphBuilder::BuildDebugStepCheck() {
|
||||||
#if !defined(PRODUCT)
|
#if !defined(PRODUCT)
|
||||||
if (build_debug_step_checks_) {
|
if (build_debug_step_checks_) {
|
||||||
|
|
|
@ -172,7 +172,6 @@ class BytecodeFlowGraphBuilder {
|
||||||
|
|
||||||
void BuildInstruction(KernelBytecode::Opcode opcode);
|
void BuildInstruction(KernelBytecode::Opcode opcode);
|
||||||
void BuildFfiAsFunction();
|
void BuildFfiAsFunction();
|
||||||
void BuildFfiNativeCallbackFunction();
|
|
||||||
void BuildDebugStepCheck();
|
void BuildDebugStepCheck();
|
||||||
|
|
||||||
#define DECLARE_BUILD_METHOD(name, encoding, kind, op1, op2, op3) \
|
#define DECLARE_BUILD_METHOD(name, encoding, kind, op1, op2, op3) \
|
||||||
|
|
|
@ -3059,12 +3059,8 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(bool is_const,
|
||||||
++argument_count;
|
++argument_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto recognized_kind = MethodRecognizer::RecognizeKind(target);
|
if (compiler::ffi::IsAsFunctionInternal(Z, H.isolate(), target)) {
|
||||||
if (recognized_kind == MethodRecognizer::kFfiAsFunctionInternal) {
|
|
||||||
return BuildFfiAsFunctionInternal();
|
return BuildFfiAsFunctionInternal();
|
||||||
} else if (FLAG_precompiled_mode &&
|
|
||||||
recognized_kind == MethodRecognizer::kFfiNativeCallbackFunction) {
|
|
||||||
return BuildFfiNativeCallbackFunction();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Fragment instructions;
|
Fragment instructions;
|
||||||
|
@ -3072,7 +3068,7 @@ Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(bool is_const,
|
||||||
|
|
||||||
const bool special_case_nop_async_stack_trace_helper =
|
const bool special_case_nop_async_stack_trace_helper =
|
||||||
!FLAG_causal_async_stacks &&
|
!FLAG_causal_async_stacks &&
|
||||||
recognized_kind == MethodRecognizer::kAsyncStackTraceHelper;
|
target.recognized_kind() == MethodRecognizer::kAsyncStackTraceHelper;
|
||||||
|
|
||||||
const bool special_case_unchecked_cast =
|
const bool special_case_unchecked_cast =
|
||||||
klass.IsTopLevel() && (klass.library() == Library::InternalLibrary()) &&
|
klass.IsTopLevel() && (klass.library() == Library::InternalLibrary()) &&
|
||||||
|
@ -5047,65 +5043,6 @@ Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction() {
|
|
||||||
#if defined(TARGET_ARCH_DBC)
|
|
||||||
UNREACHABLE();
|
|
||||||
#else
|
|
||||||
// The call-site must look like this (guaranteed by the FE which inserts it):
|
|
||||||
//
|
|
||||||
// _nativeCallbackFunction<NativeSignatureType>(target, exceptionalReturn)
|
|
||||||
//
|
|
||||||
// The FE also guarantees that all three arguments are constants.
|
|
||||||
|
|
||||||
const intptr_t argc = ReadUInt(); // read argument count
|
|
||||||
ASSERT(argc == 2); // target, exceptionalReturn
|
|
||||||
|
|
||||||
const intptr_t list_length = ReadListLength(); // read types list length
|
|
||||||
ASSERT(list_length == 1); // native signature
|
|
||||||
const TypeArguments& type_arguments =
|
|
||||||
T.BuildTypeArguments(list_length); // read types.
|
|
||||||
ASSERT(type_arguments.Length() == 1 && type_arguments.IsInstantiated());
|
|
||||||
const Function& native_sig = Function::Handle(
|
|
||||||
Z, Type::Cast(AbstractType::Handle(Z, type_arguments.TypeAt(0)))
|
|
||||||
.signature());
|
|
||||||
|
|
||||||
Fragment code;
|
|
||||||
const intptr_t positional_count =
|
|
||||||
ReadListLength(); // read positional argument count
|
|
||||||
ASSERT(positional_count == 2);
|
|
||||||
|
|
||||||
// Read target expression and extract the target function.
|
|
||||||
code += BuildExpression(); // build first positional argument (target)
|
|
||||||
Definition* target_def = B->Peek();
|
|
||||||
ASSERT(target_def->IsConstant());
|
|
||||||
const Closure& target_closure =
|
|
||||||
Closure::Cast(target_def->AsConstant()->value());
|
|
||||||
ASSERT(!target_closure.IsNull());
|
|
||||||
Function& target = Function::Handle(Z, target_closure.function());
|
|
||||||
ASSERT(!target.IsNull() && target.IsImplicitClosureFunction());
|
|
||||||
target = target.parent_function();
|
|
||||||
code += Drop();
|
|
||||||
|
|
||||||
// Build second positional argument (exceptionalReturn).
|
|
||||||
code += BuildExpression();
|
|
||||||
Definition* exceptional_return_def = B->Peek();
|
|
||||||
ASSERT(exceptional_return_def->IsConstant());
|
|
||||||
const Instance& exceptional_return =
|
|
||||||
Instance::Cast(exceptional_return_def->AsConstant()->value());
|
|
||||||
code += Drop();
|
|
||||||
|
|
||||||
const intptr_t named_args_len =
|
|
||||||
ReadListLength(); // skip (empty) named arguments list
|
|
||||||
ASSERT(named_args_len == 0);
|
|
||||||
|
|
||||||
const Function& result =
|
|
||||||
Function::ZoneHandle(Z, compiler::ffi::NativeCallbackFunction(
|
|
||||||
native_sig, target, exceptional_return));
|
|
||||||
code += Constant(result);
|
|
||||||
return code;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace dart
|
} // namespace dart
|
||||||
|
|
||||||
|
|
|
@ -353,10 +353,6 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
|
||||||
// Kernel buffer and pushes the resulting closure.
|
// Kernel buffer and pushes the resulting closure.
|
||||||
Fragment BuildFfiAsFunctionInternal();
|
Fragment BuildFfiAsFunctionInternal();
|
||||||
|
|
||||||
// Build build FG for '_nativeCallbackFunction'. Reads an Arguments from the
|
|
||||||
// Kernel buffer and pushes the resulting Function object.
|
|
||||||
Fragment BuildFfiNativeCallbackFunction();
|
|
||||||
|
|
||||||
FlowGraphBuilder* flow_graph_builder_;
|
FlowGraphBuilder* flow_graph_builder_;
|
||||||
ActiveClass* const active_class_;
|
ActiveClass* const active_class_;
|
||||||
TypeTranslator type_translator_;
|
TypeTranslator type_translator_;
|
||||||
|
|
|
@ -2397,7 +2397,6 @@ Fragment FlowGraphBuilder::FfiPointerFromAddress(const Type& result_type) {
|
||||||
// do not appear in the type arguments to a any Pointer classes in an FFI
|
// do not appear in the type arguments to a any Pointer classes in an FFI
|
||||||
// signature.
|
// signature.
|
||||||
ASSERT(args.IsNull() || args.IsInstantiated());
|
ASSERT(args.IsNull() || args.IsInstantiated());
|
||||||
args = args.Canonicalize();
|
|
||||||
|
|
||||||
Fragment code;
|
Fragment code;
|
||||||
code += Constant(args);
|
code += Constant(args);
|
||||||
|
@ -2451,6 +2450,14 @@ Fragment FlowGraphBuilder::FfiConvertArgumentToNative(
|
||||||
const Representation native_representation) {
|
const Representation native_representation) {
|
||||||
Fragment body;
|
Fragment body;
|
||||||
|
|
||||||
|
// Return 0 for void.
|
||||||
|
if (compiler::ffi::NativeTypeIsVoid(ffi_type)) {
|
||||||
|
body += Drop();
|
||||||
|
body += IntConstant(0);
|
||||||
|
body += UnboxTruncate(kUnboxedFfiIntPtr);
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
// Check for 'null'.
|
// Check for 'null'.
|
||||||
body += LoadLocal(MakeTemporary());
|
body += LoadLocal(MakeTemporary());
|
||||||
body <<= new (Z) CheckNullInstr(Pop(), String::ZoneHandle(Z, function.name()),
|
body <<= new (Z) CheckNullInstr(Pop(), String::ZoneHandle(Z, function.name()),
|
||||||
|
@ -2607,20 +2614,9 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCallback(const Function& function) {
|
||||||
/*needs_stacktrace=*/false, /*is_synthesized=*/true);
|
/*needs_stacktrace=*/false, /*is_synthesized=*/true);
|
||||||
|
|
||||||
// Return the "exceptional return" value given in 'fromFunction'.
|
// Return the "exceptional return" value given in 'fromFunction'.
|
||||||
//
|
|
||||||
// For pointer and void return types, the exceptional return is always null --
|
|
||||||
// return 0 instead.
|
|
||||||
if (compiler::ffi::NativeTypeIsPointer(ffi_type) ||
|
|
||||||
compiler::ffi::NativeTypeIsVoid(ffi_type)) {
|
|
||||||
ASSERT(function.FfiCallbackExceptionalReturn() == Object::null());
|
|
||||||
catch_body += IntConstant(0);
|
|
||||||
catch_body += UnboxTruncate(kUnboxedFfiIntPtr);
|
|
||||||
} else {
|
|
||||||
catch_body += Constant(
|
catch_body += Constant(
|
||||||
Instance::ZoneHandle(Z, function.FfiCallbackExceptionalReturn()));
|
Instance::ZoneHandle(Z, function.FfiCallbackExceptionalReturn()));
|
||||||
catch_body += FfiConvertArgumentToNative(function, ffi_type, result_rep);
|
catch_body += FfiConvertArgumentToNative(function, ffi_type, result_rep);
|
||||||
}
|
|
||||||
|
|
||||||
catch_body += NativeReturn(result_rep);
|
catch_body += NativeReturn(result_rep);
|
||||||
--catch_depth_;
|
--catch_depth_;
|
||||||
|
|
||||||
|
|
|
@ -143,8 +143,6 @@ namespace dart {
|
||||||
V(::, _classRangeCheck, ClassRangeCheck, 0xca52e30a) \
|
V(::, _classRangeCheck, ClassRangeCheck, 0xca52e30a) \
|
||||||
V(::, _asyncStackTraceHelper, AsyncStackTraceHelper, 0xaeaed5cb) \
|
V(::, _asyncStackTraceHelper, AsyncStackTraceHelper, 0xaeaed5cb) \
|
||||||
V(::, _abi, FfiAbi, 0xf2e89620) \
|
V(::, _abi, FfiAbi, 0xf2e89620) \
|
||||||
V(::, _asFunctionInternal, FfiAsFunctionInternal, 0x82525e9e) \
|
|
||||||
V(::, _nativeCallbackFunction, FfiNativeCallbackFunction, 0x591fb33c) \
|
|
||||||
|
|
||||||
// List of intrinsics:
|
// List of intrinsics:
|
||||||
// (class-name, function-name, intrinsification method, fingerprint).
|
// (class-name, function-name, intrinsification method, fingerprint).
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#include "platform/globals.h"
|
#include "platform/globals.h"
|
||||||
#include "vm/allocation.h"
|
#include "vm/allocation.h"
|
||||||
#include "vm/bitfield.h"
|
#include "vm/bitfield.h"
|
||||||
#include "vm/bss_relocs.h"
|
|
||||||
#include "vm/class_id.h"
|
#include "vm/class_id.h"
|
||||||
#include "vm/code_entry_kind.h"
|
#include "vm/code_entry_kind.h"
|
||||||
#include "vm/constants.h"
|
#include "vm/constants.h"
|
||||||
|
|
|
@ -82,6 +82,8 @@ static const intptr_t kElfDynamicTableEntrySize = 16;
|
||||||
static const intptr_t kElfSymbolHashTableEntrySize = 4;
|
static const intptr_t kElfSymbolHashTableEntrySize = 4;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const intptr_t kPageSize = 4096;
|
||||||
|
|
||||||
class Section : public ZoneAllocated {
|
class Section : public ZoneAllocated {
|
||||||
public:
|
public:
|
||||||
Section() {}
|
Section() {}
|
||||||
|
@ -113,34 +115,23 @@ class ProgramBits : public Section {
|
||||||
public:
|
public:
|
||||||
ProgramBits(bool allocate,
|
ProgramBits(bool allocate,
|
||||||
bool executable,
|
bool executable,
|
||||||
bool writable,
|
|
||||||
const uint8_t* bytes,
|
const uint8_t* bytes,
|
||||||
intptr_t filesz,
|
intptr_t size) {
|
||||||
intptr_t memsz = -1) {
|
|
||||||
if (memsz == -1) memsz = filesz;
|
|
||||||
|
|
||||||
section_type = SHT_PROGBITS;
|
section_type = SHT_PROGBITS;
|
||||||
if (allocate) {
|
if (allocate) {
|
||||||
section_flags = SHF_ALLOC;
|
section_flags = SHF_ALLOC;
|
||||||
if (executable) section_flags |= SHF_EXECINSTR;
|
if (executable) section_flags |= SHF_EXECINSTR;
|
||||||
if (writable) section_flags |= SHF_WRITE;
|
|
||||||
|
|
||||||
segment_type = PT_LOAD;
|
segment_type = PT_LOAD;
|
||||||
segment_flags = PF_R;
|
segment_flags = PF_R;
|
||||||
if (executable) segment_flags |= PF_X;
|
if (executable) segment_flags |= PF_X;
|
||||||
if (writable) segment_flags |= PF_W;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes_ = bytes;
|
bytes_ = bytes;
|
||||||
file_size = filesz;
|
file_size = memory_size = size;
|
||||||
memory_size = memsz;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write(Elf* stream) {
|
void Write(Elf* stream) { stream->WriteBytes(bytes_, memory_size); }
|
||||||
if (bytes_ != nullptr) {
|
|
||||||
stream->WriteBytes(bytes_, file_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t* bytes_;
|
const uint8_t* bytes_;
|
||||||
};
|
};
|
||||||
|
@ -380,7 +371,7 @@ static const intptr_t kNumInvalidSections = 1;
|
||||||
// Elf::segments_.
|
// Elf::segments_.
|
||||||
static const intptr_t kNumImplicitSegments = 3;
|
static const intptr_t kNumImplicitSegments = 3;
|
||||||
|
|
||||||
static const intptr_t kProgramTableSegmentSize = Elf::kPageSize;
|
static const intptr_t kProgramTableSegmentSize = kPageSize;
|
||||||
|
|
||||||
Elf::Elf(Zone* zone, StreamingWriteStream* stream)
|
Elf::Elf(Zone* zone, StreamingWriteStream* stream)
|
||||||
: zone_(zone), stream_(stream), memory_offset_(0) {
|
: zone_(zone), stream_(stream), memory_offset_(0) {
|
||||||
|
@ -424,7 +415,7 @@ intptr_t Elf::NextMemoryOffset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) {
|
intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) {
|
||||||
ProgramBits* image = new (zone_) ProgramBits(true, true, false, bytes, size);
|
ProgramBits* image = new (zone_) ProgramBits(true, true, bytes, size);
|
||||||
image->section_name = shstrtab_->AddString(".text");
|
image->section_name = shstrtab_->AddString(".text");
|
||||||
AddSection(image);
|
AddSection(image);
|
||||||
AddSegment(image);
|
AddSegment(image);
|
||||||
|
@ -443,29 +434,8 @@ intptr_t Elf::AddText(const char* name, const uint8_t* bytes, intptr_t size) {
|
||||||
return symbol->offset;
|
return symbol->offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
intptr_t Elf::AddBSSData(const char* name, intptr_t size) {
|
|
||||||
ProgramBits* image = new (zone_)
|
|
||||||
ProgramBits(true, false, true, nullptr, /*filesz=*/0, /*memsz=*/size);
|
|
||||||
image->section_name = shstrtab_->AddString(".bss");
|
|
||||||
AddSection(image);
|
|
||||||
AddSegment(image);
|
|
||||||
|
|
||||||
Symbol* symbol = new (zone_) Symbol();
|
|
||||||
symbol->cstr = name;
|
|
||||||
symbol->name = symstrtab_->AddString(name);
|
|
||||||
symbol->info = (STB_GLOBAL << 4) | STT_OBJECT;
|
|
||||||
symbol->section = image->section_index;
|
|
||||||
// For shared libraries, this is the offset from the DSO base. For static
|
|
||||||
// libraries, this is section relative.
|
|
||||||
symbol->offset = image->memory_offset;
|
|
||||||
symbol->size = size;
|
|
||||||
symtab_->AddSymbol(symbol);
|
|
||||||
|
|
||||||
return symbol->offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) {
|
intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) {
|
||||||
ProgramBits* image = new (zone_) ProgramBits(true, false, false, bytes, size);
|
ProgramBits* image = new (zone_) ProgramBits(true, false, bytes, size);
|
||||||
image->section_name = shstrtab_->AddString(".rodata");
|
image->section_name = shstrtab_->AddString(".rodata");
|
||||||
AddSection(image);
|
AddSection(image);
|
||||||
AddSegment(image);
|
AddSegment(image);
|
||||||
|
@ -485,8 +455,7 @@ intptr_t Elf::AddROData(const char* name, const uint8_t* bytes, intptr_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Elf::AddDebug(const char* name, const uint8_t* bytes, intptr_t size) {
|
void Elf::AddDebug(const char* name, const uint8_t* bytes, intptr_t size) {
|
||||||
ProgramBits* image =
|
ProgramBits* image = new (zone_) ProgramBits(false, false, bytes, size);
|
||||||
new (zone_) ProgramBits(false, false, false, bytes, size);
|
|
||||||
image->section_name = shstrtab_->AddString(name);
|
image->section_name = shstrtab_->AddString(name);
|
||||||
AddSection(image);
|
AddSection(image);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,9 @@ class Elf : public ZoneAllocated {
|
||||||
public:
|
public:
|
||||||
Elf(Zone* zone, StreamingWriteStream* stream);
|
Elf(Zone* zone, StreamingWriteStream* stream);
|
||||||
|
|
||||||
static const intptr_t kPageSize = 4096;
|
|
||||||
|
|
||||||
intptr_t NextMemoryOffset();
|
intptr_t NextMemoryOffset();
|
||||||
intptr_t AddText(const char* name, const uint8_t* bytes, intptr_t size);
|
intptr_t AddText(const char* name, const uint8_t* bytes, intptr_t size);
|
||||||
intptr_t AddROData(const char* name, const uint8_t* bytes, intptr_t size);
|
intptr_t AddROData(const char* name, const uint8_t* bytes, intptr_t size);
|
||||||
intptr_t AddBSSData(const char* name, intptr_t size);
|
|
||||||
void AddDebug(const char* name, const uint8_t* bytes, intptr_t size);
|
void AddDebug(const char* name, const uint8_t* bytes, intptr_t size);
|
||||||
|
|
||||||
void Finalize();
|
void Finalize();
|
||||||
|
|
|
@ -14,22 +14,7 @@ namespace dart {
|
||||||
DECLARE_FLAG(bool, disassemble_stubs);
|
DECLARE_FLAG(bool, disassemble_stubs);
|
||||||
|
|
||||||
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(TARGET_ARCH_DBC)
|
#if !defined(DART_PRECOMPILED_RUNTIME) && !defined(TARGET_ARCH_DBC)
|
||||||
uword NativeCallbackTrampolines::TrampolineForId(int32_t callback_id) {
|
uword NativeCallbackTrampolines::AllocateTrampoline() {
|
||||||
#if defined(DART_PRECOMPILER)
|
|
||||||
ASSERT(!Enabled());
|
|
||||||
UNREACHABLE();
|
|
||||||
#else
|
|
||||||
const intptr_t trampolines_per_page = NumCallbackTrampolinesPerPage();
|
|
||||||
const intptr_t page_index = callback_id / trampolines_per_page;
|
|
||||||
const uword entry_point = trampoline_pages_[page_index]->start();
|
|
||||||
|
|
||||||
return entry_point +
|
|
||||||
(callback_id % trampolines_per_page) *
|
|
||||||
compiler::StubCodeCompiler::kNativeCallbackTrampolineSize;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void NativeCallbackTrampolines::AllocateTrampoline() {
|
|
||||||
#if defined(DART_PRECOMPILER)
|
#if defined(DART_PRECOMPILER)
|
||||||
ASSERT(!Enabled());
|
ASSERT(!Enabled());
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
|
@ -91,11 +76,16 @@ void NativeCallbackTrampolines::AllocateTrampoline() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
next_callback_trampoline_ = memory->start();
|
||||||
trampolines_left_on_page_ = NumCallbackTrampolinesPerPage();
|
trampolines_left_on_page_ = NumCallbackTrampolinesPerPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
trampolines_left_on_page_--;
|
trampolines_left_on_page_--;
|
||||||
next_callback_id_++;
|
next_callback_id_++;
|
||||||
|
const uword entrypoint = next_callback_trampoline_;
|
||||||
|
next_callback_trampoline_ +=
|
||||||
|
compiler::StubCodeCompiler::kNativeCallbackTrampolineSize;
|
||||||
|
return entrypoint;
|
||||||
#endif // defined(DART_PRECOMPILER)
|
#endif // defined(DART_PRECOMPILER)
|
||||||
}
|
}
|
||||||
#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(TARGET_ARCH_DBC)
|
#endif // !defined(DART_PRECOMPILED_RUNTIME) && !defined(TARGET_ARCH_DBC)
|
||||||
|
|
|
@ -57,13 +57,11 @@ class NativeCallbackTrampolines : public ValueObject {
|
||||||
|
|
||||||
// Allocates a callback trampoline corresponding to the callback id
|
// Allocates a callback trampoline corresponding to the callback id
|
||||||
// 'next_callback_id()'. Returns an entrypoint to the trampoline.
|
// 'next_callback_id()'. Returns an entrypoint to the trampoline.
|
||||||
void AllocateTrampoline();
|
uword AllocateTrampoline();
|
||||||
|
|
||||||
// Get the entrypoint for a previously allocated callback ID.
|
|
||||||
uword TrampolineForId(int32_t callback_id);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MallocGrowableArray<VirtualMemory*> trampoline_pages_;
|
MallocGrowableArray<VirtualMemory*> trampoline_pages_;
|
||||||
|
uword next_callback_trampoline_ = 0;
|
||||||
intptr_t trampolines_left_on_page_ = 0;
|
intptr_t trampolines_left_on_page_ = 0;
|
||||||
intptr_t next_callback_id_ = 0;
|
intptr_t next_callback_id_ = 0;
|
||||||
|
|
||||||
|
|
|
@ -556,11 +556,6 @@ const char* NameOfStubIsolateSpecificStub(ObjectStore* object_store,
|
||||||
void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
Zone* zone = Thread::Current()->zone();
|
Zone* zone = Thread::Current()->zone();
|
||||||
|
|
||||||
#if defined(DART_PRECOMPILER)
|
|
||||||
const char* bss_symbol =
|
|
||||||
vm ? "_kDartVmSnapshotBss" : "_kDartIsolateSnapshotBss";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const char* instructions_symbol =
|
const char* instructions_symbol =
|
||||||
vm ? "_kDartVmSnapshotInstructions" : "_kDartIsolateSnapshotInstructions";
|
vm ? "_kDartVmSnapshotInstructions" : "_kDartIsolateSnapshotInstructions";
|
||||||
assembly_stream_.Print(".text\n");
|
assembly_stream_.Print(".text\n");
|
||||||
|
@ -575,16 +570,8 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
// look like a HeapPage.
|
// look like a HeapPage.
|
||||||
intptr_t instructions_length = next_text_offset_;
|
intptr_t instructions_length = next_text_offset_;
|
||||||
WriteWordLiteralText(instructions_length);
|
WriteWordLiteralText(instructions_length);
|
||||||
|
|
||||||
#if defined(DART_PRECOMPILER)
|
|
||||||
assembly_stream_.Print("%s %s - %s\n", kLiteralPrefix, bss_symbol,
|
|
||||||
instructions_symbol);
|
|
||||||
#else
|
|
||||||
WriteWordLiteralText(0); // No relocations.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
intptr_t header_words = Image::kHeaderSize / sizeof(compiler::target::uword);
|
intptr_t header_words = Image::kHeaderSize / sizeof(compiler::target::uword);
|
||||||
for (intptr_t i = Image::kHeaderFields; i < header_words; i++) {
|
for (intptr_t i = 1; i < header_words; i++) {
|
||||||
WriteWordLiteralText(0);
|
WriteWordLiteralText(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -592,7 +579,6 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
|
|
||||||
Object& owner = Object::Handle(zone);
|
Object& owner = Object::Handle(zone);
|
||||||
String& str = String::Handle(zone);
|
String& str = String::Handle(zone);
|
||||||
PcDescriptors& descriptors = PcDescriptors::Handle(zone);
|
|
||||||
|
|
||||||
ObjectStore* object_store = Isolate::Current()->object_store();
|
ObjectStore* object_store = Isolate::Current()->object_store();
|
||||||
|
|
||||||
|
@ -627,7 +613,6 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
|
|
||||||
const Instructions& insns = *data.insns_;
|
const Instructions& insns = *data.insns_;
|
||||||
const Code& code = *data.code_;
|
const Code& code = *data.code_;
|
||||||
descriptors = data.code_->pc_descriptors();
|
|
||||||
|
|
||||||
if (profile_writer_ != nullptr) {
|
if (profile_writer_ != nullptr) {
|
||||||
const intptr_t offset = Image::kHeaderSize + text_offset;
|
const intptr_t offset = Image::kHeaderSize + text_offset;
|
||||||
|
@ -732,27 +717,7 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
ASSERT(Utils::IsAligned(entry, sizeof(uword)));
|
ASSERT(Utils::IsAligned(entry, sizeof(uword)));
|
||||||
ASSERT(Utils::IsAligned(end, sizeof(uword)));
|
ASSERT(Utils::IsAligned(end, sizeof(uword)));
|
||||||
|
|
||||||
#if defined(DART_PRECOMPILER)
|
|
||||||
PcDescriptors::Iterator iterator(descriptors,
|
|
||||||
RawPcDescriptors::kBSSRelocation);
|
|
||||||
uword next_reloc_offset = iterator.MoveNext() ? iterator.PcOffset() : -1;
|
|
||||||
|
|
||||||
for (uword cursor = entry; cursor < end;
|
|
||||||
cursor += sizeof(compiler::target::uword*)) {
|
|
||||||
compiler::target::uword data =
|
|
||||||
*reinterpret_cast<compiler::target::uword*>(cursor);
|
|
||||||
if ((cursor - entry) == next_reloc_offset) {
|
|
||||||
assembly_stream_.Print("%s %s - (.) + %" Pd "\n", kLiteralPrefix,
|
|
||||||
bss_symbol, /*addend=*/data);
|
|
||||||
next_reloc_offset = iterator.MoveNext() ? iterator.PcOffset() : -1;
|
|
||||||
} else {
|
|
||||||
WriteWordLiteralText(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text_offset += end - entry;
|
|
||||||
#else
|
|
||||||
text_offset += WriteByteSequence(entry, end);
|
text_offset += WriteByteSequence(entry, end);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT((text_offset - instr_start) == insns.raw()->HeapSize());
|
ASSERT((text_offset - instr_start) == insns.raw()->HeapSize());
|
||||||
|
@ -760,16 +725,6 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
|
|
||||||
FrameUnwindEpilogue();
|
FrameUnwindEpilogue();
|
||||||
|
|
||||||
#if defined(DART_PRECOMPILER)
|
|
||||||
assembly_stream_.Print(".bss\n");
|
|
||||||
assembly_stream_.Print("%s:\n", bss_symbol);
|
|
||||||
|
|
||||||
// Currently we only put one symbol in the data section, the address of
|
|
||||||
// DLRT_GetThreadForNativeCallback, which is populated when the snapshot is
|
|
||||||
// loaded.
|
|
||||||
WriteWordLiteralText(0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
|
#if defined(TARGET_OS_LINUX) || defined(TARGET_OS_ANDROID) || \
|
||||||
defined(TARGET_OS_FUCHSIA)
|
defined(TARGET_OS_FUCHSIA)
|
||||||
assembly_stream_.Print(".section .rodata\n");
|
assembly_stream_.Print(".section .rodata\n");
|
||||||
|
@ -905,42 +860,24 @@ intptr_t BlobImageWriter::WriteByteSequence(uword start, uword end) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
const intptr_t instructions_length = next_text_offset_;
|
|
||||||
#ifdef DART_PRECOMPILER
|
#ifdef DART_PRECOMPILER
|
||||||
intptr_t segment_base = 0;
|
intptr_t segment_base = 0;
|
||||||
if (elf_ != nullptr) {
|
if (elf_ != nullptr) {
|
||||||
segment_base = elf_->NextMemoryOffset();
|
segment_base = elf_->NextMemoryOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the start of the BSS section based on the known size of the
|
|
||||||
// text section and page alignment.
|
|
||||||
intptr_t bss_base = 0;
|
|
||||||
if (elf_ != nullptr) {
|
|
||||||
bss_base =
|
|
||||||
Utils::RoundUp(segment_base + instructions_length, Elf::kPageSize);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This header provides the gap to make the instructions snapshot look like a
|
// This header provides the gap to make the instructions snapshot look like a
|
||||||
// HeapPage.
|
// HeapPage.
|
||||||
|
intptr_t instructions_length = next_text_offset_;
|
||||||
instructions_blob_stream_.WriteWord(instructions_length);
|
instructions_blob_stream_.WriteWord(instructions_length);
|
||||||
#if defined(DART_PRECOMPILER)
|
|
||||||
instructions_blob_stream_.WriteWord(elf_ != nullptr ? bss_base - segment_base
|
|
||||||
: 0);
|
|
||||||
#else
|
|
||||||
instructions_blob_stream_.WriteWord(0); // No relocations.
|
|
||||||
#endif
|
|
||||||
intptr_t header_words = Image::kHeaderSize / sizeof(uword);
|
intptr_t header_words = Image::kHeaderSize / sizeof(uword);
|
||||||
for (intptr_t i = Image::kHeaderFields; i < header_words; i++) {
|
for (intptr_t i = 1; i < header_words; i++) {
|
||||||
instructions_blob_stream_.WriteWord(0);
|
instructions_blob_stream_.WriteWord(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
intptr_t text_offset = 0;
|
intptr_t text_offset = 0;
|
||||||
|
|
||||||
#if defined(DART_PRECOMPILER)
|
|
||||||
PcDescriptors& descriptors = PcDescriptors::Handle();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NoSafepointScope no_safepoint;
|
NoSafepointScope no_safepoint;
|
||||||
for (intptr_t i = 0; i < instructions_.length(); i++) {
|
for (intptr_t i = 0; i < instructions_.length(); i++) {
|
||||||
auto& data = instructions_[i];
|
auto& data = instructions_[i];
|
||||||
|
@ -991,8 +928,6 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
marked_tags |= static_cast<uword>(insns.raw_ptr()->hash_) << 32;
|
marked_tags |= static_cast<uword>(insns.raw_ptr()->hash_) << 32;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
intptr_t payload_stream_start = 0;
|
|
||||||
|
|
||||||
#if defined(IS_SIMARM_X64)
|
#if defined(IS_SIMARM_X64)
|
||||||
const intptr_t start_offset = instructions_blob_stream_.bytes_written();
|
const intptr_t start_offset = instructions_blob_stream_.bytes_written();
|
||||||
const intptr_t size_in_bytes = InstructionsSizeInSnapshot(insns.Size());
|
const intptr_t size_in_bytes = InstructionsSizeInSnapshot(insns.Size());
|
||||||
|
@ -1003,7 +938,6 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
instructions_blob_stream_.WriteFixed<uint32_t>(
|
instructions_blob_stream_.WriteFixed<uint32_t>(
|
||||||
insns.raw_ptr()->unchecked_entrypoint_pc_offset_);
|
insns.raw_ptr()->unchecked_entrypoint_pc_offset_);
|
||||||
instructions_blob_stream_.Align(kSimarmX64InstructionsAlignment);
|
instructions_blob_stream_.Align(kSimarmX64InstructionsAlignment);
|
||||||
payload_stream_start = instructions_blob_stream_.Position();
|
|
||||||
instructions_blob_stream_.WriteBytes(
|
instructions_blob_stream_.WriteBytes(
|
||||||
reinterpret_cast<const void*>(insns.PayloadStart()), insns.Size());
|
reinterpret_cast<const void*>(insns.PayloadStart()), insns.Size());
|
||||||
instructions_blob_stream_.Align(kSimarmX64InstructionsAlignment);
|
instructions_blob_stream_.Align(kSimarmX64InstructionsAlignment);
|
||||||
|
@ -1011,61 +945,16 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
text_offset += (end_offset - start_offset);
|
text_offset += (end_offset - start_offset);
|
||||||
USE(end);
|
USE(end);
|
||||||
#else // defined(IS_SIMARM_X64)
|
#else // defined(IS_SIMARM_X64)
|
||||||
payload_stream_start = instructions_blob_stream_.Position() +
|
|
||||||
(insns.PayloadStart() - beginning);
|
|
||||||
|
|
||||||
instructions_blob_stream_.WriteWord(marked_tags);
|
instructions_blob_stream_.WriteWord(marked_tags);
|
||||||
text_offset += sizeof(uword);
|
text_offset += sizeof(uword);
|
||||||
beginning += sizeof(uword);
|
beginning += sizeof(uword);
|
||||||
text_offset += WriteByteSequence(beginning, end);
|
text_offset += WriteByteSequence(beginning, end);
|
||||||
#endif // defined(IS_SIMARM_X64)
|
#endif // defined(IS_SIMARM_X64)
|
||||||
|
|
||||||
#if defined(DART_PRECOMPILER)
|
|
||||||
// Don't patch the relocation if we're not generating ELF. The regular blobs
|
|
||||||
// format does not yet support these relocations. Use
|
|
||||||
// Code::VerifyBSSRelocations to check whether the relocations are patched
|
|
||||||
// or not after loading.
|
|
||||||
if (elf_ != nullptr) {
|
|
||||||
const intptr_t current_stream_position =
|
|
||||||
instructions_blob_stream_.Position();
|
|
||||||
|
|
||||||
descriptors = data.code_->pc_descriptors();
|
|
||||||
|
|
||||||
PcDescriptors::Iterator iterator(
|
|
||||||
descriptors, /*kind_mask=*/RawPcDescriptors::kBSSRelocation);
|
|
||||||
|
|
||||||
while (iterator.MoveNext()) {
|
|
||||||
const intptr_t reloc_offset = iterator.PcOffset();
|
|
||||||
|
|
||||||
// The instruction stream at the relocation position holds an offset
|
|
||||||
// into BSS corresponding to the symbol being resolved. This addend is
|
|
||||||
// factored into the relocation.
|
|
||||||
const auto addend = *reinterpret_cast<compiler::target::word*>(
|
|
||||||
insns.PayloadStart() + reloc_offset);
|
|
||||||
|
|
||||||
// Overwrite the relocation position in the instruction stream with the
|
|
||||||
// (positive) offset of the start of the payload from the start of the
|
|
||||||
// BSS segment plus the addend in the relocation.
|
|
||||||
instructions_blob_stream_.SetPosition(payload_stream_start +
|
|
||||||
reloc_offset);
|
|
||||||
|
|
||||||
const uword offset =
|
|
||||||
bss_base - (segment_base + payload_stream_start + reloc_offset) +
|
|
||||||
addend;
|
|
||||||
instructions_blob_stream_.WriteTargetWord(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore stream position after the relocation was patched.
|
|
||||||
instructions_blob_stream_.SetPosition(current_stream_position);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ASSERT((text_offset - instr_start) ==
|
ASSERT((text_offset - instr_start) ==
|
||||||
ImageWriter::SizeInSnapshot(insns.raw()));
|
ImageWriter::SizeInSnapshot(insns.raw()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ASSERT(instructions_blob_stream_.bytes_written() == instructions_length);
|
|
||||||
|
|
||||||
#ifdef DART_PRECOMPILER
|
#ifdef DART_PRECOMPILER
|
||||||
if (elf_ != nullptr) {
|
if (elf_ != nullptr) {
|
||||||
const char* instructions_symbol = vm ? "_kDartVmSnapshotInstructions"
|
const char* instructions_symbol = vm ? "_kDartVmSnapshotInstructions"
|
||||||
|
@ -1074,9 +963,6 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
|
||||||
elf_->AddText(instructions_symbol, instructions_blob_stream_.buffer(),
|
elf_->AddText(instructions_symbol, instructions_blob_stream_.buffer(),
|
||||||
instructions_blob_stream_.bytes_written());
|
instructions_blob_stream_.bytes_written());
|
||||||
ASSERT(segment_base == segment_base2);
|
ASSERT(segment_base == segment_base2);
|
||||||
|
|
||||||
const intptr_t real_bss_base = elf_->AddBSSData("_kDartVMBSSData", 8);
|
|
||||||
ASSERT(bss_base == real_bss_base);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,11 +48,6 @@ class Image : ValueObject {
|
||||||
return snapshot_size - kHeaderSize;
|
return snapshot_size - kHeaderSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
uword bss_offset() const {
|
|
||||||
return *(reinterpret_cast<const uword*>(raw_memory_) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr intptr_t kHeaderFields = 2;
|
|
||||||
static const intptr_t kHeaderSize = OS::kMaxPreferredCodeAlignment;
|
static const intptr_t kHeaderSize = OS::kMaxPreferredCodeAlignment;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -319,15 +314,8 @@ class AssemblyImageWriter : public ImageWriter {
|
||||||
void FrameUnwindPrologue();
|
void FrameUnwindPrologue();
|
||||||
void FrameUnwindEpilogue();
|
void FrameUnwindEpilogue();
|
||||||
intptr_t WriteByteSequence(uword start, uword end);
|
intptr_t WriteByteSequence(uword start, uword end);
|
||||||
|
|
||||||
#if defined(TARGET_ARCH_IS_64_BIT)
|
|
||||||
const char* kLiteralPrefix = ".quad";
|
|
||||||
#else
|
|
||||||
const char* kLiteralPrefix = ".long";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void WriteWordLiteralText(compiler::target::uword value) {
|
void WriteWordLiteralText(compiler::target::uword value) {
|
||||||
// Padding is helpful for comparing the .S with --disassemble.
|
// Padding is helpful for comparing the .S with --disassemble.
|
||||||
#if defined(TARGET_ARCH_IS_64_BIT)
|
#if defined(TARGET_ARCH_IS_64_BIT)
|
||||||
assembly_stream_.Print(".quad 0x%0.16" Px "\n", value);
|
assembly_stream_.Print(".quad 0x%0.16" Px "\n", value);
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -35,6 +35,15 @@ class String;
|
||||||
|
|
||||||
#define NATIVE_ENTRY_FUNCTION(name) BootstrapNatives::DN_##name
|
#define NATIVE_ENTRY_FUNCTION(name) BootstrapNatives::DN_##name
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define SET_NATIVE_RETVAL(args, value) \
|
||||||
|
RawObject* retval = value; \
|
||||||
|
ASSERT(retval->IsDartInstance()); \
|
||||||
|
arguments->SetReturnUnsafe(retval);
|
||||||
|
#else
|
||||||
|
#define SET_NATIVE_RETVAL(arguments, value) arguments->SetReturnUnsafe(value);
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DEFINE_NATIVE_ENTRY(name, type_argument_count, argument_count) \
|
#define DEFINE_NATIVE_ENTRY(name, type_argument_count, argument_count) \
|
||||||
static RawObject* DN_Helper##name(Isolate* isolate, Thread* thread, \
|
static RawObject* DN_Helper##name(Isolate* isolate, Thread* thread, \
|
||||||
Zone* zone, NativeArguments* arguments); \
|
Zone* zone, NativeArguments* arguments); \
|
||||||
|
@ -54,15 +63,9 @@ class String;
|
||||||
Isolate* isolate = thread->isolate(); \
|
Isolate* isolate = thread->isolate(); \
|
||||||
TransitionGeneratedToVM transition(thread); \
|
TransitionGeneratedToVM transition(thread); \
|
||||||
StackZone zone(thread); \
|
StackZone zone(thread); \
|
||||||
/* Be careful holding return_value_unsafe without a handle here. */ \
|
SET_NATIVE_RETVAL( \
|
||||||
/* A return of Object::sentinel means the return value has already */ \
|
arguments, \
|
||||||
/* been set. */ \
|
DN_Helper##name(isolate, thread, zone.GetZone(), arguments)); \
|
||||||
RawObject* return_value_unsafe = \
|
|
||||||
DN_Helper##name(isolate, thread, zone.GetZone(), arguments); \
|
|
||||||
if (return_value_unsafe != Object::sentinel().raw()) { \
|
|
||||||
ASSERT(return_value_unsafe->IsDartInstance()); \
|
|
||||||
arguments->SetReturnUnsafe(return_value_unsafe); \
|
|
||||||
} \
|
|
||||||
DEOPTIMIZE_ALOT; \
|
DEOPTIMIZE_ALOT; \
|
||||||
} \
|
} \
|
||||||
VERIFY_ON_TRANSITION; \
|
VERIFY_ON_TRANSITION; \
|
||||||
|
|
|
@ -12437,8 +12437,8 @@ RawInstructions* Instructions::New(intptr_t size,
|
||||||
result ^= raw;
|
result ^= raw;
|
||||||
result.SetSize(size);
|
result.SetSize(size);
|
||||||
result.SetHasSingleEntryPoint(has_single_entry_point);
|
result.SetHasSingleEntryPoint(has_single_entry_point);
|
||||||
result.set_unchecked_entrypoint_pc_offset(unchecked_entrypoint_pc_offset);
|
|
||||||
result.set_stats(nullptr);
|
result.set_stats(nullptr);
|
||||||
|
result.set_unchecked_entrypoint_pc_offset(unchecked_entrypoint_pc_offset);
|
||||||
}
|
}
|
||||||
return result.raw();
|
return result.raw();
|
||||||
}
|
}
|
||||||
|
@ -12641,8 +12641,6 @@ const char* PcDescriptors::KindAsStr(RawPcDescriptors::Kind kind) {
|
||||||
return "osr-entry ";
|
return "osr-entry ";
|
||||||
case RawPcDescriptors::kRewind:
|
case RawPcDescriptors::kRewind:
|
||||||
return "rewind ";
|
return "rewind ";
|
||||||
case RawPcDescriptors::kBSSRelocation:
|
|
||||||
return "bss reloc ";
|
|
||||||
case RawPcDescriptors::kOther:
|
case RawPcDescriptors::kOther:
|
||||||
return "other ";
|
return "other ";
|
||||||
case RawPcDescriptors::kAnyKind:
|
case RawPcDescriptors::kAnyKind:
|
||||||
|
@ -15168,25 +15166,6 @@ void Code::DumpSourcePositions() const {
|
||||||
reader.DumpSourcePositions(PayloadStart());
|
reader.DumpSourcePositions(PayloadStart());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Code::VerifyBSSRelocations() const {
|
|
||||||
const auto& descriptors = PcDescriptors::Handle(pc_descriptors());
|
|
||||||
const auto& insns = Instructions::Handle(instructions());
|
|
||||||
PcDescriptors::Iterator iterator(descriptors,
|
|
||||||
RawPcDescriptors::kBSSRelocation);
|
|
||||||
while (iterator.MoveNext()) {
|
|
||||||
const uword reloc = insns.PayloadStart() + iterator.PcOffset();
|
|
||||||
const word target = *reinterpret_cast<word*>(reloc);
|
|
||||||
// The relocation is in its original unpatched form -- the addend
|
|
||||||
// representing the target symbol itself.
|
|
||||||
if (target >= 0 &&
|
|
||||||
target <
|
|
||||||
BSS::RelocationIndex(BSS::Relocation::NumRelocations) * kWordSize) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bytecode::Disassemble(DisassemblyFormatter* formatter) const {
|
void Bytecode::Disassemble(DisassemblyFormatter* formatter) const {
|
||||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
|
|
@ -5409,9 +5409,6 @@ class Code : public Object {
|
||||||
|
|
||||||
void Disassemble(DisassemblyFormatter* formatter = NULL) const;
|
void Disassemble(DisassemblyFormatter* formatter = NULL) const;
|
||||||
|
|
||||||
// Returns true if all BSS relocations in the code have been patched.
|
|
||||||
bool VerifyBSSRelocations() const;
|
|
||||||
|
|
||||||
class Comments : public ZoneAllocated {
|
class Comments : public ZoneAllocated {
|
||||||
public:
|
public:
|
||||||
static Comments& New(intptr_t count);
|
static Comments& New(intptr_t count);
|
||||||
|
|
|
@ -1514,9 +1514,7 @@ class RawPcDescriptors : public RawObject {
|
||||||
V(OsrEntry, kRuntimeCall << 1) \
|
V(OsrEntry, kRuntimeCall << 1) \
|
||||||
/* Call rewind target address. */ \
|
/* Call rewind target address. */ \
|
||||||
V(Rewind, kOsrEntry << 1) \
|
V(Rewind, kOsrEntry << 1) \
|
||||||
/* Target-word-size relocation. */ \
|
V(Other, kRewind << 1) \
|
||||||
V(BSSRelocation, kRewind << 1) \
|
|
||||||
V(Other, kBSSRelocation << 1) \
|
|
||||||
V(AnyKind, -1)
|
V(AnyKind, -1)
|
||||||
|
|
||||||
enum Kind {
|
enum Kind {
|
||||||
|
|
|
@ -117,7 +117,6 @@ class ObjectPointerVisitor;
|
||||||
V(ExternalTwoByteString, "_ExternalTwoByteString") \
|
V(ExternalTwoByteString, "_ExternalTwoByteString") \
|
||||||
V(FactoryResult, "factory result") \
|
V(FactoryResult, "factory result") \
|
||||||
V(FallThroughError, "FallThroughError") \
|
V(FallThroughError, "FallThroughError") \
|
||||||
V(FfiCallback, "_FfiCallback") \
|
|
||||||
V(FfiDouble, "Double") \
|
V(FfiDouble, "Double") \
|
||||||
V(FfiDynamicLibrary, "DynamicLibrary") \
|
V(FfiDynamicLibrary, "DynamicLibrary") \
|
||||||
V(FfiFloat, "Float") \
|
V(FfiFloat, "Float") \
|
||||||
|
|
|
@ -941,7 +941,7 @@ DisableThreadInterruptsScope::~DisableThreadInterruptsScope() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const intptr_t kInitialCallbackIdsReserved = 1024;
|
const intptr_t kInitialCallbackIdsReserved = 1024;
|
||||||
int32_t Thread::AllocateFfiCallbackId() {
|
int32_t Thread::AllocateFfiCallbackId(uword* trampoline) {
|
||||||
Zone* Z = isolate()->current_zone();
|
Zone* Z = isolate()->current_zone();
|
||||||
if (ffi_callback_code_ == GrowableObjectArray::null()) {
|
if (ffi_callback_code_ == GrowableObjectArray::null()) {
|
||||||
ffi_callback_code_ = GrowableObjectArray::New(kInitialCallbackIdsReserved);
|
ffi_callback_code_ = GrowableObjectArray::New(kInitialCallbackIdsReserved);
|
||||||
|
@ -955,7 +955,7 @@ int32_t Thread::AllocateFfiCallbackId() {
|
||||||
if (NativeCallbackTrampolines::Enabled()) {
|
if (NativeCallbackTrampolines::Enabled()) {
|
||||||
auto* const tramps = isolate()->native_callback_trampolines();
|
auto* const tramps = isolate()->native_callback_trampolines();
|
||||||
ASSERT(tramps->next_callback_id() == id);
|
ASSERT(tramps->next_callback_id() == id);
|
||||||
tramps->AllocateTrampoline();
|
*trampoline = tramps->AllocateTrampoline();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -964,25 +964,7 @@ int32_t Thread::AllocateFfiCallbackId() {
|
||||||
|
|
||||||
void Thread::SetFfiCallbackCode(int32_t callback_id, const Code& code) {
|
void Thread::SetFfiCallbackCode(int32_t callback_id, const Code& code) {
|
||||||
Zone* Z = isolate()->current_zone();
|
Zone* Z = isolate()->current_zone();
|
||||||
|
|
||||||
/// In AOT the callback ID might have been allocated during compilation but
|
|
||||||
/// 'ffi_callback_code_' is initialized to empty again when the program
|
|
||||||
/// starts. Therefore we may need to initialize or expand it to accomodate
|
|
||||||
/// the callback ID.
|
|
||||||
|
|
||||||
if (ffi_callback_code_ == GrowableObjectArray::null()) {
|
|
||||||
ffi_callback_code_ = GrowableObjectArray::New(kInitialCallbackIdsReserved);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& array = GrowableObjectArray::Handle(Z, ffi_callback_code_);
|
const auto& array = GrowableObjectArray::Handle(Z, ffi_callback_code_);
|
||||||
|
|
||||||
if (callback_id >= array.Length()) {
|
|
||||||
if (callback_id >= array.Capacity()) {
|
|
||||||
array.Grow(callback_id + 1);
|
|
||||||
}
|
|
||||||
array.SetLength(callback_id + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
array.SetAt(callback_id, code);
|
array.SetAt(callback_id, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "platform/atomic.h"
|
#include "platform/atomic.h"
|
||||||
#include "platform/safe_stack.h"
|
#include "platform/safe_stack.h"
|
||||||
#include "vm/bitfield.h"
|
#include "vm/bitfield.h"
|
||||||
#include "vm/compiler/runtime_api.h"
|
|
||||||
#include "vm/constants.h"
|
#include "vm/constants.h"
|
||||||
#include "vm/globals.h"
|
#include "vm/globals.h"
|
||||||
#include "vm/handles.h"
|
#include "vm/handles.h"
|
||||||
|
@ -24,6 +23,7 @@
|
||||||
#include "vm/runtime_entry_list.h"
|
#include "vm/runtime_entry_list.h"
|
||||||
#include "vm/thread_stack_resource.h"
|
#include "vm/thread_stack_resource.h"
|
||||||
#include "vm/thread_state.h"
|
#include "vm/thread_state.h"
|
||||||
|
|
||||||
namespace dart {
|
namespace dart {
|
||||||
|
|
||||||
class AbstractType;
|
class AbstractType;
|
||||||
|
@ -786,11 +786,7 @@ class Thread : public ThreadState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t AllocateFfiCallbackId();
|
int32_t AllocateFfiCallbackId(uword* trampoline);
|
||||||
|
|
||||||
// Store 'code' for the native callback identified by 'callback_id'.
|
|
||||||
//
|
|
||||||
// Expands the callback code array as necessary to accomodate the callback ID.
|
|
||||||
void SetFfiCallbackCode(int32_t callback_id, const Code& code);
|
void SetFfiCallbackCode(int32_t callback_id, const Code& code);
|
||||||
|
|
||||||
// Ensure that 'callback_id' refers to a valid callback in this isolate.
|
// Ensure that 'callback_id' refers to a valid callback in this isolate.
|
||||||
|
|
|
@ -19,8 +19,6 @@ vm_sources = [
|
||||||
"bootstrap.h",
|
"bootstrap.h",
|
||||||
"bootstrap_natives.cc",
|
"bootstrap_natives.cc",
|
||||||
"bootstrap_natives.h",
|
"bootstrap_natives.h",
|
||||||
"bss_relocs.cc",
|
|
||||||
"bss_relocs.h",
|
|
||||||
"class_finalizer.cc",
|
"class_finalizer.cc",
|
||||||
"class_finalizer.h",
|
"class_finalizer.h",
|
||||||
"class_id.h",
|
"class_id.h",
|
||||||
|
|
|
@ -58,8 +58,7 @@ class Pointer<T extends NativeType> extends NativeType {
|
||||||
/// Does not accept dynamic invocations -- where the type of the receiver is
|
/// Does not accept dynamic invocations -- where the type of the receiver is
|
||||||
/// [dynamic].
|
/// [dynamic].
|
||||||
external static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
external static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
||||||
@DartRepresentationOf("T") Function f,
|
@DartRepresentationOf("T") Function f, Object exceptionalReturn);
|
||||||
[Object exceptionalReturn]);
|
|
||||||
|
|
||||||
/// Store a Dart value into this location.
|
/// Store a Dart value into this location.
|
||||||
///
|
///
|
||||||
|
|
|
@ -58,8 +58,7 @@ class Pointer<T extends NativeType> extends NativeType {
|
||||||
/// Does not accept dynamic invocations -- where the type of the receiver is
|
/// Does not accept dynamic invocations -- where the type of the receiver is
|
||||||
/// [dynamic].
|
/// [dynamic].
|
||||||
external static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
external static Pointer<NativeFunction<T>> fromFunction<T extends Function>(
|
||||||
@DartRepresentationOf("T") Function f,
|
@DartRepresentationOf("T") Function f, Object exceptionalReturn);
|
||||||
[Object exceptionalReturn]);
|
|
||||||
|
|
||||||
/// Store a Dart value into this location.
|
/// Store a Dart value into this location.
|
||||||
///
|
///
|
||||||
|
|
|
@ -5,15 +5,6 @@
|
||||||
[ $arch == simdbc || $arch == simdbc64 ]
|
[ $arch == simdbc || $arch == simdbc64 ]
|
||||||
*: Skip # SIMDBC will be deleted soon.
|
*: Skip # SIMDBC will be deleted soon.
|
||||||
|
|
||||||
# Issue 37295, not yet supported in blobs snapshots at present.
|
|
||||||
[ $compiler == dartkp && $system == windows ]
|
|
||||||
function_callbacks_test: Skip
|
|
||||||
regress_37511_callbacks_test: Skip
|
|
||||||
stacktrace_regress_37910_test: Skip
|
|
||||||
|
|
||||||
[ $compiler != dartkp || $system != windows ]
|
|
||||||
function_callbacks_unsupported_test: SkipByDesign # See above
|
|
||||||
|
|
||||||
[ $builder_tag == asan ]
|
[ $builder_tag == asan ]
|
||||||
data_not_asan_test: SkipByDesign # This test tries to allocate too much memory on purpose.
|
data_not_asan_test: SkipByDesign # This test tries to allocate too much memory on purpose.
|
||||||
|
|
||||||
|
@ -25,6 +16,11 @@ function_callbacks_test/01: Skip
|
||||||
function_callbacks_test/02: Skip
|
function_callbacks_test/02: Skip
|
||||||
function_callbacks_test/03: Skip
|
function_callbacks_test/03: Skip
|
||||||
|
|
||||||
|
[ $runtime == dart_precompiled ]
|
||||||
|
function_callbacks_test: Skip # Issue dartbug.com/37295
|
||||||
|
regress_37511_callbacks_test: Skip # Issue dartbug.com/37295
|
||||||
|
stacktrace_regress_37910_test: Skip # Issue dartbug.com/37295
|
||||||
|
|
||||||
[ $arch == arm && $system != android ]
|
[ $arch == arm && $system != android ]
|
||||||
*: Skip # "hardfp" calling convention is not yet supported (iOS is also supported but not tested): dartbug.com/36309
|
*: Skip # "hardfp" calling convention is not yet supported (iOS is also supported but not tested): dartbug.com/36309
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,19 +0,0 @@
|
||||||
// Copyright (c) 2019, 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.
|
|
||||||
//
|
|
||||||
// Dart test program for testing that FFI callbacks report an appropriate
|
|
||||||
// runtime error for unsupported snapshot formats.
|
|
||||||
|
|
||||||
import 'dart:ffi';
|
|
||||||
|
|
||||||
import 'package:expect/expect.dart';
|
|
||||||
|
|
||||||
bool checkError(UnsupportedError err) {
|
|
||||||
return "$err".contains("callbacks are not yet supported in blobs");
|
|
||||||
}
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
Expect.throws<UnsupportedError>(
|
|
||||||
() => Pointer.fromFunction<Void Function()>(main), checkError);
|
|
||||||
}
|
|
|
@ -36,7 +36,6 @@ void main() {
|
||||||
testFromFunctionTypeMismatch();
|
testFromFunctionTypeMismatch();
|
||||||
testFromFunctionClosure();
|
testFromFunctionClosure();
|
||||||
testFromFunctionTearOff();
|
testFromFunctionTearOff();
|
||||||
testFromFunctionAbstract();
|
|
||||||
testLookupFunctionGeneric();
|
testLookupFunctionGeneric();
|
||||||
testLookupFunctionGeneric2();
|
testLookupFunctionGeneric2();
|
||||||
testLookupFunctionWrongNativeFunctionSignature();
|
testLookupFunctionWrongNativeFunctionSignature();
|
||||||
|
@ -249,10 +248,6 @@ void testFromFunctionTearOff() {
|
||||||
p = ffi.fromFunction(fld); //# 75: compile-time error
|
p = ffi.fromFunction(fld); //# 75: compile-time error
|
||||||
}
|
}
|
||||||
|
|
||||||
void testFromFunctionAbstract() {
|
|
||||||
ffi.Pointer.fromFunction<Function>(testFromFunctionAbstract); //# 76: compile-time error
|
|
||||||
}
|
|
||||||
|
|
||||||
void testLookupFunctionGeneric() {
|
void testLookupFunctionGeneric() {
|
||||||
Function generic<T extends Function>() {
|
Function generic<T extends Function>() {
|
||||||
ffi.DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
ffi.DynamicLibrary l = dlopenPlatformSpecific("ffi_test_dynamic_library");
|
||||||
|
|
|
@ -35,5 +35,5 @@ MINOR 6
|
||||||
PATCH 0
|
PATCH 0
|
||||||
PRERELEASE 0
|
PRERELEASE 0
|
||||||
PRERELEASE_PATCH 0
|
PRERELEASE_PATCH 0
|
||||||
ABI_VERSION 14
|
ABI_VERSION 15
|
||||||
OLDEST_SUPPORTED_ABI_VERSION 13
|
OLDEST_SUPPORTED_ABI_VERSION 15
|
||||||
|
|
Loading…
Reference in a new issue