1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecmascript/mem/machine_code.h"
17 #include "ecmascript/compiler/aot_file/func_entry_des.h"
18 #include "ecmascript/jit/jit.h"
19 #include "ecmascript/jit/rewriter/reloc_rewriter_aarch64.h"
20 #include "ecmascript/jit/rewriter/reloc_rewriter.h"
21 #if ECMASCRIPT_ENABLE_CAST_CHECK
22 #include "ecmascript/js_tagged_value-inl.h"
23 #endif
24
25 namespace panda::ecmascript {
26
SetPageProtect(uint8_t * textStart,size_t dataSize)27 static bool SetPageProtect(uint8_t *textStart, size_t dataSize)
28 {
29 if (!Jit::GetInstance()->IsEnableJitFort()) {
30 constexpr size_t pageSize = 4096;
31 uintptr_t startPage = AlignDown(reinterpret_cast<uintptr_t>(textStart), pageSize);
32 uintptr_t endPage = AlignUp(reinterpret_cast<uintptr_t>(textStart) + dataSize, pageSize);
33 size_t protSize = endPage - startPage;
34 return PageProtect(reinterpret_cast<void*>(startPage), protSize, PAGE_PROT_EXEC_READWRITE);
35 }
36 return true;
37 }
38
MachineCodeCopyToCache(const MachineCodeDesc & desc,uint8_t * pText)39 static int MachineCodeCopyToCache([[maybe_unused]] const MachineCodeDesc &desc, [[maybe_unused]] uint8_t *pText)
40 {
41 #ifndef JIT_ENABLE_CODE_SIGN
42 if (memcpy_s(pText, desc.codeSizeAlign, // LCOV_EXCL_BR_LINE
43 reinterpret_cast<uint8_t*>(desc.codeAddr),
44 desc.codeSize) != EOK) {
45 LOG_JIT(ERROR) << "memcpy failed in CopyToCache";
46 return false;
47 }
48 #endif
49 return true;
50 }
51
SetText(const MachineCodeDesc & desc)52 bool MachineCode::SetText(const MachineCodeDesc &desc)
53 {
54 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
55 uint8_t *pText = textStart;
56 if (desc.rodataSizeBeforeTextAlign != 0) {
57 if (memcpy_s(pText, desc.rodataSizeBeforeTextAlign, // LCOV_EXCL_BR_LINE
58 reinterpret_cast<uint8_t*>(desc.rodataAddrBeforeText),
59 desc.rodataSizeBeforeText) != EOK) {
60 LOG_JIT(ERROR) << "memcpy fail in copy fast jit code";
61 return false;
62 }
63 pText += desc.rodataSizeBeforeTextAlign;
64 }
65 if (!Jit::GetInstance()->IsEnableJitFort() || !Jit::GetInstance()->IsEnableAsyncCopyToFort() ||
66 !desc.isAsyncCompileMode) {
67 if (MachineCodeCopyToCache(desc, pText) == false) {
68 return false;
69 }
70 }
71 pText += desc.codeSizeAlign;
72 if (desc.rodataSizeAfterTextAlign != 0) {
73 if (memcpy_s(pText, desc.rodataSizeAfterTextAlign, // LCOV_EXCL_BR_LINE
74 reinterpret_cast<uint8_t*>(desc.rodataAddrAfterText),
75 desc.rodataSizeAfterText) != EOK) {
76 LOG_JIT(ERROR) << "memcpy fail in copy fast jit code";
77 return false;
78 }
79 }
80 return true;
81 }
82
SetNonText(const MachineCodeDesc & desc,EntityId methodId)83 bool MachineCode::SetNonText(const MachineCodeDesc &desc, EntityId methodId)
84 {
85 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
86 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
87 if (memcpy_s(stackmapAddr, desc.stackMapOrOffsetTableSize, // LCOV_EXCL_BR_LINE
88 reinterpret_cast<uint8_t*>(desc.stackMapOrOffsetTableAddr),
89 desc.stackMapOrOffsetTableSize) != EOK) {
90 LOG_JIT(ERROR) << "memcpy fail in copy fast jit stackmap";
91 return false;
92 }
93
94 FuncEntryDes *funcEntry = reinterpret_cast<FuncEntryDes*>(desc.funcEntryDesAddr);
95 if (!funcEntry) {
96 LOG_JIT(ERROR) << "funcEntry is null.";
97 return false;
98 }
99 uint32_t cnt = desc.funcEntryDesSize / sizeof(FuncEntryDes);
100 ASSERT(cnt <= 2); // 2: jsfunction + deoptimize stub
101 for (uint32_t i = 0; i < cnt; i++) {
102 if (methodId == EntityId(funcEntry->indexInKindOrMethodId_)) {
103 uint64_t codeAddr = funcEntry->codeAddr_ +
104 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(textStart));
105 SetFuncAddr(codeAddr);
106 break;
107 }
108 funcEntry++;
109 }
110
111 SetIsFastCall(funcEntry->isFastCall_);
112 SetFpDeltaPrevFrameSp(funcEntry->fpDeltaPrevFrameSp_);
113 SetFuncSize(funcEntry->funcSize_);
114 SetCalleeRegisterNum(funcEntry->calleeRegisterNum_);
115 SetCalleeReg2OffsetArray(funcEntry->CalleeReg2Offset_);
116 return true;
117 }
118
SetData(const MachineCodeDesc & desc,JSHandle<Method> & method,size_t dataSize,RelocMap & relocInfo)119 bool MachineCode::SetData(const MachineCodeDesc &desc, JSHandle<Method> &method, size_t dataSize, RelocMap &relocInfo)
120 {
121 DISALLOW_GARBAGE_COLLECTION;
122 if (desc.codeType == MachineCodeType::BASELINE_CODE) {
123 return SetBaselineCodeData(desc, method, dataSize, relocInfo);
124 }
125
126 SetOSROffset(MachineCode::INVALID_OSR_OFFSET);
127 SetOsrDeoptFlag(false);
128 SetOsrExecuteCnt(0);
129
130 size_t instrSize = desc.rodataSizeBeforeTextAlign + desc.codeSizeAlign + desc.rodataSizeAfterTextAlign;
131
132 SetInstructionsSize(instrSize);
133 SetStackMapOrOffsetTableSize(desc.stackMapSizeAlign);
134 SetPayLoadSizeInBytes(dataSize);
135 if (Jit::GetInstance()->IsEnableJitFort()) {
136 SetInstructionsAddr(desc.instructionsAddr);
137 ASSERT(desc.instructionsAddr != 0);
138 ASSERT(dataSize == (desc.funcEntryDesSizeAlign + desc.stackMapSizeAlign) ||
139 dataSize == (desc.funcEntryDesSizeAlign + instrSize + desc.stackMapSizeAlign));
140 } else {
141 ASSERT(dataSize == (desc.funcEntryDesSizeAlign + instrSize + desc.stackMapSizeAlign));
142 }
143 if (!SetText(desc)) {
144 return false;
145 }
146 if (!SetNonText(desc, method->GetMethodId())) {
147 return false;
148 }
149
150 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
151 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
152 CString methodName = method->GetRecordNameStr() + "." + CString(method->GetMethodName());
153 LOG_JIT(DEBUG) << "Fast JIT MachineCode :" << methodName << ", " << " text addr:" <<
154 reinterpret_cast<void*>(GetText()) << ", size:" << instrSize <<
155 ", stackMap addr:" << reinterpret_cast<void*>(stackmapAddr) <<
156 ", size:" << desc.stackMapSizeAlign;
157
158 if (!SetPageProtect(textStart, dataSize)) {
159 LOG_JIT(ERROR) << "MachineCode::SetData SetPageProtect failed";
160 return false;
161 }
162 return true;
163 }
164
SetBaselineCodeData(const MachineCodeDesc & desc,JSHandle<Method> & method,size_t dataSize,RelocMap & relocInfo)165 bool MachineCode::SetBaselineCodeData(const MachineCodeDesc &desc,
166 JSHandle<Method> &method, size_t dataSize, RelocMap &relocInfo)
167 {
168 DISALLOW_GARBAGE_COLLECTION;
169
170 size_t instrSizeAlign = desc.codeSizeAlign;
171 SetInstructionsSize(instrSizeAlign);
172
173 SetStackMapOrOffsetTableSize(desc.stackMapSizeAlign);
174 if (Jit::GetInstance()->IsEnableJitFort()) {
175 ASSERT(desc.instructionsAddr != 0);
176 ASSERT(dataSize == (desc.stackMapSizeAlign) || // reg. obj
177 dataSize == (instrSizeAlign + desc.stackMapSizeAlign)); // huge obj
178 SetInstructionsAddr(desc.instructionsAddr);
179 } else {
180 ASSERT(dataSize == (instrSizeAlign + desc.stackMapSizeAlign));
181 }
182 SetPayLoadSizeInBytes(dataSize);
183
184 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
185 if (Jit::GetInstance()->IsEnableJitFort()) {
186 // relax assert for now until machine code padding for JitFort resolved
187 ASSERT(IsAligned(reinterpret_cast<uintptr_t>(textStart), TEXT_ALIGN) ||
188 IsAligned(reinterpret_cast<uintptr_t>(textStart), DATA_ALIGN));
189 } else {
190 ASSERT(IsAligned(reinterpret_cast<uintptr_t>(textStart), TEXT_ALIGN));
191 }
192 uint8_t *pText = textStart;
193
194 // relocating for baselinejit opt
195 if (desc.archType == MachineCodeArchType::AArch64 && desc.codeType == MachineCodeType::BASELINE_CODE) {
196 kungfu::RelocWriterAArch64 reWriter;
197 reWriter.RewriteRelocInfo((uint8_t*)desc.codeAddr, pText, relocInfo);
198 }
199
200 if (!Jit::GetInstance()->IsEnableJitFort() || !Jit::GetInstance()->IsEnableAsyncCopyToFort() ||
201 !desc.isAsyncCompileMode) {
202 if (MachineCodeCopyToCache(desc, pText) == false) {
203 return false;
204 }
205 }
206 pText += instrSizeAlign;
207
208 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
209 if (memcpy_s(stackmapAddr, desc.stackMapOrOffsetTableSize, // LCOV_EXCL_BR_LINE
210 reinterpret_cast<uint8_t*>(desc.stackMapOrOffsetTableAddr),
211 desc.stackMapOrOffsetTableSize) != EOK) {
212 LOG_BASELINEJIT(ERROR) << "memcpy fail in copy fast baselineJIT offsetTable";
213 return false;
214 }
215
216 SetFuncAddr(reinterpret_cast<uintptr_t>(textStart));
217
218 CString methodName = method->GetRecordNameStr() + "." + CString(method->GetMethodName());
219 LOG_BASELINEJIT(DEBUG) << "BaselineCode :" << methodName << ", " << " text addr:" <<
220 reinterpret_cast<void*>(GetText()) << ", size:" << instrSizeAlign <<
221 ", stackMap addr: 0, size: 0";
222
223 if (!SetPageProtect(textStart, dataSize)) {
224 LOG_BASELINEJIT(ERROR) << "MachineCode::SetBaseLineCodeData SetPageProtect failed";
225 return false;
226 }
227 return true;
228 }
229
IsInText(const uintptr_t pc) const230 bool MachineCode::IsInText(const uintptr_t pc) const
231 {
232 uintptr_t textStart = GetText();
233 uintptr_t textEnd = textStart + GetTextSize();
234 return textStart <= pc && pc < textEnd;
235 }
236
CalCallSiteInfo() const237 std::tuple<uint64_t, uint8_t*, int, kungfu::CalleeRegAndOffsetVec> MachineCode::CalCallSiteInfo() const
238 {
239 uintptr_t textStart = GetText();
240 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
241 ASSERT(stackmapAddr != nullptr);
242
243 int delta = GetFpDeltaPrevFrameSp();
244 kungfu::CalleeRegAndOffsetVec calleeRegInfo;
245 for (uint32_t j = 0; j < GetCalleeRegisterNum(); j++) {
246 kungfu::LLVMStackMapType::DwarfRegType reg =
247 static_cast<kungfu::LLVMStackMapType::DwarfRegType>(GetCalleeReg2OffsetArray(2 * j));
248 kungfu::LLVMStackMapType::OffsetType offset =
249 static_cast<kungfu::LLVMStackMapType::OffsetType>(GetCalleeReg2OffsetArray(2 * j + 1));
250 kungfu::LLVMStackMapType::DwarfRegAndOffsetType regAndOffset = std::make_pair(reg, offset);
251 calleeRegInfo.emplace_back(regAndOffset);
252 }
253 auto ret = std::make_tuple(textStart, stackmapAddr, delta, calleeRegInfo);
254 return ret;
255 }
256
GetText() const257 uintptr_t MachineCode::GetText() const
258 {
259 if (Jit::GetInstance()->IsEnableJitFort()) {
260 return GetInstructionsAddr();
261 } else {
262 return GetNonTextAddress();
263 }
264 }
265
GetStackMapOrOffsetTableAddress() const266 uint8_t *MachineCode::GetStackMapOrOffsetTableAddress() const
267 {
268 if (Jit::GetInstance()->IsEnableJitFort()) {
269 // stackmap immediately follows MachineCode NonText area
270 return reinterpret_cast<uint8_t*>(GetNonTextAddress());
271 } else {
272 return reinterpret_cast<uint8_t*>(GetText() + GetInstructionsSize());
273 }
274 }
275
ProcessMarkObject()276 void MachineCode::ProcessMarkObject()
277 {
278 Region *region = Region::ObjectAddressToRange(this);
279 Heap *localHeap = reinterpret_cast<Heap *>(region->GetLocalHeap());
280 if (!localHeap) {
281 // it is a huge machine code object. skip
282 return;
283 }
284 ASSERT(localHeap->GetMachineCodeSpace());
285 if (localHeap->GetMachineCodeSpace()) {
286 localHeap->GetMachineCodeSpace()->MarkJitFortMemAlive(this);
287 }
288 }
289
290 } // namespace panda::ecmascript
291