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