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 | |
14 | namespace dart { |
15 | |
16 | namespace compiler { |
17 | |
18 | namespace ffi { |
19 | |
20 | bool BaseMarshaller::ContainsHandles() const { |
21 | return dart_signature_.FfiCSignatureContainsHandles(); |
22 | } |
23 | |
24 | Location 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. |
78 | class 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 | |
152 | CallbackMarshaller::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 | |