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
23namespace panda::ecmascript {
24
25static 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
37static 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
48bool 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
79bool 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
115bool 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
161bool 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
220bool 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
227std::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
247uintptr_t MachineCode::GetText() const
248{
249    if (Jit::GetInstance()->IsEnableJitFort()) {
250        return GetInstructionsAddr();
251    } else {
252        return GetNonTextAddress();
253    }
254}
255
256uint8_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
266void 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