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