| 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 | |