/* * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once namespace ams::svc::codegen::impl { template constexpr inline bool IsIntegral = std::is_integral::value; template<> constexpr inline bool IsIntegral<::ams::svc::Address> = true; template<> constexpr inline bool IsIntegral<::ams::svc::Size> = true; template constexpr inline bool IsKUserPointer = std::is_base_of::value; template constexpr inline bool IsIntegralOrUserPointer = IsIntegral || IsUserPointer || IsKUserPointer; template constexpr std::index_sequence IndexSequenceCat(std::index_sequence, std::index_sequence) { return std::index_sequence{}; } template constexpr inline std::array ConvertToArray(std::index_sequence) { return std::array{ Is... }; } template class FunctionTraits { private: template static R GetReturnTypeImpl(R(*)(A...)); template static std::tuple GetArgsImpl(R(*)(A...)); public: using ReturnType = decltype(GetReturnTypeImpl(Function)); using ArgsType = decltype(GetArgsImpl(Function)); }; enum class CodeGenerationKind { SvcInvocationToKernelProcedure, PrepareForKernelProcedureToSvcInvocation, KernelProcedureToSvcInvocation, Invalid, }; enum class ArgumentType { In, Out, InUserPointer, OutUserPointer, Invalid, }; template constexpr inline ArgumentType GetArgumentType = [] { static_assert(!std::is_reference::value, "SVC ABI: Reference types not allowed."); static_assert(sizeof(T) <= sizeof(uint64_t), "SVC ABI: Type too large"); if constexpr (std::is_pointer::value) { static_assert(!std::is_const::type>::value, "SVC ABI: Output (T*) must not be const"); return ArgumentType::Out; } else if constexpr (IsUserPointer || IsKUserPointer) { if constexpr (T::IsInput) { return ArgumentType::InUserPointer; } else { return ArgumentType::OutUserPointer; } } else { return ArgumentType::In; } }(); template struct AbiType { static constexpr size_t RegisterSize = RS; static constexpr size_t RegisterCount = RC; static constexpr size_t ArgumentRegisterCount = ARC; static constexpr size_t PointerSize = PC; template static constexpr size_t GetSize() { if constexpr (std::is_same::value || std::is_same::value || IsUserPointer || IsKUserPointer) { return PointerSize; } else if constexpr(std::is_pointer::value) { /* Out parameter. */ return GetSize::type>(); } else if constexpr (std::is_same::value) { return 0; } else { return sizeof(T); } } template static constexpr inline size_t Size = GetSize(); }; using Aarch64Lp64Abi = AbiType<8, 8, 8, 8>; using Aarch64Ilp32Abi = AbiType<8, 8, 8, 4>; using Aarch32Ilp32Abi = AbiType<4, 4, 4, 4>; using Aarch64SvcInvokeAbi = AbiType<8, 8, 8, 8>; using Aarch32SvcInvokeAbi = AbiType<4, 8, 4, 4>; struct Abi { size_t register_size; size_t register_count; size_t pointer_size; template static constexpr Abi Convert() { return { AbiType::RegisterSize, AbiType::RegisterCount, AbiType::PointerSize }; } }; template constexpr inline bool IsPassedByPointer = [] { if (GetArgumentType != ArgumentType::In) { return true; } return (!IsIntegral && AbiType::template Size > AbiType::RegisterSize); }(); template class RegisterAllocator { private: std::array map; public: constexpr explicit RegisterAllocator() : map() { /* ... */ } constexpr bool IsAllocated(size_t i) const { return this->map[i]; } constexpr bool IsFree(size_t i) const { return !this->IsAllocated(i); } constexpr void Allocate(size_t i) { if (this->IsAllocated(i)) { std::abort(); } this->map[i] = true; } constexpr bool TryAllocate(size_t i) { if (this->IsAllocated(i)) { return false; } this->map[i] = true; return true; } constexpr size_t AllocateFirstFree() { for (size_t i = 0; i < N; i++) { if (!this->IsAllocated(i)) { this->map[i] = true; return i; } } std::abort(); } constexpr void Free(size_t i) { if (!this->IsAllocated(i)) { std::abort(); } this->map[i] = false; } constexpr size_t GetRegisterCount() const { return N; } }; }