• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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