Skip to content

Commit da39a4a

Browse files
dcharkescommit-bot@chromium.org
authored andcommitted
[vm/ffi] Remove try-catch from ffi trampoline if no handle scope
https://dart-review.googlesource.com/c/sdk/+/145591 introduced a try catch into FFI calls to call ExitHandleScope on the exception path. However, we only need this try-catch if we actually need to exit the handle scope on the exception path, which is not the case if we have no handles in the signature. So this CL makes the try catch optional. This speeds up ffi calls without handles (tested on JIT x64): FfiCall.Uint8x01(RunTime): 206.4801280066068 us. -> FfiCall.Uint8x01(RunTime): 203.7240782236708 us. Also adds a test that checks that an exception can still be propagated with Dart_PropagateError from native code when the FFI trampoline has no try catch. Change-Id: I9fac7078381c60fb8055b64fff29ea364fbc948f 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-kernel-win-debug-x64-try,vm-kernel-win-debug-ia32-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-msan-linux-release-x64-try,vm-kernel-precomp-msan-linux-release-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-obfuscate-linux-release-x64-try,dart-sdk-linux-try,analyzer-analysis-server-linux-try,analyzer-linux-release-try,front-end-linux-release-x64-try,vm-kernel-precomp-win-release-x64-try,vm-kernel-mac-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-nnbd-linux-debug-x64-try,analyzer-nnbd-linux-release-try,front-end-nnbd-linux-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/151239 Commit-Queue: Daco Harkes <[email protected]> Reviewed-by: Martin Kustermann <[email protected]> Reviewed-by: Alexander Markov <[email protected]>
1 parent 15464a4 commit da39a4a

File tree

8 files changed

+109
-35
lines changed

8 files changed

+109
-35
lines changed

runtime/bin/ffi_test/ffi_test_functions_vmspecific.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,18 @@ DART_EXPORT int64_t HandleReadFieldValue(Dart_Handle handle) {
889889
return value;
890890
}
891891

892+
// Does not have a handle in it's own signature, so does not enter and exit
893+
// scope in the trampoline.
894+
DART_EXPORT int64_t PropagateErrorWithoutHandle(Dart_Handle (*callback)()) {
895+
Dart_EnterScope();
896+
Dart_Handle result = callback();
897+
if (Dart_IsError(result)) {
898+
Dart_PropagateError(result);
899+
}
900+
Dart_ExitScope();
901+
return 0;
902+
}
903+
892904
DART_EXPORT Dart_Handle TrueHandle() {
893905
return Dart_True();
894906
}

runtime/vm/compiler/ffi/marshaller.cc

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,7 @@ namespace compiler {
1818
namespace ffi {
1919

2020
bool BaseMarshaller::ContainsHandles() const {
21-
if (IsHandle(kResultIndex)) {
22-
return true;
23-
}
24-
for (intptr_t i = 0; i < num_args(); i++) {
25-
if (IsHandle(i)) {
26-
return true;
27-
}
28-
}
29-
return false;
21+
return dart_signature_.FfiCSignatureContainsHandles();
3022
}
3123

3224
Location CallMarshaller::LocInFfiCall(intptr_t arg_index) const {

runtime/vm/compiler/frontend/kernel_to_il.cc

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2953,6 +2953,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
29532953

29542954
const auto& marshaller = *new (Z) compiler::ffi::CallMarshaller(Z, function);
29552955

2956+
const bool signature_contains_handles = marshaller.ContainsHandles();
2957+
29562958
BuildArgumentTypeChecks(TypeChecksToBuild::kCheckAllTypeParameterBounds,
29572959
&function_body, &function_body, &function_body);
29582960

@@ -2976,14 +2978,16 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
29762978
function_body += Drop();
29772979
}
29782980

2979-
// Wrap in Try catch to transition from Native to Generated on a throw from
2980-
// the dart_api.
2981-
const intptr_t try_handler_index = AllocateTryIndex();
2982-
Fragment body = TryCatch(try_handler_index);
2983-
++try_depth_;
2984-
2981+
Fragment body;
2982+
intptr_t try_handler_index = -1;
29852983
LocalVariable* api_local_scope = nullptr;
2986-
if (marshaller.ContainsHandles()) {
2984+
if (signature_contains_handles) {
2985+
// Wrap in Try catch to transition from Native to Generated on a throw from
2986+
// the dart_api.
2987+
try_handler_index = AllocateTryIndex();
2988+
body += TryCatch(try_handler_index);
2989+
++try_depth_;
2990+
29872991
body += EnterHandleScope();
29882992
api_local_scope = MakeTemporary();
29892993
}
@@ -3020,29 +3024,34 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
30203024

30213025
body += FfiConvertArgumentToDart(marshaller, compiler::ffi::kResultIndex);
30223026

3023-
if (marshaller.ContainsHandles()) {
3027+
if (signature_contains_handles) {
30243028
body += DropTempsPreserveTop(1); // Drop api_local_scope.
30253029
body += ExitHandleScope();
30263030
}
30273031

30283032
body += Return(TokenPosition::kNoSource);
30293033

3030-
--try_depth_;
3034+
if (signature_contains_handles) {
3035+
--try_depth_;
3036+
}
3037+
30313038
function_body += body;
30323039

3033-
++catch_depth_;
3034-
Fragment catch_body =
3035-
CatchBlockEntry(Array::empty_array(), try_handler_index,
3036-
/*needs_stacktrace=*/true, /*is_synthesized=*/true);
3037-
if (marshaller.ContainsHandles()) {
3040+
if (signature_contains_handles) {
3041+
++catch_depth_;
3042+
Fragment catch_body =
3043+
CatchBlockEntry(Array::empty_array(), try_handler_index,
3044+
/*needs_stacktrace=*/true, /*is_synthesized=*/true);
3045+
30383046
// TODO(41984): If we want to pass in the handle scope, move it out
30393047
// of the try catch.
30403048
catch_body += ExitHandleScope();
3049+
3050+
catch_body += LoadLocal(CurrentException());
3051+
catch_body += LoadLocal(CurrentStackTrace());
3052+
catch_body += RethrowException(TokenPosition::kNoSource, try_handler_index);
3053+
--catch_depth_;
30413054
}
3042-
catch_body += LoadLocal(CurrentException());
3043-
catch_body += LoadLocal(CurrentStackTrace());
3044-
catch_body += RethrowException(TokenPosition::kNoSource, try_handler_index);
3045-
--catch_depth_;
30463055

30473056
return new (Z) FlowGraph(*parsed_function_, graph_entry_, last_used_block_id_,
30483057
prologue_info);

runtime/vm/compiler/frontend/scope_builder.cc

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -420,14 +420,19 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
420420
: Object::dynamic_type().raw()));
421421
scope_->InsertParameterAt(i, variable);
422422
}
423-
current_function_async_marker_ = FunctionNodeHelper::kSync;
424-
++depth_.try_;
425-
AddTryVariables();
426-
--depth_.try_;
427-
++depth_.catch_;
428-
AddCatchVariables();
429-
FinalizeCatchVariables();
430-
--depth_.catch_;
423+
// Callbacks and calls with handles need try/catch variables.
424+
if (function.IsFfiTrampoline() &&
425+
(function.FfiCallbackTarget() != Function::null() ||
426+
function.FfiCSignatureContainsHandles())) {
427+
current_function_async_marker_ = FunctionNodeHelper::kSync;
428+
++depth_.try_;
429+
AddTryVariables();
430+
--depth_.try_;
431+
++depth_.catch_;
432+
AddCatchVariables();
433+
FinalizeCatchVariables();
434+
--depth_.catch_;
435+
}
431436
break;
432437
case FunctionLayout::kSignatureFunction:
433438
case FunctionLayout::kIrregexpFunction:

runtime/vm/object.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6939,6 +6939,22 @@ FunctionPtr Function::FfiCSignature() const {
69396939
return FfiTrampolineData::Cast(obj).c_signature();
69406940
}
69416941

6942+
bool Function::FfiCSignatureContainsHandles() const {
6943+
ASSERT(IsFfiTrampoline());
6944+
const Function& c_signature = Function::Handle(FfiCSignature());
6945+
const intptr_t num_params = c_signature.num_fixed_parameters();
6946+
for (intptr_t i = 0; i < num_params; i++) {
6947+
const bool is_handle =
6948+
AbstractType::Handle(c_signature.ParameterTypeAt(i)).type_class_id() ==
6949+
kFfiHandleCid;
6950+
if (is_handle) {
6951+
return true;
6952+
}
6953+
}
6954+
return AbstractType::Handle(c_signature.result_type()).type_class_id() ==
6955+
kFfiHandleCid;
6956+
}
6957+
69426958
int32_t Function::FfiCallbackId() const {
69436959
ASSERT(IsFfiTrampoline());
69446960
const Object& obj = Object::Handle(raw_ptr()->data_);

runtime/vm/object.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,6 +2504,8 @@ class Function : public Object {
25042504
// Can only be used on FFI trampolines.
25052505
FunctionPtr FfiCSignature() const;
25062506

2507+
bool FfiCSignatureContainsHandles() const;
2508+
25072509
// Can only be called on FFI trampolines.
25082510
// -1 for Dart -> native calls.
25092511
int32_t FfiCallbackId() const;

tests/ffi/vmspecific_handle_test.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ void main() {
2323
testDeepException2();
2424
testNull();
2525
testDeepRecursive();
26+
testNoHandlePropagateError();
2627
}
2728

2829
void testHandle() {
@@ -220,6 +221,19 @@ void testDeepRecursive() {
220221
Expect.isTrue(identical(someObject, result));
221222
}
222223

224+
void testNoHandlePropagateError() {
225+
bool throws = false;
226+
try {
227+
final result = propagateErrorWithoutHandle(exceptionHandleCallbackPointer);
228+
print(result);
229+
} catch (e) {
230+
throws = true;
231+
print("caught ($e)");
232+
Expect.isTrue(identical(someException, e));
233+
}
234+
Expect.isTrue(throws);
235+
}
236+
223237
final testLibrary = dlopenPlatformSpecific("ffi_test_functions");
224238

225239
final passObjectToC = testLibrary.lookupFunction<Handle Function(Handle),
@@ -247,3 +261,8 @@ final handleRecursion = testLibrary.lookupFunction<
247261
Handle, Pointer<NativeFunction<Handle Function(Int64)>>, Int64),
248262
Object Function(Object, Pointer<NativeFunction<Handle Function(Int64)>>,
249263
int)>("HandleRecursion");
264+
265+
final propagateErrorWithoutHandle = testLibrary.lookupFunction<
266+
Int64 Function(Pointer<NativeFunction<Handle Function()>>),
267+
int Function(Pointer<NativeFunction<Handle Function()>>)>(
268+
"PropagateErrorWithoutHandle");

tests/ffi_2/vmspecific_handle_test.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ void main() {
2323
testDeepException2();
2424
testNull();
2525
testDeepRecursive();
26+
testNoHandlePropagateError();
2627
}
2728

2829
void testHandle() {
@@ -220,6 +221,19 @@ void testDeepRecursive() {
220221
Expect.isTrue(identical(someObject, result));
221222
}
222223

224+
void testNoHandlePropagateError() {
225+
bool throws = false;
226+
try {
227+
final result = propagateErrorWithoutHandle(exceptionHandleCallbackPointer);
228+
print(result);
229+
} catch (e) {
230+
throws = true;
231+
print("caught ($e)");
232+
Expect.isTrue(identical(someException, e));
233+
}
234+
Expect.isTrue(throws);
235+
}
236+
223237
final testLibrary = dlopenPlatformSpecific("ffi_test_functions");
224238

225239
final passObjectToC = testLibrary.lookupFunction<Handle Function(Handle),
@@ -247,3 +261,8 @@ final handleRecursion = testLibrary.lookupFunction<
247261
Handle, Pointer<NativeFunction<Handle Function(Int64)>>, Int64),
248262
Object Function(Object, Pointer<NativeFunction<Handle Function(Int64)>>,
249263
int)>("HandleRecursion");
264+
265+
final propagateErrorWithoutHandle = testLibrary.lookupFunction<
266+
Int64 Function(Pointer<NativeFunction<Handle Function()>>),
267+
int Function(Pointer<NativeFunction<Handle Function()>>)>(
268+
"PropagateErrorWithoutHandle");

0 commit comments

Comments
 (0)