/** * Copyright (c) 2021-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PANDA_RUNTIME_ARCH_HELPERS_H_ #define PANDA_RUNTIME_ARCH_HELPERS_H_ #include "libpandabase/utils/arch.h" #include "libpandabase/utils/bit_utils.h" #include "libpandabase/utils/span.h" #include "runtime/include/value.h" #include "runtime/include/mem/panda_containers.h" namespace ark::arch { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ARCH_COPY_METHOD_ARGS_DISPATCH \ it.IncrementWithoutCheck(); \ encoding = (*it).GetEncoding(); \ ASSERT(!(encoding & ~SHORTY_ELEM_MAX)); \ /* CC-OFFNXT(G.PRE.05, G.PRE.09) code generation */ \ goto *dispatch_table[encoding]; // We use macro instead of function because it's impossible to inline a dispatch table // We should inline the dispatch table for performance reasons. // LABEL_TYPEID_INVALID before LABEL_TYPEID_REFERENCE refers to tagged type (types.yaml) and does not handles here // CC-OFFNXT(C_RULE_ID_DEFINE_LENGTH_LIMIT) solid logic // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ARCH_COPY_METHOD_ARGS(METHOD, ARG_READER, ARG_WRITER) \ do { \ [[maybe_unused]] static constexpr size_t SHORTY_ELEM_MAX = 0xF; \ static constexpr std::array dispatch_table = { \ static_cast(&&LABEL_TYPEID_END), static_cast(&&LABEL_TYPEID_VOID), \ static_cast(&&LABEL_TYPEID_U8), static_cast(&&LABEL_TYPEID_I8), \ static_cast(&&LABEL_TYPEID_U8), static_cast(&&LABEL_TYPEID_I16), \ static_cast(&&LABEL_TYPEID_U16), static_cast(&&LABEL_TYPEID_I32), \ static_cast(&&LABEL_TYPEID_U32), static_cast(&&LABEL_TYPEID_F32), \ static_cast(&&LABEL_TYPEID_F64), static_cast(&&LABEL_TYPEID_I64), \ static_cast(&&LABEL_TYPEID_U64), static_cast(&&LABEL_TYPEID_INVALID), \ static_cast(&&LABEL_TYPEID_REFERENCE), static_cast(&&LABEL_TYPEID_INVALID)}; \ \ static_assert(dispatch_table.size() - 1 == SHORTY_ELEM_MAX); \ ASSERT(dispatch_table[static_cast((*panda_file::ShortyIterator()).GetId())] == &&LABEL_TYPEID_END); \ ASSERT(dispatch_table[static_cast(TypeId::U1)] == &&LABEL_TYPEID_U8); \ ASSERT(dispatch_table[static_cast(TypeId::I8)] == &&LABEL_TYPEID_I8); \ ASSERT(dispatch_table[static_cast(TypeId::U8)] == &&LABEL_TYPEID_U8); \ ASSERT(dispatch_table[static_cast(TypeId::I16)] == &&LABEL_TYPEID_I16); \ ASSERT(dispatch_table[static_cast(TypeId::U16)] == &&LABEL_TYPEID_U16); \ ASSERT(dispatch_table[static_cast(TypeId::I32)] == &&LABEL_TYPEID_I32); \ ASSERT(dispatch_table[static_cast(TypeId::U32)] == &&LABEL_TYPEID_U32); \ ASSERT(dispatch_table[static_cast(TypeId::F32)] == &&LABEL_TYPEID_F32); \ ASSERT(dispatch_table[static_cast(TypeId::F64)] == &&LABEL_TYPEID_F64); \ ASSERT(dispatch_table[static_cast(TypeId::I64)] == &&LABEL_TYPEID_I64); \ ASSERT(dispatch_table[static_cast(TypeId::U64)] == &&LABEL_TYPEID_U64); \ ASSERT(dispatch_table[static_cast(TypeId::REFERENCE)] == &&LABEL_TYPEID_REFERENCE); \ \ uint8_t encoding = 0; \ panda_file::ShortyIterator it((METHOD)->GetShorty()); \ /* Skip the return value */ \ ARCH_COPY_METHOD_ARGS_DISPATCH \ \ LABEL_TYPEID_VOID : { \ LOG(FATAL, RUNTIME) << "Void argument is impossible"; \ UNREACHABLE(); \ } \ LABEL_TYPEID_I8 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_U8 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_I16 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_U16 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_I32 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_U32 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_F32 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_F64 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_I64 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_U64 : { \ auto v = (ARG_READER).template Read(); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_REFERENCE : { \ auto v = const_cast((ARG_READER).template ReadPtr()); \ (ARG_WRITER).template Write(v); \ ARCH_COPY_METHOD_ARGS_DISPATCH \ } \ LABEL_TYPEID_INVALID : { \ LOG(FATAL, RUNTIME) << "Invalid method's shorty, unreachable type ID"; \ UNREACHABLE(); \ } \ LABEL_TYPEID_END:; \ } while (false) template struct ExtArchTraits; #if !defined(PANDA_TARGET_ARM32_ABI_HARD) template <> struct ExtArchTraits { using SignedWordType = int32_t; using UnsignedWordType = uint32_t; static constexpr size_t NUM_GP_ARG_REGS = 4; static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits::POINTER_SIZE; static constexpr size_t NUM_FP_ARG_REGS = 0; static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits::POINTER_SIZE; static constexpr size_t GPR_SIZE = ArchTraits::POINTER_SIZE; static constexpr size_t FPR_SIZE = 0; static constexpr bool HARDFP = false; }; #else // !defined(PANDA_TARGET_ARM32_ABI_HARD) template <> struct ExtArchTraits { using SignedWordType = int32_t; using UnsignedWordType = uint32_t; static constexpr size_t NUM_GP_ARG_REGS = 4; static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits::POINTER_SIZE; static constexpr size_t NUM_FP_ARG_REGS = 16; /* s0 - s15 */ static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits::POINTER_SIZE; static constexpr size_t GPR_SIZE = ArchTraits::POINTER_SIZE; static constexpr size_t FPR_SIZE = ArchTraits::POINTER_SIZE; static constexpr bool HARDFP = true; }; #endif // !defined(PANDA_TARGET_ARM32_ABI_HARD) template <> struct ExtArchTraits { using SignedWordType = int64_t; using UnsignedWordType = uint64_t; static constexpr size_t NUM_GP_ARG_REGS = 8; static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits::POINTER_SIZE; static constexpr size_t NUM_FP_ARG_REGS = 8; static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits::POINTER_SIZE; static constexpr size_t GPR_SIZE = ArchTraits::POINTER_SIZE; static constexpr size_t FPR_SIZE = ArchTraits::POINTER_SIZE; static constexpr bool HARDFP = true; }; template <> struct ExtArchTraits { using SignedWordType = int64_t; using UnsignedWordType = uint64_t; static constexpr size_t NUM_GP_ARG_REGS = 6; static constexpr size_t GP_ARG_NUM_BYTES = NUM_GP_ARG_REGS * ArchTraits::POINTER_SIZE; static constexpr size_t NUM_FP_ARG_REGS = 8; static constexpr size_t FP_ARG_NUM_BYTES = NUM_FP_ARG_REGS * ArchTraits::POINTER_SIZE; static constexpr size_t GPR_SIZE = ArchTraits::POINTER_SIZE; static constexpr size_t FPR_SIZE = ArchTraits::POINTER_SIZE; static constexpr bool HARDFP = true; }; template inline uint8_t *AlignPtr(uint8_t *ptr) { return reinterpret_cast(RoundUp(reinterpret_cast(ptr), sizeof(T))); } template inline const uint8_t *AlignPtr(const uint8_t *ptr) { return reinterpret_cast(RoundUp(reinterpret_cast(ptr), sizeof(T))); } template typename std::enable_if::type WriteToMem(T v, uint8_t *mem) { /* * When the type is less than 4 bytes * We write 4 bytes to stack with 0 in high bytes * To avoid of unspecified behavior */ static_assert(!std::is_floating_point::value); ASSERT(reinterpret_cast(mem) % sizeof(std::uintptr_t) == 0); *reinterpret_cast(mem) = 0; mem = AlignPtr(mem); *reinterpret_cast(mem) = v; return mem; } template typename std::enable_if<(sizeof(T) >= sizeof(uint32_t)), uint8_t *>::type WriteToMem(T v, uint8_t *mem) { ASSERT(reinterpret_cast(mem) % sizeof(std::uintptr_t) == 0); mem = AlignPtr(mem); *reinterpret_cast(mem) = v; return mem; } template class ArgCounter { public: template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP, void> Count() { constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits::FPR_SIZE); fprArgSize_ = RoundUp(fprArgSize_, NUM_BYTES); if (fprArgSize_ < ExtArchTraits::FP_ARG_NUM_BYTES) { fprArgSize_ += NUM_BYTES; } else { stackSize_ = RoundUp(stackSize_, NUM_BYTES); stackSize_ += NUM_BYTES; } } template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP), void> Count() { constexpr size_t NUM_BYTES = std::max(sizeof(T), PTR_SIZE); gprArgSize_ = RoundUp(gprArgSize_, NUM_BYTES); if (gprArgSize_ < ExtArchTraits::GP_ARG_NUM_BYTES) { gprArgSize_ += NUM_BYTES; } else { stackSize_ = RoundUp(stackSize_, NUM_BYTES); stackSize_ += NUM_BYTES; } } size_t GetOnlyStackSize() const { return stackSize_; } size_t GetStackSize() const { return GetStackSpaceSize() / ArchTraits::POINTER_SIZE; } size_t GetStackSpaceSize() const { return RoundUp(ExtArchTraits::FP_ARG_NUM_BYTES + ExtArchTraits::GP_ARG_NUM_BYTES + stackSize_, 2U * ArchTraits::POINTER_SIZE); } private: static constexpr size_t PTR_SIZE = ArchTraits::POINTER_SIZE; size_t gprArgSize_ = 0; size_t fprArgSize_ = 0; size_t stackSize_ = 0; }; template class ArgReader { public: ArgReader(Span gprArgs, Span fprArgs, const uint8_t *stackArgs) : gprArgs_(gprArgs), fprArgs_(fprArgs), stackArgs_(stackArgs) { } template ALWAYS_INLINE T Read() { return *ReadPtr(); } template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP, const T *> ReadPtr() { constexpr size_t READ_BYTES = std::max(sizeof(T), ExtArchTraits::FPR_SIZE); fpArgBytesRead_ = RoundUp(fpArgBytesRead_, READ_BYTES); if (fpArgBytesRead_ < ExtArchTraits::FP_ARG_NUM_BYTES) { const T *v = reinterpret_cast(fprArgs_.data() + fpArgBytesRead_); fpArgBytesRead_ += READ_BYTES; return v; } stackArgs_ = AlignPtr(stackArgs_); const T *v = reinterpret_cast(stackArgs_); stackArgs_ += READ_BYTES; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) return v; } template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP), const T *> ReadPtr() { constexpr size_t READ_BYTES = std::max(sizeof(T), PTR_SIZE); gpArgBytesRead_ = RoundUp(gpArgBytesRead_, READ_BYTES); if (gpArgBytesRead_ < ExtArchTraits::GP_ARG_NUM_BYTES) { const T *v = reinterpret_cast(gprArgs_.data() + gpArgBytesRead_); gpArgBytesRead_ += READ_BYTES; return v; } stackArgs_ = AlignPtr(stackArgs_); const T *v = reinterpret_cast(stackArgs_); stackArgs_ += READ_BYTES; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) return v; } private: static constexpr size_t PTR_SIZE = ArchTraits::POINTER_SIZE; Span gprArgs_; Span fprArgs_; const uint8_t *stackArgs_; size_t gpArgBytesRead_ = 0; size_t fpArgBytesRead_ = 0; }; template using ExtArchTraitsWorldType = std::conditional_t, typename ExtArchTraits::SignedWordType, typename ExtArchTraits::UnsignedWordType>; template class ArgWriterBase { public: ArgWriterBase(Span gprArgs, Span fprArgs, uint8_t *stackArgs) : gprArgs_(gprArgs), fprArgs_(fprArgs), stackArgs_(stackArgs) { } ~ArgWriterBase() = default; protected: template ALWAYS_INLINE typename std::enable_if_t && sizeof(T) < ArchTraits::POINTER_SIZE, void> RegisterValueWrite(T v) { *reinterpret_cast *>(gprArgs_.data() + gpArgBytesWritten_) = v; } template ALWAYS_INLINE typename std::enable_if_t && sizeof(T) < ArchTraits::POINTER_SIZE), void> RegisterValueWrite(T v) { *reinterpret_cast(gprArgs_.data() + gpArgBytesWritten_) = v; } template void WriteNonFloatingPointValue(T v) { static_assert(!(std::is_floating_point_v && ExtArchTraits::HARDFP)); constexpr size_t WRITE_BYTES = std::max(sizeof(T), PTR_SIZE); gpArgBytesWritten_ = RoundUp(gpArgBytesWritten_, WRITE_BYTES); if (gpArgBytesWritten_ < ExtArchTraits::GP_ARG_NUM_BYTES) { ArgWriterBase::RegisterValueWrite(v); gpArgBytesWritten_ += WRITE_BYTES; } else { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) stackArgs_ = WriteToMem(v, stackArgs_) + WRITE_BYTES; } } NO_COPY_SEMANTIC(ArgWriterBase); NO_MOVE_SEMANTIC(ArgWriterBase); static constexpr size_t PTR_SIZE = ArchTraits::POINTER_SIZE; // NOLINT(misc-non-private-member-variables-in-classes) Span gprArgs_; // NOLINT(misc-non-private-member-variables-in-classes) Span fprArgs_; // NOLINT(misc-non-private-member-variables-in-classes) uint8_t *stackArgs_; // NOLINT(misc-non-private-member-variables-in-classes) size_t gpArgBytesWritten_ = 0; // NOLINT(misc-non-private-member-variables-in-classes) size_t fpArgBytesWritten_ = 0; // NOLINT(misc-non-private-member-variables-in-classes) }; template class ArgWriter : private ArgWriterBase { public: using ArgWriterBase::gprArgs_; using ArgWriterBase::fprArgs_; using ArgWriterBase::stackArgs_; using ArgWriterBase::gpArgBytesWritten_; using ArgWriterBase::fpArgBytesWritten_; using ArgWriterBase::PTR_SIZE; // NOLINTNEXTLINE(readability-non-const-parameter) ArgWriter(Span gprArgs, Span fprArgs, uint8_t *stackArgs) : ArgWriterBase(gprArgs, fprArgs, stackArgs) { } ~ArgWriter() = default; template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP, void> Write(T v) { constexpr size_t WRITE_BYTES = std::max(sizeof(T), PTR_SIZE); constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits::FPR_SIZE); if (fpArgBytesWritten_ < ExtArchTraits::FP_ARG_NUM_BYTES) { *reinterpret_cast(fprArgs_.data() + fpArgBytesWritten_) = v; fpArgBytesWritten_ += NUM_BYTES; } else { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) stackArgs_ = WriteToMem(v, stackArgs_) + WRITE_BYTES; } } template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP), void> Write(T v) { ArgWriterBase::WriteNonFloatingPointValue(v); } NO_COPY_SEMANTIC(ArgWriter); NO_MOVE_SEMANTIC(ArgWriter); }; // This class is required due to specific calling conventions in AARCH32 template <> class ArgWriter : private ArgWriterBase { public: using ArgWriterBase::gprArgs_; using ArgWriterBase::fprArgs_; using ArgWriterBase::stackArgs_; using ArgWriterBase::gpArgBytesWritten_; using ArgWriterBase::fpArgBytesWritten_; using ArgWriterBase::PTR_SIZE; // NOLINTNEXTLINE(readability-non-const-parameter) ArgWriter(Span gprArgs, Span fprArgs, uint8_t *stackArgs) : ArgWriterBase(gprArgs, fprArgs, stackArgs) { } ~ArgWriter() = default; template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP, void> Write(T v) { constexpr size_t WRITE_BYTES = std::max(sizeof(T), PTR_SIZE); if (fpArgBytesWritten_ < ExtArchTraits::FP_ARG_NUM_BYTES && (std::is_same_v || (fpArgBytesWritten_ < ExtArchTraits::FP_ARG_NUM_BYTES - sizeof(float))) && !isFloatArmStackHasBeenWritten_) { RegisterFloatingPointValueWriteArm32(v); return; } isFloatArmStackHasBeenWritten_ = true; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) stackArgs_ = WriteToMem(v, stackArgs_) + WRITE_BYTES; } template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP), void> Write(T v) { ArgWriterBase::WriteNonFloatingPointValue(v); } NO_COPY_SEMANTIC(ArgWriter); NO_MOVE_SEMANTIC(ArgWriter); private: template ALWAYS_INLINE typename std::enable_if_t<(std::is_same_v), void> RegisterFloatingPointValueWriteArm32(T v) { constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits::FPR_SIZE); if (halfEmptyRegisterOffset_ == 0) { halfEmptyRegisterOffset_ = fpArgBytesWritten_ + sizeof(float); *reinterpret_cast(fprArgs_.data() + fpArgBytesWritten_) = v; fpArgBytesWritten_ += NUM_BYTES; } else { *reinterpret_cast(fprArgs_.data() + halfEmptyRegisterOffset_) = v; if (halfEmptyRegisterOffset_ == fpArgBytesWritten_) { fpArgBytesWritten_ += NUM_BYTES; } halfEmptyRegisterOffset_ = 0; } } template ALWAYS_INLINE typename std::enable_if_t), void> RegisterFloatingPointValueWriteArm32(T v) { constexpr size_t NUM_BYTES = std::max(sizeof(T), ExtArchTraits::FPR_SIZE); fpArgBytesWritten_ = RoundUp(fpArgBytesWritten_, sizeof(T)); *reinterpret_cast(fprArgs_.data() + fpArgBytesWritten_) = v; fpArgBytesWritten_ += NUM_BYTES; } size_t halfEmptyRegisterOffset_ = 0; bool isFloatArmStackHasBeenWritten_ = false; }; class ValueWriter { public: explicit ValueWriter(PandaVector *values) : values_(values) {} ~ValueWriter() = default; template ALWAYS_INLINE typename std::enable_if_t::value, void> Write(T v) { values_->push_back(Value(*v)); } template ALWAYS_INLINE typename std::enable_if_t::value, void> Write(T v) { values_->push_back(Value(v)); } NO_COPY_SEMANTIC(ValueWriter); NO_MOVE_SEMANTIC(ValueWriter); private: PandaVector *values_; }; template class ArgReaderStack { public: explicit ArgReaderStack(const uint8_t *stackArgs) : stackArgs_(stackArgs) {} template ALWAYS_INLINE T Read() { return *ReadPtr(); } template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP, const T *> ReadPtr() { constexpr size_t READ_BYTES = sizeof(uint64_t); const T *v = reinterpret_cast(stackArgs_); stackArgs_ += READ_BYTES; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) return v; } template ALWAYS_INLINE typename std::enable_if_t && ExtArchTraits::HARDFP), const T *> ReadPtr() { constexpr size_t READ_BYTES = sizeof(uint64_t); const T *v = reinterpret_cast(stackArgs_); stackArgs_ += READ_BYTES; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) return v; } private: const uint8_t *stackArgs_; }; } // namespace ark::arch #endif // PANDA_RUNTIME_ARCH_HELPERS_H_