51f4574e50
Fixes alignment checks on GCC <= 7 and Clang <= 7. Fixes: https://github.com/dart-lang/sdk/issues/37559 Fixes: https://github.com/dart-lang/sdk/issues/36334 Original CL is in patchset 1. Tested GCC 7 and Clang 7 behavior of alignof and offsetof manually (our CQ uses newer tooling). Change-Id: I828b00ad3d36be68a0d28b0a710fea938ca17784 Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try, app-kernel-linux-debug-x64-try, vm-kernel-linux-debug-simdbc64-try,vm-kernel-linux-debug-ia32-try,vm-dartkb-linux-debug-simarm64-try,vm-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-try,vm-dartkb-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-ffi-android-product-arm-try,vm-dartkb-linux-release-x64-abi-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/112254 Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Daco Harkes <dacoharkes@google.com>
313 lines
11 KiB
Dart
313 lines
11 KiB
Dart
// 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.
|
|
|
|
// This file contains logic which is shared between the ffi_definition and
|
|
// ffi_use_site transformers.
|
|
|
|
library vm.transformations.ffi;
|
|
|
|
import 'package:kernel/ast.dart';
|
|
import 'package:kernel/class_hierarchy.dart' show ClassHierarchy;
|
|
import 'package:kernel/core_types.dart';
|
|
import 'package:kernel/library_index.dart' show LibraryIndex;
|
|
import 'package:kernel/target/targets.dart' show DiagnosticReporter;
|
|
import 'package:kernel/type_environment.dart' show TypeEnvironment;
|
|
|
|
/// Represents the (instantiated) ffi.NativeType.
|
|
enum NativeType {
|
|
kNativeType,
|
|
kNativeInteger,
|
|
kNativeDouble,
|
|
kPointer,
|
|
kNativeFunction,
|
|
kInt8,
|
|
kInt16,
|
|
kInt32,
|
|
kInt64,
|
|
kUint8,
|
|
kUint16,
|
|
kUint32,
|
|
kUnit64,
|
|
kIntptr,
|
|
kFloat,
|
|
kDouble,
|
|
kVoid,
|
|
kStruct
|
|
}
|
|
|
|
const NativeType kNativeTypeIntStart = NativeType.kInt8;
|
|
const NativeType kNativeTypeIntEnd = NativeType.kIntptr;
|
|
|
|
/// The [NativeType] class names, indexed by [NativeType].
|
|
const List<String> nativeTypeClassNames = [
|
|
'NativeType',
|
|
'_NativeInteger',
|
|
'_NativeDouble',
|
|
'Pointer',
|
|
'NativeFunction',
|
|
'Int8',
|
|
'Int16',
|
|
'Int32',
|
|
'Int64',
|
|
'Uint8',
|
|
'Uint16',
|
|
'Uint32',
|
|
'Uint64',
|
|
'IntPtr',
|
|
'Float',
|
|
'Double',
|
|
'Void',
|
|
'Struct'
|
|
];
|
|
|
|
const int UNKNOWN = 0;
|
|
const int WORD_SIZE = -1;
|
|
|
|
/// The [NativeType] sizes in bytes, indexed by [NativeType].
|
|
const List<int> nativeTypeSizes = [
|
|
UNKNOWN, // NativeType
|
|
UNKNOWN, // NativeInteger
|
|
UNKNOWN, // NativeDouble
|
|
WORD_SIZE, // Pointer
|
|
UNKNOWN, // NativeFunction
|
|
1, // Int8
|
|
2, // Int16
|
|
4, // Int32
|
|
8, // Int64
|
|
1, // Uint8
|
|
2, // Uint16
|
|
4, // Uint32
|
|
8, // Uint64
|
|
WORD_SIZE, // IntPtr
|
|
4, // Float
|
|
8, // Double
|
|
UNKNOWN, // Void
|
|
UNKNOWN, // Struct
|
|
];
|
|
|
|
/// The struct layout in various ABIs.
|
|
///
|
|
/// ABIs differ per architectures and with different compilers.
|
|
/// We pick the default struct layout based on the architecture and OS.
|
|
///
|
|
/// Compilers _can_ deviate from the default layout, but this prevents
|
|
/// executables from making system calls. So this seems rather uncommon.
|
|
///
|
|
/// In the future, we might support custom struct layouts. For more info see
|
|
/// https://github.com/dart-lang/sdk/issues/35768.
|
|
enum Abi {
|
|
/// Layout in all 64bit ABIs (x64 and arm64).
|
|
wordSize64,
|
|
|
|
/// Layout in System V ABI for x386 (ia32 on Linux) and in iOS Arm 32 bit.
|
|
wordSize32Align32,
|
|
|
|
/// Layout in both the Arm 32 bit ABI and the Windows ia32 ABI.
|
|
wordSize32Align64,
|
|
}
|
|
|
|
/// WORD_SIZE in bytes.
|
|
const wordSize = <Abi, int>{
|
|
Abi.wordSize64: 8,
|
|
Abi.wordSize32Align32: 4,
|
|
Abi.wordSize32Align64: 4,
|
|
};
|
|
|
|
/// Elements that are not aligned to their size.
|
|
///
|
|
/// Has an entry for all Abis. Empty entries document that every native
|
|
/// type is aligned to it's own size in this ABI.
|
|
///
|
|
/// See runtime/vm/compiler/ffi.cc for asserts in the VM that verify these
|
|
/// alignments.
|
|
///
|
|
/// TODO(37470): Add uncommon primitive data types when we want to support them.
|
|
const nonSizeAlignment = <Abi, Map<NativeType, int>>{
|
|
Abi.wordSize64: {},
|
|
|
|
// x86 System V ABI:
|
|
// > uint64_t | size 8 | alignment 4
|
|
// > double | size 8 | alignment 4
|
|
// https://github.com/hjl-tools/x86-psABI/wiki/intel386-psABI-1.1.pdf page 8.
|
|
//
|
|
// iOS 32 bit alignment:
|
|
// https://developer.apple.com/documentation/uikit/app_and_environment/updating_your_app_from_32-bit_to_64-bit_architecture/updating_data_structures
|
|
Abi.wordSize32Align32: {NativeType.kDouble: 4, NativeType.kInt64: 4},
|
|
|
|
// The default for MSVC x86:
|
|
// > The alignment-requirement for all data except structures, unions, and
|
|
// > arrays is either the size of the object or the current packing size
|
|
// > (specified with either /Zp or the pack pragma, whichever is less).
|
|
// https://docs.microsoft.com/en-us/cpp/c-language/padding-and-alignment-of-structure-members?view=vs-2019
|
|
//
|
|
// GCC _can_ compile on Linux to this alignment with -malign-double, but does
|
|
// not do so by default:
|
|
// > Warning: if you use the -malign-double switch, structures containing the
|
|
// > above types are aligned differently than the published application
|
|
// > binary interface specifications for the x86-32 and are not binary
|
|
// > compatible with structures in code compiled without that switch.
|
|
// https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
|
|
//
|
|
// Arm always requires 8 byte alignment for 8 byte values:
|
|
// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042d/IHI0042D_aapcs.pdf 4.1 Fundamental Data Types
|
|
Abi.wordSize32Align64: {},
|
|
};
|
|
|
|
/// [FfiTransformer] contains logic which is shared between
|
|
/// _FfiUseSiteTransformer and _FfiDefinitionTransformer.
|
|
class FfiTransformer extends Transformer {
|
|
final TypeEnvironment env;
|
|
final CoreTypes coreTypes;
|
|
final LibraryIndex index;
|
|
final ClassHierarchy hierarchy;
|
|
final DiagnosticReporter diagnosticReporter;
|
|
|
|
final Class intClass;
|
|
final Class doubleClass;
|
|
final Class pragmaClass;
|
|
final Field pragmaName;
|
|
final Field pragmaOptions;
|
|
final Procedure listElementAt;
|
|
|
|
final Library ffiLibrary;
|
|
final Class nativeFunctionClass;
|
|
final Class pointerClass;
|
|
final Class structClass;
|
|
final Procedure castMethod;
|
|
final Procedure loadMethod;
|
|
final Procedure storeMethod;
|
|
final Procedure offsetByMethod;
|
|
final Procedure asFunctionMethod;
|
|
final Procedure asFunctionInternal;
|
|
final Procedure lookupFunctionMethod;
|
|
final Procedure fromFunctionMethod;
|
|
final Field addressOfField;
|
|
final Constructor structFromPointer;
|
|
final Procedure libraryLookupMethod;
|
|
final Procedure abiMethod;
|
|
|
|
/// Classes corresponding to [NativeType], indexed by [NativeType].
|
|
final List<Class> nativeTypesClasses;
|
|
|
|
FfiTransformer(
|
|
this.index, this.coreTypes, this.hierarchy, this.diagnosticReporter)
|
|
: env = new TypeEnvironment(coreTypes, hierarchy),
|
|
intClass = coreTypes.intClass,
|
|
doubleClass = coreTypes.doubleClass,
|
|
pragmaClass = coreTypes.pragmaClass,
|
|
pragmaName = coreTypes.pragmaName,
|
|
pragmaOptions = coreTypes.pragmaOptions,
|
|
listElementAt = coreTypes.index.getMember('dart:core', 'List', '[]'),
|
|
ffiLibrary = index.getLibrary('dart:ffi'),
|
|
nativeFunctionClass = index.getClass('dart:ffi', 'NativeFunction'),
|
|
pointerClass = index.getClass('dart:ffi', 'Pointer'),
|
|
structClass = index.getClass('dart:ffi', 'Struct'),
|
|
castMethod = index.getMember('dart:ffi', 'Pointer', 'cast'),
|
|
loadMethod = index.getMember('dart:ffi', 'Pointer', 'load'),
|
|
storeMethod = index.getMember('dart:ffi', 'Pointer', 'store'),
|
|
offsetByMethod = index.getMember('dart:ffi', 'Pointer', 'offsetBy'),
|
|
addressOfField = index.getMember('dart:ffi', 'Struct', 'addressOf'),
|
|
structFromPointer =
|
|
index.getMember('dart:ffi', 'Struct', 'fromPointer'),
|
|
asFunctionMethod = index.getMember('dart:ffi', 'Pointer', 'asFunction'),
|
|
asFunctionInternal =
|
|
index.getTopLevelMember('dart:ffi', '_asFunctionInternal'),
|
|
lookupFunctionMethod =
|
|
index.getMember('dart:ffi', 'DynamicLibrary', 'lookupFunction'),
|
|
fromFunctionMethod =
|
|
index.getMember('dart:ffi', 'Pointer', 'fromFunction'),
|
|
libraryLookupMethod =
|
|
index.getMember('dart:ffi', 'DynamicLibrary', 'lookup'),
|
|
abiMethod = index.getTopLevelMember('dart:ffi', '_abi'),
|
|
nativeTypesClasses = nativeTypeClassNames
|
|
.map((name) => index.getClass('dart:ffi', name))
|
|
.toList();
|
|
|
|
/// Computes the Dart type corresponding to a ffi.[NativeType], returns null
|
|
/// if it is not a valid NativeType.
|
|
///
|
|
/// [Int8] -> [int]
|
|
/// [Int16] -> [int]
|
|
/// [Int32] -> [int]
|
|
/// [Int64] -> [int]
|
|
/// [Uint8] -> [int]
|
|
/// [Uint16] -> [int]
|
|
/// [Uint32] -> [int]
|
|
/// [Uint64] -> [int]
|
|
/// [IntPtr] -> [int]
|
|
/// [Double] -> [double]
|
|
/// [Float] -> [double]
|
|
/// [Void] -> [void]
|
|
/// [Pointer]<T> -> [Pointer]<T>
|
|
/// T extends [Pointer] -> T
|
|
/// [NativeFunction]<T1 Function(T2, T3) -> S1 Function(S2, S3)
|
|
/// where DartRepresentationOf(Tn) -> Sn
|
|
DartType convertNativeTypeToDartType(DartType nativeType, bool allowStructs) {
|
|
if (nativeType is! InterfaceType) {
|
|
return null;
|
|
}
|
|
InterfaceType native = nativeType;
|
|
Class nativeClass = native.classNode;
|
|
NativeType nativeType_ = getType(nativeClass);
|
|
|
|
if (hierarchy.isSubclassOf(nativeClass, structClass)) {
|
|
return allowStructs ? nativeType : null;
|
|
}
|
|
if (nativeType_ == null) {
|
|
return null;
|
|
}
|
|
if (nativeType_ == NativeType.kPointer) {
|
|
return nativeType;
|
|
}
|
|
if (kNativeTypeIntStart.index <= nativeType_.index &&
|
|
nativeType_.index <= kNativeTypeIntEnd.index) {
|
|
return InterfaceType(intClass);
|
|
}
|
|
if (nativeType_ == NativeType.kFloat || nativeType_ == NativeType.kDouble) {
|
|
return InterfaceType(doubleClass);
|
|
}
|
|
if (nativeType_ == NativeType.kVoid) {
|
|
return VoidType();
|
|
}
|
|
if (nativeType_ != NativeType.kNativeFunction ||
|
|
native.typeArguments[0] is! FunctionType) {
|
|
return null;
|
|
}
|
|
|
|
FunctionType fun = native.typeArguments[0];
|
|
if (fun.namedParameters.isNotEmpty) return null;
|
|
if (fun.positionalParameters.length != fun.requiredParameterCount) {
|
|
return null;
|
|
}
|
|
if (fun.typeParameters.length != 0) return null;
|
|
// TODO(36730): Structs cannot appear in native function signatures.
|
|
DartType returnType =
|
|
convertNativeTypeToDartType(fun.returnType, /*allowStructs=*/ false);
|
|
if (returnType == null) return null;
|
|
List<DartType> argumentTypes = fun.positionalParameters
|
|
.map((t) => convertNativeTypeToDartType(t, /*allowStructs=*/ false))
|
|
.toList();
|
|
if (argumentTypes.contains(null)) return null;
|
|
return FunctionType(argumentTypes, returnType);
|
|
}
|
|
|
|
NativeType getType(Class c) {
|
|
int index = nativeTypesClasses.indexOf(c);
|
|
if (index == -1) {
|
|
return null;
|
|
}
|
|
return NativeType.values[index];
|
|
}
|
|
}
|
|
|
|
/// Contains replaced members, of which all the call sites need to be replaced.
|
|
///
|
|
/// [ReplacedMembers] is populated by _FfiDefinitionTransformer and consumed by
|
|
/// _FfiUseSiteTransformer.
|
|
class ReplacedMembers {
|
|
final Map<Field, Procedure> replacedGetters;
|
|
final Map<Field, Procedure> replacedSetters;
|
|
ReplacedMembers(this.replacedGetters, this.replacedSetters);
|
|
}
|