1/*
2 * Copyright (c) 2022 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#ifndef ECMASCRIPT_COMPILER_ASSEMBLER_H
16#define ECMASCRIPT_COMPILER_ASSEMBLER_H
17
18#include "ecmascript/mem/dyn_chunk.h"
19#ifdef JIT_ENABLE_CODE_SIGN
20#include "ecmascript/compiler/jit_signcode.h"
21#include "jit_buffer_integrity.h"
22#endif
23
24namespace panda::ecmascript {
25#ifdef JIT_ENABLE_CODE_SIGN
26using namespace OHOS::Security::CodeSign;
27#endif
28enum class Triple {
29    TRIPLE_AMD64,
30    TRIPLE_AARCH64,
31    TRIPLE_ARM32,
32};
33
34#define TARGET_X64 "x86_64-unknown-linux-gnu"
35#define TARGET_AARCH64 "aarch64-unknown-linux-gnu"
36#define TARGET_ARM32 "arm-unknown-linux-gnu"
37
38class GCStackMapRegisters {
39public:
40#if defined(PANDA_TARGET_AMD64)
41    static constexpr int SP = 7;  /* x7 */
42    static constexpr int FP = 6;  /* x6 */
43#elif defined(PANDA_TARGET_ARM64)
44    static constexpr int SP = 31;  /* x31 */
45    static constexpr int FP = 29;  /* x29 */
46#elif defined(PANDA_TARGET_ARM32)
47    static constexpr int SP = 13;  /* x13 */
48    static constexpr int FP = 11;  /* x11 */
49#else
50    static constexpr int SP = -1;
51    static constexpr int FP = -1;
52#endif
53
54static int GetFpRegByTriple(Triple triple)
55{
56    int fp = -1;
57    switch (triple) {
58        case Triple::TRIPLE_AMD64:
59            fp = 6;  /* x6 */
60            break;
61        case Triple::TRIPLE_ARM32:
62            fp = 11;  /* x11 */
63            break;
64        case Triple::TRIPLE_AARCH64:
65            fp = 29;  /* x29 */
66            break;
67        default:
68            UNREACHABLE();
69            break;
70    }
71    return fp;
72}
73
74static int GetSpRegByTriple(Triple triple)
75{
76    int sp = -1;
77    switch (triple) {
78        case Triple::TRIPLE_AMD64:
79            sp = 7;  /* x7 */
80            break;
81        case Triple::TRIPLE_ARM32:
82            sp = 13;  /* x13 */
83            break;
84        case Triple::TRIPLE_AARCH64:
85            sp = 31;  /* x31 */
86            break;
87        default:
88            UNREACHABLE();
89            break;
90    }
91    return sp;
92}
93};
94
95enum Distance {
96    Near,
97    Far
98};
99
100// When run from cpp to assembly code, there are some insts before the assembly frame is ready.
101// When return from assembly code to cpp, there are some insts after the assembly frame is broken.
102// And here are the numbers of insts. Only AsmInterpreterEntryFrame is dealt here, and there is no need
103// for OptimizedEntryFrame because insts for OptimizedEntryFrame are negligible.
104enum FrameCompletionPos : uint64_t {
105    // X64
106    X64CppToAsmInterp = 28,
107    X64AsmInterpToCpp = 9,
108    X64EntryFrameDuration = 70,
109    // ARM64
110    ARM64CppToAsmInterp = 56,
111    ARM64AsmInterpToCpp = 40,
112    ARM64EntryFrameDuration = 116,
113};
114
115class Label {
116public:
117    Label() = default;
118
119    ~Label() = default;
120
121    bool IsBound() const
122    {
123        return pos_ > 0;
124    }
125
126    bool IsLinked() const
127    {
128        return pos_ < 0;
129    }
130
131    bool IsLinkedNear() const
132    {
133        return nearPos_ > 0;
134    }
135
136    uint32_t GetPos() const
137    {
138        return static_cast<uint32_t>(pos_ - 1);
139    }
140
141    uint32_t GetLinkedPos() const
142    {
143        ASSERT(!IsBound());
144        return static_cast<uint32_t>(-pos_ - 1);
145    }
146
147    void BindTo(int32_t pos)
148    {
149        // +1 skip offset 0
150        pos_ = pos + 1;
151    }
152
153    void LinkTo(int32_t pos)
154    {
155        // +1 skip offset 0
156        pos_ = - (pos + 1);
157    }
158
159    void UnlinkNearPos()
160    {
161        nearPos_ = 0;
162    }
163
164    void LinkNearPos(uint32_t pos)
165    {
166        // +1 skip offset 0
167        nearPos_ = pos + 1;
168    }
169
170    uint32_t GetLinkedNearPos() const
171    {
172        ASSERT(!IsBound());
173        return static_cast<uint32_t>(nearPos_ - 1);
174    }
175
176private:
177    int32_t pos_ = 0;
178    uint32_t nearPos_ = 0;
179};
180
181class Assembler {
182public:
183    explicit Assembler(Chunk *chunk)
184        : buffer_(chunk) {}
185    ~Assembler() = default;
186
187    void EmitU8(uint8_t v)
188    {
189        buffer_.EmitChar(v);
190    }
191
192    void EmitI8(int8_t v)
193    {
194        buffer_.EmitChar(static_cast<uint8_t>(v));
195    }
196
197    void EmitU16(uint16_t v)
198    {
199        buffer_.EmitU16(v);
200    }
201
202    ARK_INLINE void EmitU32(uint32_t v)
203    {
204        buffer_.EmitU32(v);
205#ifdef JIT_ENABLE_CODE_SIGN
206        if (doCodeSign && kungfu::JitSignCode::GetInstance()->GetCodeSigner() != nullptr) {
207            int err = AppendData(kungfu::JitSignCode::GetInstance()->GetCodeSigner(),
208                                 reinterpret_cast<const uint8_t *>(&v), sizeof(v));
209            if (err != 0) {
210                LOG_BASELINEJIT(ERROR) << "Baseline AppendData failed, err = " << std::hex << err;
211            }
212        }
213#endif
214    }
215
216    void EmitI32(int32_t v)
217    {
218        buffer_.EmitU32(static_cast<uint32_t>(v));
219    }
220
221    void EmitU64(uint64_t v)
222    {
223        buffer_.EmitU64(v);
224    }
225
226    void PutI8(size_t offset, int8_t data)
227    {
228        buffer_.PutU8(offset, static_cast<int8_t>(data));
229    }
230
231    ARK_INLINE void PutI32(size_t offset, int32_t data)
232    {
233        buffer_.PutU32(offset, static_cast<int32_t>(data));
234#ifdef JIT_ENABLE_CODE_SIGN
235        if (doCodeSign && kungfu::JitSignCode::GetInstance()->GetCodeSigner() != nullptr) {
236            int err = PatchData(kungfu::JitSignCode::GetInstance()->GetCodeSigner(),
237                                offset, reinterpret_cast<const uint8_t *>(&data), sizeof(data));
238            if (err != 0) {
239                LOG_BASELINEJIT(ERROR) << "Baseline PatchData failed, err = " << std::hex << err;
240            }
241        }
242#endif
243    }
244
245    uint32_t GetU32(size_t offset) const
246    {
247        return buffer_.GetU32(offset);
248    }
249
250    int8_t GetI8(size_t offset) const
251    {
252        return static_cast<int8_t>(buffer_.GetU8(offset));
253    }
254
255    uint8_t GetU8(size_t offset) const
256    {
257        return buffer_.GetU8(offset);
258    }
259
260    size_t GetCurrentPosition() const
261    {
262        return buffer_.GetSize();
263    }
264
265    uint8_t *GetBegin() const
266    {
267        return buffer_.GetBegin();
268    }
269
270    static bool InRangeN(int32_t x, uint32_t n)
271    {
272        int32_t limit = 1 << (n - 1);
273        return (x >= -limit) && (x < limit);
274    }
275
276    static bool InRange8(int32_t x)
277    {
278        // 8: range8
279        return InRangeN(x, 8);
280    }
281
282    static void GetFrameCompletionPos(uint64_t &headerSize, uint64_t &tailSize, uint64_t &entryDuration)
283    {
284#if defined(PANDA_TARGET_AMD64)
285        headerSize = FrameCompletionPos::X64CppToAsmInterp;
286        tailSize = FrameCompletionPos::X64AsmInterpToCpp;
287        entryDuration = FrameCompletionPos::X64EntryFrameDuration;
288#elif defined(PANDA_TARGET_ARM64)
289        headerSize = FrameCompletionPos::ARM64CppToAsmInterp;
290        tailSize = FrameCompletionPos::ARM64AsmInterpToCpp;
291        entryDuration = FrameCompletionPos::ARM64EntryFrameDuration;
292#else
293        headerSize = 0;
294        tailSize = 0;
295        entryDuration = 0;
296        LOG_ECMA(FATAL) << "Assembler does not currently support other platforms, please run on x64 and arm64";
297#endif
298    }
299
300    void SetDoCodeSign()
301    {
302        doCodeSign = true;
303    }
304
305private:
306    DynChunk buffer_;
307    bool doCodeSign = false;
308};
309}  // panda::ecmascript
310#endif  // ECMASCRIPT_COMPILER_ASSEMBLER_H
311