[ffi] Convert ABI-specific integers to fixed-width integers when doing wasm FfiNative transformation.

It's a bug if a class has more than one `@AbiSpecificIntegerMapping`
annotation. Reflect this in the return type of method for getting the
annotation and issue a diagnostic if the invariant doesn't hold.

The invariant is checked by `_FfiDefinitionTransformer`.

Change-Id: Iadf4636090c6ec814f623c9245b3a1fe8b466b4b
Tested: Unit tests written with all ABI specific integer types.
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/268105
Reviewed-by: Daco Harkes <dacoharkes@google.com>
Commit-Queue: Jackson Gardner <jacksongardner@google.com>
Reviewed-by: Ömer Ağacan <omersa@google.com>
This commit is contained in:
Jackson Gardner 2022-11-08 22:16:28 +00:00 committed by Commit Queue
parent 89a74663b5
commit 81a63dafe5
7 changed files with 186 additions and 17 deletions

View file

@ -0,0 +1,8 @@
// Copyright (c) 2022, 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.
// The ABI type sizes are the same for 32-bit Wasm as for 32-bit ARM, so we
// can just use an ABI enum index corresponding to a 32-bit ARM platform.
const int kWasmAbiEnumIndex = 0; // androidArm

View file

@ -8,8 +8,10 @@ import 'package:kernel/core_types.dart';
import 'package:kernel/library_index.dart' show LibraryIndex;
import 'package:kernel/reference_from_index.dart' show ReferenceFromIndex;
import 'package:kernel/target/targets.dart' show DiagnosticReporter;
import 'package:vm/transformations/ffi/abi.dart' show Abi;
import 'package:vm/transformations/ffi/common.dart' show NativeType;
import 'package:vm/transformations/ffi/native.dart' show FfiNativeTransformer;
import 'abi.dart' show kWasmAbiEnumIndex;
/// Transform `@FfiNative`-annotated functions to convert Dart arguments to
/// Wasm arguments expected by the FFI functions, and convert the Wasm function
@ -236,7 +238,8 @@ class WasmFfiNativeTransformer extends FfiNativeTransformer {
///
/// Returns `null` for [Void] values.
Expression? _dartValueToFfiValue(DartType ffiType, Expression expr) {
final InterfaceType abiType_ = ffiType as InterfaceType;
final InterfaceType abiType_ =
_getFixedWidthIntegerFromAbiSpecificInteger(ffiType as InterfaceType);
final NativeType abiTypeNativeType = getType(abiType_.classNode)!;
switch (abiTypeNativeType) {
@ -292,7 +295,8 @@ class WasmFfiNativeTransformer extends FfiNativeTransformer {
/// For example, converts an `Bool` native type to Dart bool by checking the
/// Wasm I32 value for the bool: 0 means `false`, non-0 means `true`.
Expression _ffiValueToDartValue(DartType ffiType, Expression expr) {
final InterfaceType ffiType_ = ffiType as InterfaceType;
final InterfaceType ffiType_ =
_getFixedWidthIntegerFromAbiSpecificInteger(ffiType as InterfaceType);
final NativeType nativeType = getType(ffiType_.classNode)!;
Expression instanceInvocation(Procedure converter, Expression receiver) =>
@ -344,6 +348,22 @@ class WasmFfiNativeTransformer extends FfiNativeTransformer {
}
}
InterfaceType _getFixedWidthIntegerFromAbiSpecificInteger(
InterfaceType ffiType) {
final MapConstant? abiIntegerMapping =
getAbiSpecificIntegerMappingAnnotation(ffiType.classNode);
if (abiIntegerMapping == null) {
// This isn't an ABI specific integer. Just return the type itself
return ffiType;
}
final Abi wasmAbi = Abi.values[kWasmAbiEnumIndex];
final entry = abiIntegerMapping.entries
.firstWhere((e) => constantAbis[e.key] == wasmAbi);
return (entry.value as InstanceConstant)
.classNode
.getThisType(coreTypes, Nullability.nonNullable);
}
/// Converts an FFI type like `InterfaceType(Int8)` to the corresponding Wasm
/// type (`InterfaceType(WasmI32)`) according to emscripten Wasm ABI.
///
@ -353,8 +373,8 @@ class WasmFfiNativeTransformer extends FfiNativeTransformer {
if (ffiType is! InterfaceType) {
throw 'Native type is not an interface type: $ffiType';
}
final Class nativeClass = ffiType.classNode;
final NativeType nativeType_ = getType(nativeClass)!;
ffiType = _getFixedWidthIntegerFromAbiSpecificInteger(ffiType);
final NativeType nativeType_ = getType(ffiType.classNode)!;
switch (nativeType_) {
case NativeType.kInt8:

View file

@ -9,6 +9,7 @@ import 'package:dart2wasm/translator.dart';
import 'package:kernel/ast.dart';
import 'package:wasm_builder/wasm_builder.dart' as w;
import 'abi.dart' show kWasmAbiEnumIndex;
typedef CodeGenCallback = void Function(w.Instructions);
@ -19,10 +20,6 @@ typedef CodeGenCallback = void Function(w.Instructions);
class Intrinsifier {
final CodeGenerator codeGen;
// The ABI type sizes are the same for 32-bit Wasm as for 32-bit ARM, so we
// can just use an ABI enum index corresponding to a 32-bit ARM platform.
static const int abiEnumIndex = 0; // androidArm
static const w.ValueType boolType = w.NumType.i32;
static const w.ValueType intType = w.NumType.i64;
static const w.ValueType doubleType = w.NumType.f64;
@ -487,7 +484,7 @@ class Intrinsifier {
} else if (arg is StaticInvocation) {
if (arg.target.enclosingLibrary.name == "dart.ffi" &&
arg.name.text == "_abi") {
constIndex = abiEnumIndex;
constIndex = kWasmAbiEnumIndex;
}
}
if (constIndex != null) {

View file

@ -924,14 +924,21 @@ class FfiTransformer extends Transformer {
functionType: numMultiplication.getterType as FunctionType);
}
Iterable<MapConstant> getAbiSpecificIntegerMappingAnnotations(Class node) {
return node.annotations
MapConstant? getAbiSpecificIntegerMappingAnnotation(Class node) {
final annotations = node.annotations
.whereType<ConstantExpression>()
.map((e) => e.constant)
.whereType<InstanceConstant>()
.where((e) => e.classNode == abiSpecificIntegerMappingClass)
.map((instanceConstant) =>
instanceConstant.fieldValues.values.single as MapConstant);
instanceConstant.fieldValues.values.single as MapConstant)
.toList();
// There can be at most one annotation (checked by `_FfiDefinitionTransformer`)
if (annotations.length == 1) {
return annotations[0];
}
return null;
}
/// Generates an expression performing an Abi specific integer load or store.

View file

@ -51,14 +51,14 @@ abstract class NativeTypeCfe {
}
if (transformer.isAbiSpecificIntegerSubtype(dartType)) {
final clazz = (dartType as InterfaceType).classNode;
final mappingConstants =
transformer.getAbiSpecificIntegerMappingAnnotations(clazz);
if (alreadyInAbiSpecificType || mappingConstants.length != 1) {
final mappingConstant =
transformer.getAbiSpecificIntegerMappingAnnotation(clazz);
if (alreadyInAbiSpecificType || mappingConstant == null) {
// Unsupported mapping.
return AbiSpecificNativeTypeCfe({}, clazz);
}
final mapping =
Map.fromEntries(mappingConstants.first.entries.map((e) => MapEntry(
Map.fromEntries(mappingConstant.entries.map((e) => MapEntry(
transformer.constantAbis[e.key]!,
NativeTypeCfe(
transformer,

View file

@ -39,7 +39,7 @@ external int addUint64(int a, int b);
@FfiNative<Bool Function(Bool)>("ffi.negateBool")
external bool negateBool(bool b);
@FfiNative<Bool Function(Int32)>("ffi.boolReturn")
@FfiNative<Bool Function(Int)>("ffi.boolReturn")
external bool boolReturn(int b);
@FfiNative<Void Function()>("ffi.toggleBool")
@ -48,6 +48,52 @@ external void toggleBool();
@FfiNative<Double Function(Double)>("ffi.sqrt")
external double sqrt(double d);
@FfiNative<Char Function(Char)>("ffi.incrementChar")
external int incrementChar(int a);
@FfiNative<UnsignedChar Function(UnsignedChar)>("ffi.incrementUnsignedChar")
external int incrementUnsignedChar(int a);
@FfiNative<SignedChar Function(SignedChar)>("ffi.incrementSignedChar")
external int incrementSignedChar(int a);
@FfiNative<Short Function(Short)>("ffi.incrementShort")
external int incrementShort(int a);
@FfiNative<UnsignedShort Function(UnsignedShort)>("ffi.incrementUnsignedShort")
external int incrementUnsignedShort(int a);
@FfiNative<Int Function(Int)>("ffi.incrementInt")
external int incrementInt(int a);
@FfiNative<UnsignedInt Function(UnsignedInt)>("ffi.incrementUnsignedInt")
external int incrementUnsignedInt(int a);
@FfiNative<Long Function(Long)>("ffi.incrementLong")
external int incrementLong(int a);
@FfiNative<UnsignedLong Function(UnsignedLong)>("ffi.incrementUnsignedLong")
external int incrementUnsignedLong(int a);
@FfiNative<LongLong Function(LongLong)>("ffi.incrementLongLong")
external int incrementLongLong(int a);
@FfiNative<UnsignedLongLong Function(UnsignedLongLong)>(
"ffi.incrementUnsignedLongLong")
external int incrementUnsignedLongLong(int a);
@FfiNative<IntPtr Function(IntPtr)>("ffi.incrementIntPtr")
external int incrementIntPtr(int a);
@FfiNative<UintPtr Function(UintPtr)>("ffi.incrementUintPtr")
external int incrementUintPtr(int a);
@FfiNative<Size Function(Size)>("ffi.incrementSize")
external int incrementSize(int a);
@FfiNative<WChar Function(WChar)>("ffi.incrementWchar")
external int incrementWchar(int a);
class MyStruct extends Struct implements NativeFieldWrapperClass1 {
@Double()
external double x;
@ -104,4 +150,20 @@ void main() {
clearStruct(struct_);
Expect.equals(struct_.ref.x, 0.0);
Expect.equals(struct_.ref.y, 0);
Expect.equals(incrementChar(1), 2);
Expect.equals(incrementUnsignedChar(3), 4);
Expect.equals(incrementSignedChar(5), 6);
Expect.equals(incrementShort(7), 8);
Expect.equals(incrementUnsignedShort(9), 10);
Expect.equals(incrementInt(11), 12);
Expect.equals(incrementUnsignedInt(13), 14);
Expect.equals(incrementLong(15), 16);
Expect.equals(incrementUnsignedLong(17), 18);
Expect.equals(incrementLongLong(19), 20);
Expect.equals(incrementUnsignedLongLong(21), 22);
Expect.equals(incrementIntPtr(23), 24);
Expect.equals(incrementUintPtr(25), 26);
Expect.equals(incrementSize(27), 28);
Expect.equals(incrementWchar(29), 30);
}

View file

@ -88,3 +88,78 @@ bool boolReturn(int a) {
return b;
}
}
EMSCRIPTEN_KEEPALIVE
char incrementChar(char a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
unsigned char incrementUnsignedChar(unsigned char a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
signed char incrementSignedChar(signed char a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
short incrementShort(short a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
unsigned short incrementUnsignedShort(unsigned short a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
int incrementInt(int a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
unsigned int incrementUnsignedInt(unsigned int a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
long incrementLong(long a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
unsigned long incrementUnsignedLong(unsigned long a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
long long incrementLongLong(long long a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
unsigned long long incrementUnsignedLongLong(unsigned long long a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
intptr_t incrementIntPtr(intptr_t a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
uintptr_t incrementUintPtr(uintptr_t a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
size_t incrementSize(size_t a) {
return a + 1;
}
EMSCRIPTEN_KEEPALIVE
wchar_t incrementWchar(wchar_t a) {
return a + 1;
}