CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/CodeBlock.h
Views: 1401
1
// Copyright 2013 Dolphin Emulator Project
2
// Licensed under GPLv2
3
// Refer to the license.txt file included.
4
5
#pragma once
6
7
#include <cstddef>
8
9
#include "Common/CommonTypes.h"
10
#include "Common/Log.h"
11
#include "Common/MemoryUtil.h"
12
13
#if PPSSPP_PLATFORM(SWITCH)
14
#include <cstdio>
15
#include <switch.h>
16
#endif // PPSSPP_PLATFORM(SWITCH)
17
18
// Everything that needs to generate code should inherit from this.
19
// You get memory management for free, plus, you can use all emitter functions without
20
// having to prefix them with gen-> or something similar.
21
// Example implementation:
22
// class JIT : public CodeBlock<ARMXEmitter>, public JitInterface {}
23
24
class CodeBlockCommon {
25
public:
26
CodeBlockCommon() {}
27
virtual ~CodeBlockCommon() {}
28
29
bool IsInSpace(const u8 *ptr) const {
30
return (ptr >= region) && (ptr < (region + region_size));
31
}
32
33
virtual const u8 *GetCodePtr() const = 0;
34
35
u8 *GetBasePtr() const {
36
return region;
37
}
38
39
size_t GetOffset(const u8 *ptr) const {
40
return ptr - region;
41
}
42
43
virtual const u8 *GetCodePtrFromWritablePtr(u8 *ptr) = 0;
44
virtual u8 *GetWritablePtrFromCodePtr(const u8 *ptr) = 0;
45
46
protected:
47
virtual void SetCodePtr(u8 *ptr) = 0;
48
49
// Note: this should be the readable/executable side if writable is a different pointer.
50
u8 *region = nullptr;
51
size_t region_size = 0;
52
};
53
54
template<class T> class CodeBlock : public CodeBlockCommon, public T {
55
private:
56
CodeBlock(const CodeBlock &) = delete;
57
void operator=(const CodeBlock &) = delete;
58
59
// A privately used function to set the executable RAM space to something invalid.
60
// For debugging usefulness it should be used to set the RAM to a host specific breakpoint instruction
61
virtual void PoisonMemory(int offset) = 0;
62
63
public:
64
CodeBlock() {}
65
~CodeBlock() {
66
if (region)
67
FreeCodeSpace();
68
}
69
70
// Call this before you generate any code.
71
void AllocCodeSpace(int size) {
72
region_size = size;
73
#if PPSSPP_PLATFORM(SWITCH)
74
Result rc = jitCreate(&jitController, size);
75
if(R_FAILED(rc)) {
76
printf("Failed to create Jitbuffer of size 0x%x err: 0x%x\n", size, rc);
77
}
78
printf("[NXJIT]: Initialized RX: %p RW: %p\n", jitController.rx_addr, jitController.rw_addr);
79
80
region = (u8 *)jitController.rx_addr;
81
writableRegion = (u8 *)jitController.rw_addr;
82
#else // PPSSPP_PLATFORM(SWITCH)
83
// The protection will be set to RW if PlatformIsWXExclusive.
84
region = (u8 *)AllocateExecutableMemory(region_size);
85
writableRegion = region;
86
#endif // !PPSSPP_PLATFORM(SWITCH)
87
T::SetCodePointer(region, writableRegion);
88
}
89
90
// Always clear code space with breakpoints, so that if someone accidentally executes
91
// uninitialized, it just breaks into the debugger.
92
void ClearCodeSpace(int offset) {
93
if (!region) {
94
return;
95
}
96
if (PlatformIsWXExclusive()) {
97
ProtectMemoryPages(region, region_size, MEM_PROT_READ | MEM_PROT_WRITE);
98
}
99
// If not WX Exclusive, no need to call ProtectMemoryPages because we never change the protection from RWX.
100
PoisonMemory(offset);
101
ResetCodePtr(offset);
102
if (PlatformIsWXExclusive() && offset != 0) {
103
// Need to re-protect the part we didn't clear.
104
ProtectMemoryPages(region, offset, MEM_PROT_READ | MEM_PROT_EXEC);
105
}
106
}
107
108
// BeginWrite/EndWrite assume that we keep appending.
109
// If you don't specify a size and we later encounter an executable non-writable block, we're screwed.
110
// These CANNOT be nested. We rely on the memory protection starting at READ|WRITE after start and reset.
111
void BeginWrite(size_t sizeEstimate = 1) {
112
_dbg_assert_msg_(!writeStart_, "Can't nest BeginWrite calls");
113
114
// In case the last block made the current page exec/no-write, let's fix that.
115
if (PlatformIsWXExclusive()) {
116
writeStart_ = GetCodePtr();
117
if (writeStart_ + sizeEstimate - region > (ptrdiff_t)region_size)
118
sizeEstimate = region_size - (writeStart_ - region);
119
writeEstimated_ = sizeEstimate;
120
ProtectMemoryPages(writeStart_, sizeEstimate, MEM_PROT_READ | MEM_PROT_WRITE);
121
}
122
}
123
124
// In case you now know your original estimate is wrong.
125
void ContinueWrite(size_t sizeEstimate = 1) {
126
_dbg_assert_msg_(writeStart_, "Must have already called BeginWrite()");
127
if (PlatformIsWXExclusive()) {
128
const uint8_t *pos = GetCodePtr();
129
if (pos + sizeEstimate - region > (ptrdiff_t)region_size)
130
sizeEstimate = region_size - (pos - region);
131
writeEstimated_ = pos - writeStart_ + sizeEstimate;
132
ProtectMemoryPages(pos, sizeEstimate, MEM_PROT_READ | MEM_PROT_WRITE);
133
}
134
}
135
136
void EndWrite() {
137
// OK, we're done. Re-protect the memory we touched.
138
if (PlatformIsWXExclusive() && writeStart_ != nullptr) {
139
const uint8_t *end = GetCodePtr();
140
size_t sz = end - writeStart_;
141
if (sz > writeEstimated_)
142
WARN_LOG(Log::JIT, "EndWrite(): Estimated %d bytes, wrote %d", (int)writeEstimated_, (int)sz);
143
// If we protected and wrote less, we may need to unprotect.
144
// Especially if we're linking blocks or similar.
145
if (sz < writeEstimated_)
146
sz = writeEstimated_;
147
ProtectMemoryPages(writeStart_, sz, MEM_PROT_READ | MEM_PROT_EXEC);
148
writeStart_ = nullptr;
149
}
150
}
151
152
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
153
void FreeCodeSpace() {
154
#if !PPSSPP_PLATFORM(SWITCH)
155
ProtectMemoryPages(region, region_size, MEM_PROT_READ | MEM_PROT_WRITE);
156
FreeExecutableMemory(region, region_size);
157
#else // !PPSSPP_PLATFORM(SWITCH)
158
jitClose(&jitController);
159
printf("[NXJIT]: Jit closed\n");
160
#endif // PPSSPP_PLATFORM(SWITCH)
161
region = nullptr;
162
writableRegion = nullptr;
163
region_size = 0;
164
}
165
166
const u8 *GetCodePtr() const override {
167
return T::GetCodePointer();
168
}
169
170
void ResetCodePtr(size_t offset) {
171
T::SetCodePointer(region + offset, writableRegion + offset);
172
}
173
174
size_t GetSpaceLeft() const {
175
return region_size - (T::GetCodePointer() - region);
176
}
177
178
const u8 *GetCodePtrFromWritablePtr(u8 *ptr) override {
179
// So we can adjust region to writable space. Might be zero.
180
ptrdiff_t writable = T::GetWritableCodePtr() - T::GetCodePointer();
181
return ptr - writable;
182
}
183
184
u8 *GetWritablePtrFromCodePtr(const u8 *ptr) override {
185
// So we can adjust region to writable space. Might be zero.
186
ptrdiff_t writable = T::GetWritableCodePtr() - T::GetCodePointer();
187
return (u8 *)ptr + writable;
188
}
189
190
protected:
191
void SetCodePtr(u8 *ptr) override {
192
T::SetCodePointer(ptr, GetWritablePtrFromCodePtr(ptr));
193
}
194
195
private:
196
// Note: this is a readable pointer.
197
const uint8_t *writeStart_ = nullptr;
198
uint8_t *writableRegion = nullptr;
199
size_t writeEstimated_ = 0;
200
#if PPSSPP_PLATFORM(SWITCH)
201
Jit jitController;
202
#endif // PPSSPP_PLATFORM(SWITCH)
203
};
204
205