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_SIMULATOR_BASE_H_ |
6 | #define V8_SIMULATOR_BASE_H_ |
7 | |
8 | #include <type_traits> |
9 | |
10 | #include "src/globals.h" |
11 | #include "src/isolate.h" |
12 | |
13 | #if defined(USE_SIMULATOR) |
14 | |
15 | namespace v8 { |
16 | namespace internal { |
17 | |
18 | class Instruction; |
19 | class Redirection; |
20 | |
21 | class SimulatorBase { |
22 | public: |
23 | // Call on process start and exit. |
24 | static void InitializeOncePerProcess(); |
25 | static void GlobalTearDown(); |
26 | |
27 | static base::Mutex* redirection_mutex() { return redirection_mutex_; } |
28 | static Redirection* redirection() { return redirection_; } |
29 | static void set_redirection(Redirection* r) { redirection_ = r; } |
30 | |
31 | static base::Mutex* i_cache_mutex() { return i_cache_mutex_; } |
32 | static base::CustomMatcherHashMap* i_cache() { return i_cache_; } |
33 | |
34 | // Runtime call support. |
35 | static Address RedirectExternalReference(Address external_function, |
36 | ExternalReference::Type type); |
37 | |
38 | protected: |
39 | template <typename Return, typename SimT, typename CallImpl, typename... Args> |
40 | static Return VariadicCall(SimT* sim, CallImpl call, Address entry, |
41 | Args... args) { |
42 | // Convert all arguments to intptr_t. Fails if any argument is not integral |
43 | // or pointer. |
44 | std::array<intptr_t, sizeof...(args)> args_arr{{ConvertArg(args)...}}; |
45 | intptr_t ret = (sim->*call)(entry, args_arr.size(), args_arr.data()); |
46 | return ConvertReturn<Return>(ret); |
47 | } |
48 | |
49 | // Convert back integral return types. This is always a narrowing conversion. |
50 | template <typename T> |
51 | static typename std::enable_if<std::is_integral<T>::value, T>::type |
52 | ConvertReturn(intptr_t ret) { |
53 | static_assert(sizeof(T) <= sizeof(intptr_t), "type bigger than ptrsize" ); |
54 | return static_cast<T>(ret); |
55 | } |
56 | |
57 | // Convert back pointer-typed return types. |
58 | template <typename T> |
59 | static typename std::enable_if<std::is_pointer<T>::value, T>::type |
60 | ConvertReturn(intptr_t ret) { |
61 | return reinterpret_cast<T>(ret); |
62 | } |
63 | |
64 | template <typename T> |
65 | static typename std::enable_if<std::is_base_of<Object, T>::value, T>::type |
66 | ConvertReturn(intptr_t ret) { |
67 | return Object(ret); |
68 | } |
69 | |
70 | // Convert back void return type (i.e. no return). |
71 | template <typename T> |
72 | static typename std::enable_if<std::is_void<T>::value, T>::type ConvertReturn( |
73 | intptr_t ret) {} |
74 | |
75 | private: |
76 | static base::Mutex* redirection_mutex_; |
77 | static Redirection* redirection_; |
78 | |
79 | static base::Mutex* i_cache_mutex_; |
80 | static base::CustomMatcherHashMap* i_cache_; |
81 | |
82 | // Helper methods to convert arbitrary integer or pointer arguments to the |
83 | // needed generic argument type intptr_t. |
84 | |
85 | // Convert integral argument to intptr_t. |
86 | template <typename T> |
87 | static typename std::enable_if<std::is_integral<T>::value, intptr_t>::type |
88 | ConvertArg(T arg) { |
89 | static_assert(sizeof(T) <= sizeof(intptr_t), "type bigger than ptrsize" ); |
90 | #if V8_TARGET_ARCH_MIPS64 |
91 | // The MIPS64 calling convention is to sign extend all values, even unsigned |
92 | // ones. |
93 | using signed_t = typename std::make_signed<T>::type; |
94 | return static_cast<intptr_t>(static_cast<signed_t>(arg)); |
95 | #else |
96 | // Standard C++ convertion: Sign-extend signed values, zero-extend unsigned |
97 | // values. |
98 | return static_cast<intptr_t>(arg); |
99 | #endif |
100 | } |
101 | |
102 | // Convert pointer-typed argument to intptr_t. |
103 | template <typename T> |
104 | static typename std::enable_if<std::is_pointer<T>::value, intptr_t>::type |
105 | ConvertArg(T arg) { |
106 | return reinterpret_cast<intptr_t>(arg); |
107 | } |
108 | }; |
109 | |
110 | // When the generated code calls an external reference we need to catch that in |
111 | // the simulator. The external reference will be a function compiled for the |
112 | // host architecture. We need to call that function instead of trying to |
113 | // execute it with the simulator. We do that by redirecting the external |
114 | // reference to a trapping instruction that is handled by the simulator. We |
115 | // write the original destination of the jump just at a known offset from the |
116 | // trapping instruction so the simulator knows what to call. |
117 | // |
118 | // The following are trapping instructions used for various architectures: |
119 | // - V8_TARGET_ARCH_ARM: svc (Supervisor Call) |
120 | // - V8_TARGET_ARCH_ARM64: svc (Supervisor Call) |
121 | // - V8_TARGET_ARCH_MIPS: swi (software-interrupt) |
122 | // - V8_TARGET_ARCH_MIPS64: swi (software-interrupt) |
123 | // - V8_TARGET_ARCH_PPC: svc (Supervisor Call) |
124 | // - V8_TARGET_ARCH_S390: svc (Supervisor Call) |
125 | class Redirection { |
126 | public: |
127 | Redirection(Address external_function, ExternalReference::Type type); |
128 | |
129 | Address address_of_instruction() { |
130 | #if ABI_USES_FUNCTION_DESCRIPTORS |
131 | return reinterpret_cast<Address>(function_descriptor_); |
132 | #else |
133 | return reinterpret_cast<Address>(&instruction_); |
134 | #endif |
135 | } |
136 | |
137 | void* external_function() { |
138 | return reinterpret_cast<void*>(external_function_); |
139 | } |
140 | ExternalReference::Type type() { return type_; } |
141 | |
142 | static Redirection* Get(Address external_function, |
143 | ExternalReference::Type type); |
144 | |
145 | static Redirection* FromInstruction(Instruction* instruction) { |
146 | Address addr_of_instruction = reinterpret_cast<Address>(instruction); |
147 | Address addr_of_redirection = |
148 | addr_of_instruction - offsetof(Redirection, instruction_); |
149 | return reinterpret_cast<Redirection*>(addr_of_redirection); |
150 | } |
151 | |
152 | static void* ReverseRedirection(intptr_t reg) { |
153 | Redirection* redirection = FromInstruction( |
154 | reinterpret_cast<Instruction*>(reinterpret_cast<void*>(reg))); |
155 | return redirection->external_function(); |
156 | } |
157 | |
158 | static void DeleteChain(Redirection* redirection) { |
159 | while (redirection != nullptr) { |
160 | Redirection* next = redirection->next_; |
161 | delete redirection; |
162 | redirection = next; |
163 | } |
164 | } |
165 | |
166 | private: |
167 | Address external_function_; |
168 | uint32_t instruction_; |
169 | ExternalReference::Type type_; |
170 | Redirection* next_; |
171 | #if ABI_USES_FUNCTION_DESCRIPTORS |
172 | intptr_t function_descriptor_[3]; |
173 | #endif |
174 | }; |
175 | |
176 | } // namespace internal |
177 | } // namespace v8 |
178 | |
179 | #endif // defined(USE_SIMULATOR) |
180 | #endif // V8_SIMULATOR_BASE_H_ |
181 | |