1/**
2 * Copyright (c) 2021-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
16#ifndef LIBPANDABASE_UTILS_CFRAME_LAYOUT_H
17#define LIBPANDABASE_UTILS_CFRAME_LAYOUT_H
18
19#include "arch.h"
20
21namespace panda {
22
23enum FrameBridgeKind {
24    INTERPRETER_TO_COMPILED_CODE = 1,
25    COMPILED_CODE_TO_INTERPRETER = 2,
26    BYPASS = 3,
27};
28
29template <ssize_t START, ssize_t SIZE>
30class StackRegion {
31public:
32    static_assert(SIZE >= 0);
33    template <size_t SZ>
34    using NextStackRegion = StackRegion<START + SIZE, SZ>;
35
36    using NextStackSlot = StackRegion<START + SIZE, 1>;
37
38    static constexpr ssize_t Start()
39    {
40        return START;
41    }
42    static constexpr ssize_t End()
43    {
44        return Start() + GetSize();
45    }
46    static constexpr ssize_t GetSize()
47    {
48        return SIZE;
49    }
50    template <class CFrameLayoutT>
51    static constexpr ssize_t GetOffsetFromSpInSlots(const CFrameLayoutT &fl)
52    {
53        return fl.template GetFrameSize<CFrameLayoutT::SLOTS>() - START - 2U;
54    }
55    template <class CFrameLayoutT>
56    static constexpr ssize_t GetOffsetFromSpInBytes(const CFrameLayoutT &fl)
57    {
58        return GetOffsetFromSpInSlots(fl) * fl.GetSlotSize();
59    }
60
61private:
62};
63
64class CFrameLayout {
65public:
66    constexpr CFrameLayout(Arch arch, size_t spills_count)
67        : arch_(arch), spills_count_(AlignSpillCount(arch, spills_count))
68    {
69    }
70    ~CFrameLayout() = default;
71    DEFAULT_COPY_SEMANTIC(CFrameLayout);
72    DEFAULT_MOVE_SEMANTIC(CFrameLayout);
73
74    enum OffsetOrigin { SP, FP };
75    enum OffsetUnit { BYTES, SLOTS };
76
77    using StackArgSlot = StackRegion<-2, 1>;               // -2 slot
78    using LrSlot = StackArgSlot::NextStackSlot;            // -1 slot
79    using PrevFrameSlot = LrSlot::NextStackSlot;           //  0 slot
80    using MethodSlot = PrevFrameSlot::NextStackSlot;       //  1 slot
81    using FlagsSlot = MethodSlot::NextStackSlot;           //  2 slot
82    using DataRegion = FlagsSlot::NextStackRegion<2>;      // [3..4] slots
83    using LocalsRegion = DataRegion::NextStackRegion<4>;   // [5..8] slots
84    using SlotsRegion = LocalsRegion::NextStackRegion<0>;  // [9...] slots
85    using RegsRegion = SlotsRegion;
86
87    static constexpr ssize_t HEADER_SIZE = FlagsSlot::End() - LrSlot::Start();
88
89    enum class FrameKind : uint8_t { DEFAULT = 0, OSR = 1, NATIVE = 2, LAST = NATIVE };
90
91    using ShouldDeoptimizeFlag = BitField<bool, 0, 1>;
92    using HasFloatRegsFlag = ShouldDeoptimizeFlag::NextFlag;
93    using FrameKindField = HasFloatRegsFlag::NextField<FrameKind, MinimumBitsToStore(FrameKind::LAST)>;
94
95    // Current usage of the locals:
96    //  [0..1] slots: internal spill slots for codegen
97    //  [2..3] slots: fp and lr in osr mode
98    // TODO(msherstennikov): need to make flexible machinery to handle locals
99    static constexpr size_t LOCALS_START_SLOT = 5;
100    static constexpr size_t STACK_START_SLOT = 9;
101    static constexpr size_t CALLEE_REGS_START_SLOT = STACK_START_SLOT;
102
103    // NB! This 4 static constants are for cframe_test and stack_walker_test
104    // Use getters below in other code.
105    static constexpr size_t CALLEE_FP_REGS_START_SLOT =
106        CALLEE_REGS_START_SLOT + GetCalleeRegsCount(RUNTIME_ARCH, false);
107    static constexpr size_t CALLER_REGS_START_SLOT = CALLEE_FP_REGS_START_SLOT + GetCalleeRegsCount(RUNTIME_ARCH, true);
108    static constexpr size_t CALLER_FP_REGS_START_SLOT =
109        CALLER_REGS_START_SLOT + GetCallerRegsCount(RUNTIME_ARCH, false);
110    static constexpr size_t SPILLS_START_SLOT = CALLER_FP_REGS_START_SLOT + GetCallerRegsCount(RUNTIME_ARCH, true);
111
112    // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
113    constexpr size_t GetCalleeRegsStartSlot() const
114    {
115        return STACK_START_SLOT;
116    }
117
118    constexpr size_t GetCalleeFpRegsStartSlot() const
119    {
120        return GetCalleeRegsStartSlot() + ::panda::GetCalleeRegsCount(GetArch(), false);
121    }
122
123    constexpr size_t GetCallerRegsStartSlot() const
124    {
125        return GetCalleeFpRegsStartSlot() + ::panda::GetCalleeRegsCount(GetArch(), true);
126    }
127
128    constexpr size_t GetCallerFpRegsStartSlot() const
129    {
130        return GetCallerRegsStartSlot() + ::panda::GetCallerRegsCount(GetArch(), false);
131    }
132
133    constexpr size_t GetSpillsStartSlot() const
134    {
135        return GetCallerFpRegsStartSlot() + ::panda::GetCallerRegsCount(GetArch(), true);
136    }
137
138    // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
139    constexpr ptrdiff_t GetStackArgsStartSlot() const
140    {
141        return StackArgSlot::Start();
142    }
143
144    constexpr Arch GetArch() const
145    {
146        return arch_;
147    }
148
149    template <OffsetUnit unit>
150    constexpr size_t GetFrameSize() const
151    {
152        // +1 for LR slot
153        size_t size_in_slots = STACK_START_SLOT + GetFirstSpillSlot() + spills_count_ + 1U;
154        return unit == BYTES ? (size_in_slots * GetSlotSize()) : size_in_slots;
155    }
156
157    template <OffsetOrigin origin, OffsetUnit unit>
158    constexpr ssize_t GetOffset(ssize_t slot) const
159    {
160        if constexpr (origin == SP) {  // NOLINT(readability-braces-around-statements)
161            const auto OFFSET = GetFrameSize<SLOTS>() - slot - 2U;
162            if constexpr (unit == BYTES) {  // NOLINT
163                return OFFSET * GetSlotSize();
164            }
165            return OFFSET;
166        } else {                            // NOLINT
167            if constexpr (unit == BYTES) {  // NOLINT
168                return slot * PointerSize(arch_);
169            }
170            return slot;
171        }
172    }
173
174    template <OffsetOrigin origin, OffsetUnit unit>
175    constexpr ssize_t GetMethodOffset() const
176    {
177        return GetOffset<origin, unit>(MethodSlot::Start());
178    }
179
180    template <OffsetOrigin origin, OffsetUnit unit>
181    constexpr ssize_t GetReturnAddressOffset() const
182    {
183        return GetOffset<origin, unit>(LrSlot::Start());
184    }
185
186    template <OffsetOrigin origin, OffsetUnit unit>
187    constexpr ssize_t GetFreeSlotOffset() const
188    {
189        return GetOffset<origin, unit>(LOCALS_START_SLOT);
190    }
191
192    template <OffsetOrigin origin, OffsetUnit unit>
193    constexpr ssize_t GetSpillOffset(size_t spill_slot) const
194    {
195        size_t shift = Is64BitsArch(arch_) ? 0 : 1;  // in arm32 one slot is 2 word and shifted by 1
196        return GetOffset<origin, unit>(STACK_START_SLOT + GetFirstSpillSlot() + (spill_slot << shift) + shift);
197    }
198
199    constexpr ssize_t GetSpillOffsetFromSpInBytes(size_t spill_slot) const
200    {
201        return GetSpillOffset<CFrameLayout::SP, CFrameLayout::BYTES>(spill_slot);
202    }
203
204    // NOLINTNEXTLINE(readability-convert-member-functions-to-static)
205    static constexpr ssize_t GetStackStartSlot()
206    {
207        return STACK_START_SLOT;
208    }
209
210    constexpr size_t GetCalleeSlotsCount() const
211    {
212        return GetCalleeRegistersCount(false) + GetCalleeRegistersCount(true);
213    }
214
215    constexpr size_t GetCallerSlotsCount() const
216    {
217        return GetCallerRegistersCount(false) + GetCallerRegistersCount(true);
218    }
219
220    constexpr size_t GetFirstSpillSlot() const
221    {
222        return GetCalleeSlotsCount() + GetCallerSlotsCount();
223    }
224    constexpr size_t GetLastSpillSlot() const
225    {
226        return GetFirstSpillSlot() + spills_count_ - 1;
227    }
228
229    constexpr size_t GetCalleeFirstSlot(bool is_fp) const
230    {
231        return is_fp ? GetCalleeRegistersCount(false) : 0;
232    }
233
234    constexpr size_t GetCalleeLastSlot(bool is_fp) const
235    {
236        return GetCalleeFirstSlot(is_fp) + GetCalleeRegistersCount(is_fp) - 1;
237    }
238
239    constexpr size_t GetCallerFirstSlot(bool is_fp) const
240    {
241        return GetCalleeLastSlot(true) + 1 + (is_fp ? GetCallerRegistersCount(false) : 0);
242    }
243
244    constexpr size_t GetCallerLastSlot(bool is_fp) const
245    {
246        return GetCallerFirstSlot(is_fp) + GetCallerRegistersCount(is_fp) - 1;
247    }
248
249    constexpr size_t GetCalleeRegistersCount(bool is_fp) const
250    {
251        return panda::GetCalleeRegsCount(arch_, is_fp);
252    }
253
254    constexpr size_t GetCallerRegistersCount(bool is_fp) const
255    {
256        return panda::GetCallerRegsCount(arch_, is_fp);
257    }
258
259    constexpr size_t GetSlotSize() const
260    {
261        return PointerSize(arch_);
262    }
263
264    constexpr size_t GetSpillsCount() const
265    {
266        return spills_count_;
267    }
268
269    constexpr size_t GetRegsSlotsCount() const
270    {
271        return GetCalleeSlotsCount() + GetCallerSlotsCount() + GetSpillsCount();
272    }
273
274    static constexpr size_t GetLocalsCount()
275    {
276        return STACK_START_SLOT - LOCALS_START_SLOT;
277    }
278
279    static constexpr ssize_t GetOsrFpLrSlot()
280    {
281        return LocalsRegion::Start() + static_cast<ssize_t>(GetLocalsCount()) - 1;
282    }
283
284    constexpr ssize_t GetOsrFpLrOffset() const
285    {
286        return GetOffset<CFrameLayout::FP, CFrameLayout::BYTES>(GetOsrFpLrSlot());
287    }
288
289    static constexpr size_t GetFpLrSlotsCount()
290    {
291        static_assert(MethodSlot::Start() > LrSlot::Start());
292        return MethodSlot::Start() - LrSlot::Start();
293    }
294
295    static constexpr size_t GetTopToRegsSlotsCount()
296    {
297        static_assert(SlotsRegion::Start() > LrSlot::Start());
298        return SlotsRegion::Start() - LrSlot::Start();
299    }
300
301private:
302    constexpr size_t AlignSpillCount(Arch arch, size_t spills_count)
303    {
304        // Allign by odd-number, because GetSpillsStartSlot begins from fp (+1 slot for lr)
305        if (arch == Arch::AARCH64 || arch == Arch::X86_64) {
306            // 2 is to calculate the spills_count on the AARCH32 processor
307            if (((GetSpillsStartSlot() + spills_count) % 2) == 0) {
308                spills_count++;
309            }
310        } else if (arch == Arch::AARCH32) {
311            // Additional slot for spill/fill <-> sf-registers ldrd miscorp
312            // 2 is to calculate the spills_count on the AARCH32 processor
313            spills_count = (spills_count + 1) * 2;
314            if (((GetSpillsStartSlot() + spills_count) % 2) == 0) {
315                spills_count++;
316            }
317        }
318        return spills_count;
319    }
320
321private:
322    Arch arch_;
323    size_t spills_count_ {0};
324};
325
326static_assert(CFrameLayout::GetLocalsCount() >= 2U);
327static_assert(CFrameLayout::GetFpLrSlotsCount() == 2U);
328
329enum class FrameKind { NONE, INTERPRETER, COMPILER };
330
331template <FrameKind kind>
332struct BoundaryFrame;
333
334template <>
335struct BoundaryFrame<FrameKind::INTERPRETER> {
336    static constexpr ssize_t METHOD_OFFSET = 1;
337    static constexpr ssize_t FP_OFFSET = 0;
338    static constexpr ssize_t RETURN_OFFSET = 2;
339    static constexpr ssize_t CALLEES_OFFSET = -1;
340};
341
342template <>
343struct BoundaryFrame<FrameKind::COMPILER> {
344    static constexpr ssize_t METHOD_OFFSET = -1;
345    static constexpr ssize_t FP_OFFSET = 0;
346    static constexpr ssize_t RETURN_OFFSET = 1;
347    static constexpr ssize_t CALLEES_OFFSET = -2;
348};
349
350using CFrameReturnAddr = CFrameLayout::LrSlot;
351using CFramePrevFrame = CFrameLayout::PrevFrameSlot;
352using CFrameMethod = CFrameLayout::MethodSlot;
353using CFrameFlags = CFrameLayout::FlagsSlot;
354using CFrameData = CFrameLayout::DataRegion;
355using CFrameLocals = CFrameLayout::LocalsRegion;
356using CFrameSlots = CFrameLayout::SlotsRegion;
357using CFrameRegs = CFrameLayout::RegsRegion;
358
359}  // namespace panda
360
361#endif  // LIBPANDABASE_UTILS_CFRAME_LAYOUT_H
362