Skip to content

Commit 4258a59

Browse files
Clement SkauCommit Bot
Clement Skau
authored and
Commit Bot
committed
[vm][ffi] Passes FFI handles on stack.
Passing handles in FFI calls has significant overhead due to how each handle requires a runtime entry to allocate in the handle scope. This change removes that runtime entry by relying on the register allocator to allocate all handle arguments on the stack, so that we don't need to allocate them separately. To pass the stack handles to the native call we then pass a pointer to the stack slot as the native argument. Testing: - We already have comprehensive tests for correctness in the form of the FFI tests. These make calls with various combinations of Handle and non-handle arguments. - Correct GC behaviour is likewise covered in `vmspecific_handle_test.dart` which makes calls with handles arguments and triggers GC in-flight. In case we do not correctly pass the handles on the stack, the GC will trash them during the call. TEST=Existing. Bug: #47624 Change-Id: Ic837bad5484daaa5534b7c2e8707ac2c5dfa480f Cq-Include-Trybots: luci.dart.try:vm-kernel-gcc-linux-try,vm-kernel-linux-debug-simriscv64-try,vm-kernel-nnbd-mac-release-arm64-try,vm-kernel-precomp-android-release-arm64c-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-dwarf-linux-product-x64-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-linux-debug-simriscv64-try,vm-precomp-ffi-qemu-linux-release-arm-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243320 Reviewed-by: Slava Egorov <[email protected]> Commit-Queue: Clement Skau <[email protected]>
1 parent 112cba1 commit 4258a59

File tree

7 files changed

+96
-27
lines changed

7 files changed

+96
-27
lines changed

runtime/vm/compiler/backend/il.cc

+27
Original file line numberDiff line numberDiff line change
@@ -6506,6 +6506,11 @@ void BitCastInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
65066506

65076507
Representation FfiCallInstr::RequiredInputRepresentation(intptr_t idx) const {
65086508
if (idx < TargetAddressIndex()) {
6509+
// All input handles are passed as Tagged values on the stack to
6510+
// FfiCallInstr, which passes "handles", i.e. pointers, to these.
6511+
if (marshaller_.IsHandle(marshaller_.ArgumentIndex(idx))) {
6512+
return kTagged;
6513+
}
65096514
return marshaller_.RepInFfiCall(idx);
65106515
} else if (idx == TargetAddressIndex()) {
65116516
return kUnboxedFfiIntPtr;
@@ -6621,6 +6626,28 @@ void FfiCallInstr::EmitParamMoves(FlowGraphCompiler* compiler,
66216626
// http://dartbug.com/45055), which means all incomming arguments
66226627
// originate from parameters and thus are non-constant.
66236628
UNREACHABLE();
6629+
}
6630+
6631+
// Handles are passed into FfiCalls as Tagged values on the stack, and
6632+
// then we pass pointers to these handles to the native function here.
6633+
if (marshaller_.IsHandle(arg_index)) {
6634+
ASSERT(compiler::target::LocalHandle::ptr_offset() == 0);
6635+
ASSERT(compiler::target::LocalHandle::InstanceSize() ==
6636+
compiler::target::kWordSize);
6637+
ASSERT(num_defs == 1);
6638+
ASSERT(origin.IsStackSlot());
6639+
if (def_target.IsRegisters()) {
6640+
__ AddImmediate(def_target.AsLocation().reg(), origin.base_reg(),
6641+
origin.stack_index() * compiler::target::kWordSize);
6642+
} else {
6643+
ASSERT(def_target.IsStack());
6644+
const auto& target_stack = def_target.AsStack();
6645+
__ AddImmediate(temp0, origin.base_reg(),
6646+
origin.stack_index() * compiler::target::kWordSize);
6647+
__ StoreToOffset(temp0,
6648+
compiler::Address(target_stack.base_register(),
6649+
target_stack.offset_in_bytes()));
6650+
}
66246651
} else {
66256652
#if defined(INCLUDE_IL_PRINTER)
66266653
__ Comment("def_target %s <- origin %s %s", def_target.ToCString(),

runtime/vm/compiler/ffi/marshaller.cc

+9
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,15 @@ Location CallMarshaller::LocInFfiCall(intptr_t def_index_global) const {
389389
return loc.AsLocation();
390390
}
391391

392+
// Force all handles to be Stack locations.
393+
// Since non-leaf calls block all registers, Any locations effectively mean
394+
// Stack.
395+
// TODO(dartbug.com/38985): Once we start inlining FFI trampolines, the inputs
396+
// can be constants as well.
397+
if (IsHandle(arg_index)) {
398+
return Location::Any();
399+
}
400+
392401
if (loc.IsMultiple()) {
393402
const intptr_t def_index_in_arg =
394403
def_index_global - FirstDefinitionIndex(arg_index);

runtime/vm/compiler/frontend/kernel_to_il.cc

+25-23
Original file line numberDiff line numberDiff line change
@@ -4007,15 +4007,10 @@ Fragment FlowGraphBuilder::ExitHandleScope() {
40074007
return Fragment(instr);
40084008
}
40094009

4010-
Fragment FlowGraphBuilder::AllocateHandle(LocalVariable* api_local_scope) {
4010+
Fragment FlowGraphBuilder::AllocateHandle() {
40114011
Fragment code;
4012-
if (api_local_scope != nullptr) {
4013-
// Use the reference the scope we created in the trampoline.
4014-
code += LoadLocal(api_local_scope);
4015-
} else {
4016-
// Or get a reference to the top handle scope.
4017-
code += GetTopHandleScope();
4018-
}
4012+
// Get a reference to the top handle scope.
4013+
code += GetTopHandleScope();
40194014
Value* api_local_scope_value = Pop();
40204015
auto* instr = new (Z) AllocateHandleInstr(api_local_scope_value);
40214016
Push(instr);
@@ -4039,10 +4034,10 @@ Fragment FlowGraphBuilder::RawStoreField(int32_t offset) {
40394034
return code;
40404035
}
40414036

4042-
Fragment FlowGraphBuilder::WrapHandle(LocalVariable* api_local_scope) {
4037+
Fragment FlowGraphBuilder::WrapHandle() {
40434038
Fragment code;
40444039
LocalVariable* object = MakeTemporary();
4045-
code += AllocateHandle(api_local_scope);
4040+
code += AllocateHandle();
40464041

40474042
code += LoadLocal(MakeTemporary()); // Duplicate handle pointer.
40484043
code += ConvertUnboxedToUntagged(kUnboxedIntPtr);
@@ -4528,8 +4523,7 @@ Fragment FlowGraphBuilder::FfiConvertPrimitiveToDart(
45284523

45294524
Fragment FlowGraphBuilder::FfiConvertPrimitiveToNative(
45304525
const compiler::ffi::BaseMarshaller& marshaller,
4531-
intptr_t arg_index,
4532-
LocalVariable* api_local_scope) {
4526+
intptr_t arg_index) {
45334527
ASSERT(!marshaller.IsCompound(arg_index));
45344528

45354529
Fragment body;
@@ -4538,7 +4532,7 @@ Fragment FlowGraphBuilder::FfiConvertPrimitiveToNative(
45384532
body += LoadUntagged(compiler::target::PointerBase::data_offset());
45394533
body += ConvertUntaggedToUnboxed(kUnboxedFfiIntPtr);
45404534
} else if (marshaller.IsHandle(arg_index)) {
4541-
body += WrapHandle(api_local_scope);
4535+
body += WrapHandle();
45424536
} else {
45434537
if (marshaller.IsBool(arg_index)) {
45444538
body += BoolToInt();
@@ -4621,16 +4615,17 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
46214615

46224616
Fragment body;
46234617
intptr_t try_handler_index = -1;
4624-
LocalVariable* api_local_scope = nullptr;
46254618
if (signature_contains_handles) {
46264619
// Wrap in Try catch to transition from Native to Generated on a throw from
46274620
// the dart_api.
46284621
try_handler_index = AllocateTryIndex();
46294622
body += TryCatch(try_handler_index);
46304623
++try_depth_;
4631-
4624+
// TODO(dartbug.com/48989): Remove scope for calls where we don't actually
4625+
// need it.
4626+
// We no longer need the scope for passing in Handle arguments, but the
4627+
// native function might for instance be relying on this scope for Dart API.
46324628
body += EnterHandleScope();
4633-
api_local_scope = MakeTemporary("api_local_scope");
46344629
}
46354630

46364631
// Allocate typed data before FfiCall and pass it in to ffi call if needed.
@@ -4652,7 +4647,12 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
46524647
} else {
46534648
body += LoadLocal(parsed_function_->ParameterVariable(
46544649
kFirstArgumentParameterOffset + i));
4655-
body += FfiConvertPrimitiveToNative(marshaller, i, api_local_scope);
4650+
// FfiCallInstr specifies all handle locations as Stack, and will pass a
4651+
// pointer to the stack slot as the native handle argument.
4652+
// Therefore we do not need to wrap handles.
4653+
if (!marshaller.IsHandle(i)) {
4654+
body += FfiConvertPrimitiveToNative(marshaller, i);
4655+
}
46564656
}
46574657
}
46584658

@@ -4703,6 +4703,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
47034703
}
47044704

47054705
if (signature_contains_handles) {
4706+
// TODO(dartbug.com/48989): Remove scope for calls where we don't actually
4707+
// need it.
47064708
body += DropTempsPreserveTop(1); // Drop api_local_scope.
47074709
body += ExitHandleScope();
47084710
}
@@ -4721,6 +4723,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiNative(const Function& function) {
47214723
CatchBlockEntry(Array::empty_array(), try_handler_index,
47224724
/*needs_stacktrace=*/true, /*is_synthesized=*/true);
47234725

4726+
// TODO(dartbug.com/48989): Remove scope for calls where we don't actually
4727+
// need it.
47244728
// TODO(41984): If we want to pass in the handle scope, move it out
47254729
// of the try catch.
47264730
catch_body += ExitHandleScope();
@@ -4805,8 +4809,8 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCallback(const Function& function) {
48054809
body += FfiCallbackConvertCompoundReturnToNative(
48064810
marshaller, compiler::ffi::kResultIndex);
48074811
} else {
4808-
body += FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex,
4809-
/*api_local_scope=*/nullptr);
4812+
body +=
4813+
FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex);
48104814
}
48114815

48124816
body += NativeReturn(marshaller);
@@ -4830,8 +4834,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCallback(const Function& function) {
48304834
} else if (marshaller.IsHandle(compiler::ffi::kResultIndex)) {
48314835
catch_body += UnhandledException();
48324836
catch_body +=
4833-
FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex,
4834-
/*api_local_scope=*/nullptr);
4837+
FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex);
48354838

48364839
} else if (marshaller.IsCompound(compiler::ffi::kResultIndex)) {
48374840
ASSERT(function.FfiCallbackExceptionalReturn() == Object::null());
@@ -4853,8 +4856,7 @@ FlowGraph* FlowGraphBuilder::BuildGraphOfFfiCallback(const Function& function) {
48534856
catch_body += Constant(
48544857
Instance::ZoneHandle(Z, function.FfiCallbackExceptionalReturn()));
48554858
catch_body +=
4856-
FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex,
4857-
/*api_local_scope=*/nullptr);
4859+
FfiConvertPrimitiveToNative(marshaller, compiler::ffi::kResultIndex);
48584860
}
48594861

48604862
catch_body += NativeReturn(marshaller);

runtime/vm/compiler/frontend/kernel_to_il.h

+3-4
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,7 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
305305
// Works for FFI call arguments, and FFI callback return values.
306306
Fragment FfiConvertPrimitiveToNative(
307307
const compiler::ffi::BaseMarshaller& marshaller,
308-
intptr_t arg_index,
309-
LocalVariable* api_local_scope);
308+
intptr_t arg_index);
310309

311310
// Pops an unboxed native value, and pushes a Dart object, according to the
312311
// semantics of FFI argument translation.
@@ -388,7 +387,7 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
388387
Fragment ExitHandleScope();
389388

390389
// Leaves a `LocalHandle` on the stack.
391-
Fragment AllocateHandle(LocalVariable* api_local_scope);
390+
Fragment AllocateHandle();
392391

393392
// Loads a tagged value from an untagged base + offset from outside the heap.
394393
Fragment RawLoadField(int32_t offset);
@@ -399,7 +398,7 @@ class FlowGraphBuilder : public BaseFlowGraphBuilder {
399398
Fragment RawStoreField(int32_t offset);
400399

401400
// Wraps an `Object` from the stack and leaves a `LocalHandle` on the stack.
402-
Fragment WrapHandle(LocalVariable* api_local_scope);
401+
Fragment WrapHandle();
403402

404403
// Unwraps a `LocalHandle` from the stack and leaves the object on the stack.
405404
Fragment UnwrapHandle();

runtime/vm/compiler/runtime_api.h

+1
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,7 @@ class ArgumentsDescriptor : public AllStatic {
667667
class LocalHandle : public AllStatic {
668668
public:
669669
static word ptr_offset();
670+
static word InstanceSize();
670671
};
671672

672673
class Pointer : public AllStatic {

0 commit comments

Comments
 (0)