Skip to content

Commit 597cd06

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
[vm/ffi] Pointer load and store as extension methods
Issue: #37773 Change-Id: I836d6305b613cf05590d872874f4517831be3e08 Cq-Include-Trybots: luci.dart.try:vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64-try,app-kernel-linux-debug-x64-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-dartkb-linux-release-x64-abi-try,vm-kernel-precomp-android-release-arm64-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-mac-debug-simdbc64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-reload-mac-release-simdbc64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-kernel-precomp-mac-release-simarm_x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/118992 Reviewed-by: Samir Jindel <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent 1738852 commit 597cd06

File tree

10 files changed

+1578
-25
lines changed

10 files changed

+1578
-25
lines changed

pkg/vm/lib/transformations/ffi_use_sites.dart

+4
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,10 @@ class _FfiUseSiteTransformer extends FfiTransformer {
364364
// this rewiring.
365365
final DartType pointerType = node.receiver.getStaticType(env);
366366
final DartType nativeType = _pointerTypeGetTypeArg(pointerType);
367+
if (nativeType is TypeParameterType) {
368+
// Do not rewire generic invocations.
369+
return node;
370+
}
367371
final Class nativeClass = (nativeType as InterfaceType).classNode;
368372
final NativeType nt = getType(nativeClass);
369373
if (optimizedTypes.contains(nt)) {

runtime/lib/ffi.cc

+2-18
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,6 @@ static void CheckSized(const AbstractType& type_arg) {
5050

5151
// The following functions are runtime checks on arguments.
5252

53-
static const Pointer& AsPointer(const Instance& instance) {
54-
if (!instance.IsPointer()) {
55-
const String& error = String::Handle(String::NewFormatted(
56-
"Expected a Pointer object but found %s", instance.ToCString()));
57-
Exceptions::ThrowArgumentError(error);
58-
}
59-
return Pointer::Cast(instance);
60-
}
61-
6253
static const Integer& AsInteger(const Instance& instance) {
6354
if (!instance.IsInteger()) {
6455
const String& error = String::Handle(String::NewFormatted(
@@ -294,16 +285,10 @@ CLASS_LIST_FFI_NUMERIC(DEFINE_NATIVE_ENTRY_STORE)
294285

295286
DEFINE_NATIVE_ENTRY(Ffi_storePointer, 0, 2) {
296287
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, pointer, arguments->NativeArgAt(0));
297-
GET_NATIVE_ARGUMENT(Instance, new_value, arguments->NativeArgAt(1));
288+
GET_NON_NULL_NATIVE_ARGUMENT(Pointer, new_value, arguments->NativeArgAt(1));
298289
AbstractType& pointer_type_arg =
299290
AbstractType::Handle(pointer.type_argument());
300291

301-
if (new_value.IsNull()) {
302-
const String& error = String::Handle(
303-
String::NewFormatted("Argument to Pointer.store is null."));
304-
Exceptions::ThrowArgumentError(error);
305-
}
306-
307292
auto& new_value_type =
308293
AbstractType::Handle(zone, new_value.GetType(Heap::kNew));
309294
if (!new_value_type.IsSubtypeOf(pointer_type_arg, Heap::kNew)) {
@@ -315,9 +300,8 @@ DEFINE_NATIVE_ENTRY(Ffi_storePointer, 0, 2) {
315300
}
316301

317302
ASSERT(IsPointerType(pointer_type_arg));
318-
ASSERT(new_value.IsPointer());
319303
uword* slot = reinterpret_cast<uword*>(pointer.NativeAddress());
320-
*slot = AsPointer(new_value).NativeAddress();
304+
*slot = new_value.NativeAddress();
321305
return Object::null();
322306
}
323307

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
//
5+
// This file generates the extension methods public API and the extension
6+
// methods patch file for all integers, double, and float.
7+
// The PointerPointer and PointerStruct extension are written by hand since
8+
// those are not repetitive.
9+
10+
import 'dart:io';
11+
12+
//
13+
// Configuration.
14+
//
15+
16+
const Map<String, String> nativeToDartType = {
17+
"Int8": "int",
18+
"Int16": "int",
19+
"Int32": "int",
20+
"Int64": "int",
21+
"Uint8": "int",
22+
"Uint16": "int",
23+
"Uint32": "int",
24+
"Uint64": "int",
25+
"IntPtr": "int",
26+
"Float": "double",
27+
"Double": "double",
28+
};
29+
30+
//
31+
// Generator.
32+
//
33+
34+
main(List<String> arguments) {
35+
final parsedArgs = parseArguments(arguments);
36+
37+
generate(parsedArgs.path, "ffi.g.dart", generatePublicExtension);
38+
generate(parsedArgs.path, "ffi_patch.g.dart", generatePatchExtension);
39+
}
40+
41+
void generate(Uri path, String fileName,
42+
Function(StringBuffer, String, String) generator) {
43+
final buffer = StringBuffer();
44+
generateHeader(buffer);
45+
nativeToDartType.forEach((String nativeType, String dartType) {
46+
generator(buffer, nativeType, dartType);
47+
});
48+
generateFooter(buffer);
49+
50+
final fullPath = path.resolve(fileName).path;
51+
new File(fullPath).writeAsStringSync(buffer.toString());
52+
final fmtResult = Process.runSync(dartfmtPath().path, ["-w", fullPath]);
53+
if (fmtResult.exitCode != 0) {
54+
throw Exception(
55+
"Formatting failed:\n${fmtResult.stdout}\n${fmtResult.stderr}\n");
56+
}
57+
print("Generated $fullPath.");
58+
}
59+
60+
void generateHeader(StringBuffer buffer) {
61+
final header = """
62+
//
63+
// The following code is generated, do not edit by hand.
64+
//
65+
// Code generated by `runtime/tools/ffi/sdk_lib_ffi_generator.dart`.
66+
//
67+
68+
""";
69+
70+
buffer.write(header);
71+
}
72+
73+
void generatePublicExtension(
74+
StringBuffer buffer, String nativeType, String dartType) {
75+
final storeTrunctateInt = """
76+
/// Note that ints which do not fit in [$nativeType] are truncated.
77+
""";
78+
79+
final storeTrunctateDouble = """
80+
/// Note that doubles stored into Pointer<[Float]> lose precision.
81+
""";
82+
83+
final storeTruncate =
84+
isInt(nativeType) ? storeTrunctateInt : storeTrunctateDouble;
85+
86+
final loadSignExtendInt = """
87+
/// Note that ints are signextended.
88+
""";
89+
90+
final loadSignExtend = isInt(nativeType) ? loadSignExtendInt : "";
91+
92+
buffer.write("""
93+
/// Extension on [Pointer] specialized for the type argument [$nativeType].
94+
extension ${nativeType}Pointer on Pointer<$nativeType> {
95+
/// Load a Dart value from this location.
96+
///
97+
/// The value is automatically unmarshalled from its native representation.
98+
$loadSignExtend ///
99+
/// Note that [address] needs to be aligned to the size of [$nativeType].
100+
external $dartType get value;
101+
102+
/// Store a Dart value into this location.
103+
///
104+
/// The [value] is automatically marshalled into its native representation.
105+
$storeTruncate ///
106+
/// Note that [address] needs to be aligned to the size of [$nativeType].
107+
external void set value($dartType value);
108+
109+
/// Load a Dart value from this location offset by [index].
110+
///
111+
/// The value is automatically unmarshalled from its native representation.
112+
$loadSignExtend ///
113+
/// Note that [address] needs to be aligned to the size of [$nativeType].
114+
external $dartType operator [](int index);
115+
116+
/// Store a Dart value into this location offset by [index].
117+
///
118+
/// The [value] is automatically marshalled into its native representation.
119+
$storeTruncate ///
120+
/// Note that [address] needs to be aligned to the size of [$nativeType].
121+
external void operator []=(int index, $dartType value);
122+
}
123+
124+
""");
125+
}
126+
127+
void generatePatchExtension(
128+
StringBuffer buffer, String nativeType, String dartType) {
129+
buffer.write("""
130+
extension ${nativeType}Pointer on Pointer<$nativeType> {
131+
@patch
132+
$dartType get value => _load$nativeType(this);
133+
134+
@patch
135+
void set value($dartType value) => _store$nativeType(this, value);
136+
137+
@patch
138+
$dartType operator [](int index) => this.elementAt(index).value;
139+
140+
@patch
141+
void operator []=(int index, $dartType value) => this.elementAt(index).value = value;
142+
}
143+
144+
""");
145+
}
146+
147+
void generateFooter(StringBuffer buffer) {
148+
final footer = """
149+
//
150+
// End of generated code.
151+
//
152+
""";
153+
154+
buffer.write(footer);
155+
}
156+
157+
//
158+
// Helper functions.
159+
//
160+
161+
bool isInt(String type) => type.startsWith("Int") || type.startsWith("Uint");
162+
163+
class Arguments {
164+
final Uri path;
165+
Arguments(this.path);
166+
}
167+
168+
Arguments parseArguments(List<String> arguments) {
169+
final parsedArgs = Map<String, dynamic>();
170+
String flag = null;
171+
for (final String arg in arguments) {
172+
if (flag == "path") {
173+
parsedArgs[flag] = Uri.parse(arg);
174+
flag = null;
175+
} else if (arg == "-p" || arg == "--path") {
176+
flag = "path";
177+
} else {
178+
throw Exception("Unknown argument: $arg");
179+
}
180+
}
181+
182+
Uri path = parsedArgs["path"];
183+
if (path == null) {
184+
path = Platform.script;
185+
print("No path provided, generating files next to generator.");
186+
}
187+
188+
return Arguments(path);
189+
}
190+
191+
Uri dartfmtPath() {
192+
// TODO(dacoharkes): Use "../../../tools/sdks/dart-sdk/bin/dartfmt" when the
193+
// pinned fully supports extension methods.
194+
return Uri.parse("dartfmt");
195+
}

runtime/vm/compiler/frontend/kernel_to_il.cc

+6
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
10741074

10751075
ASSERT(function.NumParameters() == 1);
10761076
body += LoadLocal(parsed_function_->RawParameterVariable(0)); // Pointer.
1077+
body += CheckNullOptimized(TokenPosition::kNoSource,
1078+
String::ZoneHandle(Z, function.name()));
10771079
body += LoadNativeField(Slot::Pointer_c_memory_address());
10781080
body += UnboxTruncate(kUnboxedIntPtr); // Truncating, so signed is ok.
10791081
body += ConvertIntptrToUntagged(); // Requires signed intptr.
@@ -1169,6 +1171,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
11691171
// But we type check it as a method on a generic class at runtime.
11701172
body += LoadLocal(arg_value);
11711173
body += LoadLocal(arg_pointer);
1174+
body += CheckNullOptimized(TokenPosition::kNoSource,
1175+
String::ZoneHandle(Z, function.name()));
11721176
// We pass the Pointer type argument as instantiator_type_args.
11731177
//
11741178
// Call sites to this recognized method are guaranteed to pass a
@@ -1189,6 +1193,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfRecognizedMethod(
11891193

11901194
ASSERT(function.NumParameters() == 2);
11911195
body += LoadLocal(arg_pointer); // Pointer.
1196+
body += CheckNullOptimized(TokenPosition::kNoSource,
1197+
String::ZoneHandle(Z, function.name()));
11921198
body += LoadNativeField(Slot::Pointer_c_memory_address());
11931199
body += UnboxTruncate(kUnboxedIntPtr); // Truncating, so signed is ok.
11941200
body += ConvertIntptrToUntagged(); // Requires signed intptr.

runtime/vm/object.cc

-1
Original file line numberDiff line numberDiff line change
@@ -21494,7 +21494,6 @@ RawDynamicLibrary* DynamicLibrary::New(void* handle, Heap::Space space) {
2149421494
}
2149521495

2149621496
bool Pointer::IsPointer(const Instance& obj) {
21497-
ASSERT(!obj.IsNull());
2149821497
return RawObject::IsFfiPointerClassId(obj.raw()->GetClassId());
2149921498
}
2150021499

0 commit comments

Comments
 (0)