1// Copyright (c) 2020, 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#include "vm/compiler/ffi/marshaller.h"
6
7#include "vm/compiler/ffi/frame_rebase.h"
8#include "vm/compiler/ffi/native_location.h"
9#include "vm/compiler/ffi/native_type.h"
10#include "vm/raw_object.h"
11#include "vm/stack_frame.h"
12#include "vm/symbols.h"
13
14namespace dart {
15
16namespace compiler {
17
18namespace ffi {
19
20bool BaseMarshaller::ContainsHandles() const {
21 return dart_signature_.FfiCSignatureContainsHandles();
22}
23
24Location CallMarshaller::LocInFfiCall(intptr_t arg_index) const {
25 if (arg_index == kResultIndex) {
26 return Location(arg_index).AsLocation();
27 }
28
29 // Floating point values are never split: they are either in a single "FPU"
30 // register or a contiguous 64-bit slot on the stack. Unboxed 64-bit integer
31 // values, in contrast, can be split between any two registers on a 32-bit
32 // system.
33 //
34 // There is an exception for iOS and Android 32-bit ARM, where
35 // floating-point values are treated as integers as far as the calling
36 // convention is concerned. However, the representation of these arguments
37 // are set to kUnboxedInt32 or kUnboxedInt64 already, so we don't have to
38 // account for that here.
39 const bool is_atomic = RepInFfiCall(arg_index) == kUnboxedDouble ||
40 RepInFfiCall(arg_index) == kUnboxedFloat;
41
42 const NativeLocation& loc = this->Location(arg_index);
43 // Don't pin stack locations, they need to be moved anyway.
44 if (loc.IsStack()) {
45 if (loc.payload_type().SizeInBytes() == 2 * compiler::target::kWordSize &&
46 !is_atomic) {
47 return Location::Pair(Location::Any(), Location::Any());
48 }
49 return Location::Any();
50 }
51
52#if defined(TARGET_ARCH_ARM)
53 // Only pin FPU register if it is the lowest bits.
54 if (loc.IsFpuRegisters()) {
55 const auto& fpu_loc = loc.AsFpuRegisters();
56 if (fpu_loc.IsLowestBits()) {
57 return fpu_loc.WidenToQFpuRegister(zone_).AsLocation();
58 }
59 return Location::Any();
60 }
61#endif // defined(TARGET_ARCH_ARM)
62
63 return loc.AsLocation();
64}
65
66// This classes translates the ABI location of arguments into the locations they
67// will inhabit after entry-frame setup in the invocation of a native callback.
68//
69// Native -> Dart callbacks must push all the arguments before executing any
70// Dart code because the reading the Thread from TLS requires calling a native
71// stub, and the argument registers are volatile on all ABIs we support.
72//
73// To avoid complicating initial definitions, all callback arguments are read
74// off the stack from their pushed locations, so this class updates the argument
75// positions to account for this.
76//
77// See 'NativeEntryInstr::EmitNativeCode' for details.
78class CallbackArgumentTranslator : public ValueObject {
79 public:
80 static NativeLocations& TranslateArgumentLocations(
81 const NativeLocations& arg_locs,
82 Zone* zone) {
83 auto& pushed_locs = *(new NativeLocations(arg_locs.length()));
84
85 CallbackArgumentTranslator translator;
86 for (intptr_t i = 0, n = arg_locs.length(); i < n; i++) {
87 translator.AllocateArgument(*arg_locs[i]);
88 }
89 for (intptr_t i = 0, n = arg_locs.length(); i < n; ++i) {
90 pushed_locs.Add(&translator.TranslateArgument(*arg_locs[i], zone));
91 }
92
93 return pushed_locs;
94 }
95
96 private:
97 void AllocateArgument(const NativeLocation& arg) {
98 if (arg.IsStack()) return;
99
100 ASSERT(arg.IsRegisters() || arg.IsFpuRegisters());
101 if (arg.IsRegisters()) {
102 argument_slots_required_ += arg.AsRegisters().num_regs();
103 } else {
104 argument_slots_required_ += 8 / target::kWordSize;
105 }
106 }
107
108 const NativeLocation& TranslateArgument(const NativeLocation& arg,
109 Zone* zone) {
110 if (arg.IsStack()) {
111 // Add extra slots after the saved arguments for the return address and
112 // frame pointer of the dummy arguments frame, which will be between the
113 // saved argument registers and stack arguments. Also add slots for the
114 // shadow space if present (factored into
115 // kCallbackSlotsBeforeSavedArguments).
116 //
117 // Finally, if we are using NativeCallbackTrampolines, factor in the extra
118 // stack space corresponding to those trampolines' frames (above the entry
119 // frame).
120 intptr_t stack_delta = kCallbackSlotsBeforeSavedArguments;
121 if (NativeCallbackTrampolines::Enabled()) {
122 stack_delta += StubCodeCompiler::kNativeCallbackTrampolineStackDelta;
123 }
124 FrameRebase rebase(
125 /*old_base=*/SPREG, /*new_base=*/SPREG,
126 /*stack_delta=*/(argument_slots_required_ + stack_delta) *
127 compiler::target::kWordSize,
128 zone);
129 return rebase.Rebase(arg);
130 }
131
132 if (arg.IsRegisters()) {
133 const auto& result = *new (zone) NativeStackLocation(
134 arg.payload_type(), arg.container_type(), SPREG,
135 argument_slots_used_ * compiler::target::kWordSize);
136 argument_slots_used_ += arg.AsRegisters().num_regs();
137 return result;
138 }
139
140 ASSERT(arg.IsFpuRegisters());
141 const auto& result = *new (zone) NativeStackLocation(
142 arg.payload_type(), arg.container_type(), SPREG,
143 argument_slots_used_ * compiler::target::kWordSize);
144 argument_slots_used_ += 8 / target::kWordSize;
145 return result;
146 }
147
148 intptr_t argument_slots_used_ = 0;
149 intptr_t argument_slots_required_ = 0;
150};
151
152CallbackMarshaller::CallbackMarshaller(Zone* zone,
153 const Function& dart_signature)
154 : BaseMarshaller(zone, dart_signature),
155 callback_locs_(
156 CallbackArgumentTranslator::TranslateArgumentLocations(arg_locs_,
157 zone_)) {}
158
159} // namespace ffi
160
161} // namespace compiler
162
163} // namespace dart
164