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