1 // Copyright 2017 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_EXECUTION_SIMULATOR_BASE_H_ 6 #define V8_EXECUTION_SIMULATOR_BASE_H_ 7 8 #include <type_traits> 9 10 #ifdef V8_TARGET_ARCH_ARM64 11 #include "include/v8-fast-api-calls.h" 12 #endif // V8_TARGET_ARCH_ARM64 13 #include "src/base/hashmap.h" 14 #include "src/common/globals.h" 15 #include "src/execution/isolate.h" 16 17 #if defined(USE_SIMULATOR) 18 19 namespace v8 { 20 namespace internal { 21 22 class Instruction; 23 class Redirection; 24 25 class SimulatorBase { 26 public: 27 // Call on process start and exit. 28 static void InitializeOncePerProcess(); 29 static void GlobalTearDown(); 30 redirection_mutex()31 static base::Mutex* redirection_mutex() { return redirection_mutex_; } redirection()32 static Redirection* redirection() { return redirection_; } set_redirection(Redirection * r)33 static void set_redirection(Redirection* r) { redirection_ = r; } 34 i_cache_mutex()35 static base::Mutex* i_cache_mutex() { return i_cache_mutex_; } i_cache()36 static base::CustomMatcherHashMap* i_cache() { return i_cache_; } 37 38 // Runtime call support. 39 static Address RedirectExternalReference(Address external_function, 40 ExternalReference::Type type); 41 42 protected: 43 template <typename Return, typename SimT, typename CallImpl, typename... Args> VariadicCall(SimT * sim,CallImpl call,Address entry,Args...args)44 static Return VariadicCall(SimT* sim, CallImpl call, Address entry, 45 Args... args) { 46 // Convert all arguments to intptr_t. Fails if any argument is not integral 47 // or pointer. 48 std::array<intptr_t, sizeof...(args)> args_arr{{ConvertArg(args)...}}; 49 intptr_t ret = (sim->*call)(entry, args_arr.size(), args_arr.data()); 50 return ConvertReturn<Return>(ret); 51 } 52 53 // Convert back integral return types. This is always a narrowing conversion. 54 template <typename T> 55 static typename std::enable_if<std::is_integral<T>::value, T>::type ConvertReturn(intptr_t ret)56 ConvertReturn(intptr_t ret) { 57 static_assert(sizeof(T) <= sizeof(intptr_t), "type bigger than ptrsize"); 58 return static_cast<T>(ret); 59 } 60 61 // Convert back pointer-typed return types. 62 template <typename T> 63 static typename std::enable_if<std::is_pointer<T>::value, T>::type ConvertReturn(intptr_t ret)64 ConvertReturn(intptr_t ret) { 65 return reinterpret_cast<T>(ret); 66 } 67 68 template <typename T> 69 static typename std::enable_if<std::is_base_of<Object, T>::value, T>::type ConvertReturn(intptr_t ret)70 ConvertReturn(intptr_t ret) { 71 return Object(ret); 72 } 73 74 #ifdef V8_TARGET_ARCH_ARM64 75 template <typename T> 76 static typename std::enable_if<std::is_same<T, v8::AnyCType>::value, T>::type ConvertReturn(intptr_t ret)77 ConvertReturn(intptr_t ret) { 78 v8::AnyCType result; 79 result.int64_value = static_cast<int64_t>(ret); 80 return result; 81 } 82 #endif // V8_TARGET_ARCH_ARM64 83 84 // Convert back void return type (i.e. no return). 85 template <typename T> ConvertReturn(intptr_t ret)86 static typename std::enable_if<std::is_void<T>::value, T>::type ConvertReturn( 87 intptr_t ret) {} 88 89 private: 90 static base::Mutex* redirection_mutex_; 91 static Redirection* redirection_; 92 93 static base::Mutex* i_cache_mutex_; 94 static base::CustomMatcherHashMap* i_cache_; 95 96 // Helper methods to convert arbitrary integer or pointer arguments to the 97 // needed generic argument type intptr_t. 98 99 // Convert integral argument to intptr_t. 100 template <typename T> 101 static typename std::enable_if<std::is_integral<T>::value, intptr_t>::type ConvertArg(T arg)102 ConvertArg(T arg) { 103 static_assert(sizeof(T) <= sizeof(intptr_t), "type bigger than ptrsize"); 104 #if V8_TARGET_ARCH_MIPS64 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_LOONG64 105 // The MIPS64, LOONG64 and RISCV64 calling convention is to sign extend all 106 // values, even unsigned ones. 107 using signed_t = typename std::make_signed<T>::type; 108 return static_cast<intptr_t>(static_cast<signed_t>(arg)); 109 #else 110 // Standard C++ convertion: Sign-extend signed values, zero-extend unsigned 111 // values. 112 return static_cast<intptr_t>(arg); 113 #endif 114 } 115 116 // Convert pointer-typed argument to intptr_t. 117 template <typename T> 118 static typename std::enable_if<std::is_pointer<T>::value, intptr_t>::type ConvertArg(T arg)119 ConvertArg(T arg) { 120 return reinterpret_cast<intptr_t>(arg); 121 } 122 123 template <typename T> 124 static 125 typename std::enable_if<std::is_floating_point<T>::value, intptr_t>::type ConvertArg(T arg)126 ConvertArg(T arg) { 127 UNREACHABLE(); 128 } 129 }; 130 131 // When the generated code calls an external reference we need to catch that in 132 // the simulator. The external reference will be a function compiled for the 133 // host architecture. We need to call that function instead of trying to 134 // execute it with the simulator. We do that by redirecting the external 135 // reference to a trapping instruction that is handled by the simulator. We 136 // write the original destination of the jump just at a known offset from the 137 // trapping instruction so the simulator knows what to call. 138 // 139 // The following are trapping instructions used for various architectures: 140 // - V8_TARGET_ARCH_ARM: svc (Supervisor Call) 141 // - V8_TARGET_ARCH_ARM64: svc (Supervisor Call) 142 // - V8_TARGET_ARCH_MIPS: swi (software-interrupt) 143 // - V8_TARGET_ARCH_MIPS64: swi (software-interrupt) 144 // - V8_TARGET_ARCH_PPC: svc (Supervisor Call) 145 // - V8_TARGET_ARCH_PPC64: svc (Supervisor Call) 146 // - V8_TARGET_ARCH_S390: svc (Supervisor Call) 147 // - V8_TARGET_ARCH_RISCV64: ecall (Supervisor Call) 148 class Redirection { 149 public: 150 Redirection(Address external_function, ExternalReference::Type type); 151 address_of_instruction()152 Address address_of_instruction() { 153 #if ABI_USES_FUNCTION_DESCRIPTORS 154 return reinterpret_cast<Address>(function_descriptor_); 155 #else 156 return reinterpret_cast<Address>(&instruction_); 157 #endif 158 } 159 external_function()160 void* external_function() { 161 return reinterpret_cast<void*>(external_function_); 162 } type()163 ExternalReference::Type type() { return type_; } 164 165 static Redirection* Get(Address external_function, 166 ExternalReference::Type type); 167 FromInstruction(Instruction * instruction)168 static Redirection* FromInstruction(Instruction* instruction) { 169 Address addr_of_instruction = reinterpret_cast<Address>(instruction); 170 Address addr_of_redirection = 171 addr_of_instruction - offsetof(Redirection, instruction_); 172 return reinterpret_cast<Redirection*>(addr_of_redirection); 173 } 174 ReverseRedirection(intptr_t reg)175 static void* ReverseRedirection(intptr_t reg) { 176 Redirection* redirection = FromInstruction( 177 reinterpret_cast<Instruction*>(reinterpret_cast<void*>(reg))); 178 return redirection->external_function(); 179 } 180 DeleteChain(Redirection * redirection)181 static void DeleteChain(Redirection* redirection) { 182 while (redirection != nullptr) { 183 Redirection* next = redirection->next_; 184 delete redirection; 185 redirection = next; 186 } 187 } 188 189 private: 190 Address external_function_; 191 uint32_t instruction_; 192 ExternalReference::Type type_; 193 Redirection* next_; 194 #if ABI_USES_FUNCTION_DESCRIPTORS 195 intptr_t function_descriptor_[3]; 196 #endif 197 }; 198 199 class SimulatorData { 200 public: 201 // Calls AddSignatureForTarget for each function and signature, registering 202 // an encoded version of the signature within a mapping maintained by the 203 // simulator (from function address -> encoded signature). The function 204 // is supposed to be called whenever one compiles a fast API function with 205 // possibly multiple overloads. 206 // Note that this function is called from one or more compiler threads, 207 // while the main thread might be reading at the same time from the map, so 208 // both Register* and Get* are guarded with a single mutex. 209 void RegisterFunctionsAndSignatures(Address* c_functions, 210 const CFunctionInfo* const* c_signatures, 211 unsigned num_functions); 212 // The following method is used by the simulator itself to query 213 // whether a signature is registered for the call target and use this 214 // information to address arguments correctly (load them from either GP or 215 // FP registers, or from the stack). 216 const EncodedCSignature& GetSignatureForTarget(Address target); 217 // This method is exposed only for tests, which don't need synchronisation. AddSignatureForTargetForTesting(Address target,const EncodedCSignature & signature)218 void AddSignatureForTargetForTesting(Address target, 219 const EncodedCSignature& signature) { 220 AddSignatureForTarget(target, signature); 221 } 222 223 private: AddSignatureForTarget(Address target,const EncodedCSignature & signature)224 void AddSignatureForTarget(Address target, 225 const EncodedCSignature& signature) { 226 target_to_signature_table_[target] = signature; 227 } 228 229 v8::base::Mutex signature_map_mutex_; 230 typedef std::unordered_map<Address, EncodedCSignature> TargetToSignatureTable; 231 TargetToSignatureTable target_to_signature_table_; 232 }; 233 234 } // namespace internal 235 } // namespace v8 236 237 #endif // defined(USE_SIMULATOR) 238 #endif // V8_EXECUTION_SIMULATOR_BASE_H_ 239