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