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
20 namespace panda::ecmascript {
21
SetPageProtect(uint8_t * textStart,size_t dataSize)22 static bool SetPageProtect(uint8_t *textStart, size_t dataSize)
23 {
24 if (!Jit::GetInstance()->IsEnableJitFort()) {
25 constexpr size_t pageSize = 4096;
26 uintptr_t startPage = AlignDown(reinterpret_cast<uintptr_t>(textStart), pageSize);
27 uintptr_t endPage = AlignUp(reinterpret_cast<uintptr_t>(textStart) + dataSize, pageSize);
28 size_t protSize = endPage - startPage;
29 return PageProtect(reinterpret_cast<void*>(startPage), protSize, PAGE_PROT_EXEC_READWRITE);
30 }
31 return true;
32 }
33
MachineCodeCopyToCache(const MachineCodeDesc & desc,uint8_t * pText)34 static int MachineCodeCopyToCache([[maybe_unused]] const MachineCodeDesc &desc, [[maybe_unused]] uint8_t *pText)
35 {
36 #ifndef JIT_ENABLE_CODE_SIGN
37 if (memcpy_s(pText, desc.codeSizeAlign, reinterpret_cast<uint8_t*>(desc.codeAddr), desc.codeSize) != EOK) {
38 LOG_JIT(ERROR) << "memcpy failed in CopyToCache";
39 return false;
40 }
41 #endif
42 return true;
43 }
44
SetText(const MachineCodeDesc & desc)45 bool MachineCode::SetText(const MachineCodeDesc &desc)
46 {
47 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
48 uint8_t *pText = textStart;
49 if (desc.rodataSizeBeforeTextAlign != 0) {
50 if (memcpy_s(pText, desc.rodataSizeBeforeTextAlign,
51 reinterpret_cast<uint8_t*>(desc.rodataAddrBeforeText), desc.rodataSizeBeforeText) != EOK) {
52 LOG_JIT(ERROR) << "memcpy fail in copy fast jit code";
53 return false;
54 }
55 pText += desc.rodataSizeBeforeTextAlign;
56 }
57 if (!Jit::GetInstance()->IsEnableJitFort() || !Jit::GetInstance()->IsEnableAsyncCopyToFort() ||
58 !desc.isAsyncCompileMode) {
59 if (MachineCodeCopyToCache(desc, pText) == false) {
60 return false;
61 }
62 }
63 pText += desc.codeSizeAlign;
64 if (desc.rodataSizeAfterTextAlign != 0) {
65 if (memcpy_s(pText, desc.rodataSizeAfterTextAlign,
66 reinterpret_cast<uint8_t*>(desc.rodataAddrAfterText), desc.rodataSizeAfterText) != EOK) {
67 LOG_JIT(ERROR) << "memcpy fail in copy fast jit code";
68 return false;
69 }
70 }
71 return true;
72 }
73
SetNonText(const MachineCodeDesc & desc,EntityId methodId)74 bool MachineCode::SetNonText(const MachineCodeDesc &desc, EntityId methodId)
75 {
76 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
77 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
78 if (memcpy_s(stackmapAddr, desc.stackMapOrOffsetTableSize,
79 reinterpret_cast<uint8_t*>(desc.stackMapOrOffsetTableAddr),
80 desc.stackMapOrOffsetTableSize) != EOK) {
81 LOG_JIT(ERROR) << "memcpy fail in copy fast jit stackmap";
82 return false;
83 }
84
85 FuncEntryDes *funcEntry = reinterpret_cast<FuncEntryDes*>(GetFuncEntryDesAddress());
86 if (memcpy_s(funcEntry, desc.funcEntryDesSize, reinterpret_cast<uint8_t*>(desc.funcEntryDesAddr),
87 desc.funcEntryDesSize) != EOK) {
88 LOG_JIT(ERROR) << "memcpy fail in copy fast jit funcEntry";
89 return false;
90 }
91
92 uint32_t cnt = desc.funcEntryDesSize / sizeof(FuncEntryDes);
93 ASSERT(cnt <= 2); // 2: jsfunction + deoptimize stub
94 for (uint32_t i = 0; i < cnt; i++) {
95 funcEntry->codeAddr_ += reinterpret_cast<uintptr_t>(textStart);
96 if (methodId == EntityId(funcEntry->indexInKindOrMethodId_)) {
97 SetFuncAddr(funcEntry->codeAddr_);
98 }
99 funcEntry++;
100 }
101 return true;
102 }
103
SetData(const MachineCodeDesc & desc,JSHandle<Method> & method,size_t dataSize)104 bool MachineCode::SetData(const MachineCodeDesc &desc, JSHandle<Method> &method, size_t dataSize)
105 {
106 DISALLOW_GARBAGE_COLLECTION;
107 if (desc.codeType == MachineCodeType::BASELINE_CODE) {
108 return SetBaselineCodeData(desc, method, dataSize);
109 }
110
111 SetOSROffset(MachineCode::INVALID_OSR_OFFSET);
112 SetOsrDeoptFlag(false);
113 SetOsrExecuteCnt(0);
114
115 size_t instrSize = desc.rodataSizeBeforeTextAlign + desc.codeSizeAlign + desc.rodataSizeAfterTextAlign;
116
117 SetFuncEntryDesSize(desc.funcEntryDesSizeAlign);
118 SetInstructionsSize(instrSize);
119 SetStackMapOrOffsetTableSize(desc.stackMapSizeAlign);
120 SetPayLoadSizeInBytes(dataSize);
121 if (Jit::GetInstance()->IsEnableJitFort()) {
122 SetInstructionsAddr(desc.instructionsAddr);
123 ASSERT(desc.instructionsAddr != 0);
124 ASSERT(dataSize == (desc.funcEntryDesSizeAlign + desc.stackMapSizeAlign) ||
125 dataSize == (desc.funcEntryDesSizeAlign + instrSize + desc.stackMapSizeAlign));
126 } else {
127 ASSERT(dataSize == (desc.funcEntryDesSizeAlign + instrSize + desc.stackMapSizeAlign));
128 }
129 if (!SetText(desc)) {
130 return false;
131 }
132 if (!SetNonText(desc, method->GetMethodId())) {
133 return false;
134 }
135
136 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
137 uint32_t cnt = desc.funcEntryDesSize / sizeof(FuncEntryDes);
138 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
139 CString methodName = method->GetRecordNameStr() + "." + CString(method->GetMethodName());
140 LOG_JIT(DEBUG) << "Fast JIT MachineCode :" << methodName << ", " << " text addr:" <<
141 reinterpret_cast<void*>(GetText()) << ", size:" << instrSize <<
142 ", stackMap addr:" << reinterpret_cast<void*>(stackmapAddr) <<
143 ", size:" << desc.stackMapSizeAlign <<
144 ", funcEntry addr:" << reinterpret_cast<void*>(GetFuncEntryDesAddress()) << ", count:" << cnt;
145
146 if (!SetPageProtect(textStart, dataSize)) {
147 LOG_JIT(ERROR) << "MachineCode::SetData SetPageProtect failed";
148 return false;
149 }
150 return true;
151 }
152
SetBaselineCodeData(const MachineCodeDesc & desc,JSHandle<Method> & method,size_t dataSize)153 bool MachineCode::SetBaselineCodeData(const MachineCodeDesc &desc,
154 JSHandle<Method> &method, size_t dataSize)
155 {
156 DISALLOW_GARBAGE_COLLECTION;
157 SetFuncEntryDesSize(0);
158
159 size_t instrSizeAlign = desc.codeSizeAlign;
160 SetInstructionsSize(instrSizeAlign);
161
162 SetStackMapOrOffsetTableSize(desc.stackMapSizeAlign);
163 if (Jit::GetInstance()->IsEnableJitFort()) {
164 ASSERT(desc.instructionsAddr != 0);
165 ASSERT(dataSize == (desc.stackMapSizeAlign) || // reg. obj
166 dataSize == (instrSizeAlign + desc.stackMapSizeAlign)); // huge obj
167 SetInstructionsAddr(desc.instructionsAddr);
168 } else {
169 ASSERT(dataSize == (instrSizeAlign + desc.stackMapSizeAlign));
170 }
171 SetPayLoadSizeInBytes(dataSize);
172
173 uint8_t *textStart = reinterpret_cast<uint8_t*>(GetText());
174 if (Jit::GetInstance()->IsEnableJitFort()) {
175 // relax assert for now until machine code padding for JitFort resolved
176 ASSERT(IsAligned(reinterpret_cast<uintptr_t>(textStart), TEXT_ALIGN) ||
177 IsAligned(reinterpret_cast<uintptr_t>(textStart), DATA_ALIGN));
178 } else {
179 ASSERT(IsAligned(reinterpret_cast<uintptr_t>(textStart), TEXT_ALIGN));
180 }
181 uint8_t *pText = textStart;
182
183 if (!Jit::GetInstance()->IsEnableJitFort() || !Jit::GetInstance()->IsEnableAsyncCopyToFort() ||
184 !desc.isAsyncCompileMode) {
185 if (MachineCodeCopyToCache(desc, pText) == false) {
186 return false;
187 }
188 }
189 pText += instrSizeAlign;
190
191 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
192 if (memcpy_s(stackmapAddr, desc.stackMapOrOffsetTableSize,
193 reinterpret_cast<uint8_t*>(desc.stackMapOrOffsetTableAddr),
194 desc.stackMapOrOffsetTableSize) != EOK) {
195 LOG_BASELINEJIT(ERROR) << "memcpy fail in copy fast baselineJIT offsetTable";
196 return false;
197 }
198
199 SetFuncAddr(reinterpret_cast<uintptr_t>(textStart));
200
201 CString methodName = method->GetRecordNameStr() + "." + CString(method->GetMethodName());
202 LOG_BASELINEJIT(DEBUG) << "BaselineCode :" << methodName << ", " << " text addr:" <<
203 reinterpret_cast<void*>(GetText()) << ", size:" << instrSizeAlign <<
204 ", stackMap addr: 0, size: 0, funcEntry addr: 0, count 0";
205
206 if (!SetPageProtect(textStart, dataSize)) {
207 LOG_BASELINEJIT(ERROR) << "MachineCode::SetBaseLineCodeData SetPageProtect failed";
208 return false;
209 }
210 return true;
211 }
212
IsInText(const uintptr_t pc) const213 bool MachineCode::IsInText(const uintptr_t pc) const
214 {
215 uintptr_t textStart = GetText();
216 uintptr_t textEnd = textStart + GetTextSize();
217 return textStart <= pc && pc < textEnd;
218 }
219
GetFuncEntryDes() const220 uintptr_t MachineCode::GetFuncEntryDes() const
221 {
222 uint32_t funcEntryCnt = GetFuncEntryDesSize() / sizeof(FuncEntryDes);
223 FuncEntryDes *funcEntryDes = reinterpret_cast<FuncEntryDes*>(GetFuncEntryDesAddress());
224 for (uint32_t i = 0; i < funcEntryCnt; i++) {
225 if (funcEntryDes->codeAddr_ == GetFuncAddr()) {
226 return reinterpret_cast<uintptr_t>(funcEntryDes);
227 }
228 funcEntryDes++;
229 }
230 UNREACHABLE();
231 return 0;
232 }
233
CalCallSiteInfo(uintptr_t retAddr) const234 std::tuple<uint64_t, uint8_t*, int, kungfu::CalleeRegAndOffsetVec> MachineCode::CalCallSiteInfo(uintptr_t retAddr) const
235 {
236 uintptr_t textStart = GetText();
237 uint8_t *stackmapAddr = GetStackMapOrOffsetTableAddress();
238 ASSERT(stackmapAddr != nullptr);
239
240 uint32_t funcEntryCnt = GetFuncEntryDesSize() / sizeof(FuncEntryDes);
241 FuncEntryDes *tmpFuncEntryDes = reinterpret_cast<FuncEntryDes*>(GetFuncEntryDesAddress());
242 FuncEntryDes *funcEntryDes = nullptr;
243 ASSERT(tmpFuncEntryDes != nullptr);
244 uintptr_t pc = retAddr - 1; // -1: for pc
245 for (uint32_t i = 0; i < funcEntryCnt; i++) {
246 if (tmpFuncEntryDes->codeAddr_ <= pc && pc < (tmpFuncEntryDes->codeAddr_ + tmpFuncEntryDes->funcSize_)) {
247 funcEntryDes = tmpFuncEntryDes;
248 break;
249 }
250 tmpFuncEntryDes++;
251 }
252
253 if (funcEntryDes == nullptr) {
254 return {};
255 }
256
257 int delta = funcEntryDes->fpDeltaPrevFrameSp_;
258 kungfu::CalleeRegAndOffsetVec calleeRegInfo;
259 for (uint32_t j = 0; j < funcEntryDes->calleeRegisterNum_; j++) {
260 kungfu::LLVMStackMapType::DwarfRegType reg =
261 static_cast<kungfu::LLVMStackMapType::DwarfRegType>(funcEntryDes->CalleeReg2Offset_[2 * j]);
262 kungfu::LLVMStackMapType::OffsetType offset =
263 static_cast<kungfu::LLVMStackMapType::OffsetType>(funcEntryDes->CalleeReg2Offset_[2 * j + 1]);
264 kungfu::LLVMStackMapType::DwarfRegAndOffsetType regAndOffset = std::make_pair(reg, offset);
265 calleeRegInfo.emplace_back(regAndOffset);
266 }
267 auto ret = std::make_tuple(textStart, stackmapAddr, delta, calleeRegInfo);
268 return ret;
269 }
270
GetText() const271 uintptr_t MachineCode::GetText() const
272 {
273 if (Jit::GetInstance()->IsEnableJitFort()) {
274 return GetInstructionsAddr();
275 } else {
276 return GetFuncEntryDesAddress() + GetFuncEntryDesSize();
277 }
278 }
279
GetStackMapOrOffsetTableAddress() const280 uint8_t *MachineCode::GetStackMapOrOffsetTableAddress() const
281 {
282 if (Jit::GetInstance()->IsEnableJitFort()) {
283 // stackmap immediately follows FuncEntryDesc area
284 return reinterpret_cast<uint8_t*>(GetFuncEntryDesAddress() + GetFuncEntryDesSize());
285 } else {
286 return reinterpret_cast<uint8_t*>(GetText() + GetInstructionsSize());
287 }
288 }
289
290 } // namespace panda::ecmascript
291