diff options
20 files changed, 415 insertions, 242 deletions
| diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 2db28dcf0..ab05788d7 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -284,13 +284,18 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {  std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {      std::vector<u8> buffer; -    const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; +    const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && +                           BufferDescriptorA()[buffer_index].Size()};      auto& memory = Core::System::GetInstance().Memory();      if (is_buffer_a) { +        ASSERT_MSG(BufferDescriptorA().size() > buffer_index, +                   "BufferDescriptorA invalid buffer_index {}", buffer_index);          buffer.resize(BufferDescriptorA()[buffer_index].Size());          memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());      } else { +        ASSERT_MSG(BufferDescriptorX().size() > buffer_index, +                   "BufferDescriptorX invalid buffer_index {}", buffer_index);          buffer.resize(BufferDescriptorX()[buffer_index].Size());          memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());      } @@ -305,7 +310,8 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,          return 0;      } -    const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; +    const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && +                           BufferDescriptorB()[buffer_index].Size()};      const std::size_t buffer_size{GetWriteBufferSize(buffer_index)};      if (size > buffer_size) {          LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, @@ -315,8 +321,16 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,      auto& memory = Core::System::GetInstance().Memory();      if (is_buffer_b) { +        ASSERT_MSG(BufferDescriptorB().size() > buffer_index, +                   "BufferDescriptorB invalid buffer_index {}", buffer_index); +        ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size, +                   "BufferDescriptorB buffer_index {} is not large enough", buffer_index);          memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);      } else { +        ASSERT_MSG(BufferDescriptorC().size() > buffer_index, +                   "BufferDescriptorC invalid buffer_index {}", buffer_index); +        ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size, +                   "BufferDescriptorC buffer_index {} is not large enough", buffer_index);          memory.WriteBlock(BufferDescriptorC()[buffer_index].Address(), buffer, size);      } @@ -324,15 +338,35 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,  }  std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { -    const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; -    return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() -                       : BufferDescriptorX()[buffer_index].Size(); +    const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && +                           BufferDescriptorA()[buffer_index].Size()}; +    if (is_buffer_a) { +        ASSERT_MSG(BufferDescriptorA().size() > buffer_index, +                   "BufferDescriptorA invalid buffer_index {}", buffer_index); +        ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0, +                   "BufferDescriptorA buffer_index {} is empty", buffer_index); +        return BufferDescriptorA()[buffer_index].Size(); +    } else { +        ASSERT_MSG(BufferDescriptorX().size() > buffer_index, +                   "BufferDescriptorX invalid buffer_index {}", buffer_index); +        ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0, +                   "BufferDescriptorX buffer_index {} is empty", buffer_index); +        return BufferDescriptorX()[buffer_index].Size(); +    }  }  std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { -    const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; -    return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() -                       : BufferDescriptorC()[buffer_index].Size(); +    const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && +                           BufferDescriptorB()[buffer_index].Size()}; +    if (is_buffer_b) { +        ASSERT_MSG(BufferDescriptorB().size() > buffer_index, +                   "BufferDescriptorB invalid buffer_index {}", buffer_index); +        return BufferDescriptorB()[buffer_index].Size(); +    } else { +        ASSERT_MSG(BufferDescriptorC().size() > buffer_index, +                   "BufferDescriptorC invalid buffer_index {}", buffer_index); +        return BufferDescriptorC()[buffer_index].Size(); +    }  }  std::string HLERequestContext::Description() const { diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 1d99bf7a2..9cae5c73d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1863,10 +1863,14 @@ static ResultCode CreateTransferMemory(Core::System& system, Handle* handle, VAd      }      auto& kernel = system.Kernel(); -    auto transfer_mem_handle = TransferMemory::Create(kernel, addr, size, perms); +    auto transfer_mem_handle = TransferMemory::Create(kernel, system.Memory(), addr, size, perms); + +    if (const auto reserve_result{transfer_mem_handle->Reserve()}; reserve_result.IsError()) { +        return reserve_result; +    }      auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); -    const auto result = handle_table.Create(std::move(transfer_mem_handle)); +    const auto result{handle_table.Create(std::move(transfer_mem_handle))};      if (result.Failed()) {          return result.Code();      } diff --git a/src/core/hle/kernel/transfer_memory.cpp b/src/core/hle/kernel/transfer_memory.cpp index f0e73f57b..f2d3f8b49 100644 --- a/src/core/hle/kernel/transfer_memory.cpp +++ b/src/core/hle/kernel/transfer_memory.cpp @@ -8,15 +8,23 @@  #include "core/hle/kernel/shared_memory.h"  #include "core/hle/kernel/transfer_memory.h"  #include "core/hle/result.h" +#include "core/memory.h"  namespace Kernel { -TransferMemory::TransferMemory(KernelCore& kernel) : Object{kernel} {} -TransferMemory::~TransferMemory() = default; +TransferMemory::TransferMemory(KernelCore& kernel, Memory::Memory& memory) +    : Object{kernel}, memory{memory} {} -std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_address, -                                                       u64 size, MemoryPermission permissions) { -    std::shared_ptr<TransferMemory> transfer_memory{std::make_shared<TransferMemory>(kernel)}; +TransferMemory::~TransferMemory() { +    // Release memory region when transfer memory is destroyed +    Reset(); +} + +std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, Memory::Memory& memory, +                                                       VAddr base_address, u64 size, +                                                       MemoryPermission permissions) { +    std::shared_ptr<TransferMemory> transfer_memory{ +        std::make_shared<TransferMemory>(kernel, memory)};      transfer_memory->base_address = base_address;      transfer_memory->memory_size = size; @@ -27,7 +35,7 @@ std::shared_ptr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr  }  const u8* TransferMemory::GetPointer() const { -    return backing_block.get()->data(); +    return memory.GetPointer(base_address);  }  u64 TransferMemory::GetSize() const { @@ -62,6 +70,52 @@ ResultCode TransferMemory::MapMemory(VAddr address, u64 size, MemoryPermission p      return RESULT_SUCCESS;  } +ResultCode TransferMemory::Reserve() { +    auto& vm_manager{owner_process->VMManager()}; +    const auto check_range_result{vm_manager.CheckRangeState( +        base_address, memory_size, MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, +        MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::All, +        VMAPermission::ReadWrite, MemoryAttribute::Mask, MemoryAttribute::None, +        MemoryAttribute::IpcAndDeviceMapped)}; + +    if (check_range_result.Failed()) { +        return check_range_result.Code(); +    } + +    auto [state_, permissions_, attribute] = *check_range_result; + +    if (const auto result{vm_manager.ReprotectRange( +            base_address, memory_size, SharedMemory::ConvertPermissions(owner_permissions))}; +        result.IsError()) { +        return result; +    } + +    return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, +                                         attribute | MemoryAttribute::Locked); +} + +ResultCode TransferMemory::Reset() { +    auto& vm_manager{owner_process->VMManager()}; +    if (const auto result{vm_manager.CheckRangeState( +            base_address, memory_size, +            MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, +            MemoryState::FlagTransfer | MemoryState::FlagMemoryPoolAllocated, VMAPermission::None, +            VMAPermission::None, MemoryAttribute::Mask, MemoryAttribute::Locked, +            MemoryAttribute::IpcAndDeviceMapped)}; +        result.Failed()) { +        return result.Code(); +    } + +    if (const auto result{ +            vm_manager.ReprotectRange(base_address, memory_size, VMAPermission::ReadWrite)}; +        result.IsError()) { +        return result; +    } + +    return vm_manager.SetMemoryAttribute(base_address, memory_size, MemoryAttribute::Mask, +                                         MemoryAttribute::None); +} +  ResultCode TransferMemory::UnmapMemory(VAddr address, u64 size) {      if (memory_size != size) {          return ERR_INVALID_SIZE; diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h index 0a6e15d18..6e388536a 100644 --- a/src/core/hle/kernel/transfer_memory.h +++ b/src/core/hle/kernel/transfer_memory.h @@ -11,6 +11,10 @@  union ResultCode; +namespace Memory { +class Memory; +} +  namespace Kernel {  class KernelCore; @@ -26,12 +30,13 @@ enum class MemoryPermission : u32;  ///  class TransferMemory final : public Object {  public: -    explicit TransferMemory(KernelCore& kernel); +    explicit TransferMemory(KernelCore& kernel, Memory::Memory& memory);      ~TransferMemory() override;      static constexpr HandleType HANDLE_TYPE = HandleType::TransferMemory; -    static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, VAddr base_address, u64 size, +    static std::shared_ptr<TransferMemory> Create(KernelCore& kernel, Memory::Memory& memory, +                                                  VAddr base_address, u64 size,                                                    MemoryPermission permissions);      TransferMemory(const TransferMemory&) = delete; @@ -80,6 +85,14 @@ public:      ///      ResultCode UnmapMemory(VAddr address, u64 size); +    /// Reserves the region to be used for the transfer memory, called after the transfer memory is +    /// created. +    ResultCode Reserve(); + +    /// Resets the region previously used for the transfer memory, called after the transfer memory +    /// is closed. +    ResultCode Reset(); +  private:      /// Memory block backing this instance.      std::shared_ptr<PhysicalMemory> backing_block; @@ -98,6 +111,8 @@ private:      /// Whether or not this transfer memory instance has mapped memory.      bool is_mapped = false; + +    Memory::Memory& memory;  };  } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 0b3500fce..024c22901 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -544,7 +544,8 @@ MemoryInfo VMManager::QueryMemory(VAddr address) const {  ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,                                           MemoryAttribute attribute) { -    constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped; +    constexpr auto ignore_mask = +        MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped | MemoryAttribute::Locked;      constexpr auto attribute_mask = ~ignore_mask;      const auto result = CheckRangeState( diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 850a7ebc3..90b4b006a 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -98,6 +98,8 @@ enum class MemoryAttribute : u32 {      DeviceMapped = 4,      /// Uncached memory      Uncached = 8, + +    IpcAndDeviceMapped = LockedForIPC | DeviceMapped,  };  constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) { @@ -654,6 +656,35 @@ public:      /// is scheduled.      Common::PageTable page_table{Memory::PAGE_BITS}; +    using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>; + +    /// Checks if an address range adheres to the specified states provided. +    /// +    /// @param address         The starting address of the address range. +    /// @param size            The size of the address range. +    /// @param state_mask      The memory state mask. +    /// @param state           The state to compare the individual VMA states against, +    ///                        which is done in the form of: (vma.state & state_mask) != state. +    /// @param permission_mask The memory permissions mask. +    /// @param permissions     The permission to compare the individual VMA permissions against, +    ///                        which is done in the form of: +    ///                        (vma.permission & permission_mask) != permission. +    /// @param attribute_mask  The memory attribute mask. +    /// @param attribute       The memory attributes to compare the individual VMA attributes +    ///                        against, which is done in the form of: +    ///                        (vma.attributes & attribute_mask) != attribute. +    /// @param ignore_mask     The memory attributes to ignore during the check. +    /// +    /// @returns If successful, returns a tuple containing the memory attributes +    ///          (with ignored bits specified by ignore_mask unset), memory permissions, and +    ///          memory state across the memory range. +    /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE. +    /// +    CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, +                                 VMAPermission permission_mask, VMAPermission permissions, +                                 MemoryAttribute attribute_mask, MemoryAttribute attribute, +                                 MemoryAttribute ignore_mask) const; +  private:      using VMAIter = VMAMap::iterator; @@ -707,35 +738,6 @@ private:      /// Clears out the page table      void ClearPageTable(); -    using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>; - -    /// Checks if an address range adheres to the specified states provided. -    /// -    /// @param address         The starting address of the address range. -    /// @param size            The size of the address range. -    /// @param state_mask      The memory state mask. -    /// @param state           The state to compare the individual VMA states against, -    ///                        which is done in the form of: (vma.state & state_mask) != state. -    /// @param permission_mask The memory permissions mask. -    /// @param permissions     The permission to compare the individual VMA permissions against, -    ///                        which is done in the form of: -    ///                        (vma.permission & permission_mask) != permission. -    /// @param attribute_mask  The memory attribute mask. -    /// @param attribute       The memory attributes to compare the individual VMA attributes -    ///                        against, which is done in the form of: -    ///                        (vma.attributes & attribute_mask) != attribute. -    /// @param ignore_mask     The memory attributes to ignore during the check. -    /// -    /// @returns If successful, returns a tuple containing the memory attributes -    ///          (with ignored bits specified by ignore_mask unset), memory permissions, and -    ///          memory state across the memory range. -    /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE. -    /// -    CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state, -                                 VMAPermission permission_mask, VMAPermission permissions, -                                 MemoryAttribute attribute_mask, MemoryAttribute attribute, -                                 MemoryAttribute ignore_mask) const; -      /// Gets the amount of memory currently mapped (state != Unmapped) in a range.      ResultVal<std::size_t> SizeOfAllocatedVMAsInRange(VAddr address, std::size_t size) const; diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index a0c806e8f..1838260fd 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -50,17 +50,8 @@ std::shared_ptr<Thread> WaitObject::GetHighestPriorityReadyThread() const {          if (ShouldWait(thread.get()))              continue; -        // A thread is ready to run if it's either in ThreadStatus::WaitSynch -        // and the rest of the objects it is waiting on are ready. -        bool ready_to_run = true; -        if (thread_status == ThreadStatus::WaitSynch) { -            ready_to_run = thread->AllWaitObjectsReady(); -        } - -        if (ready_to_run) { -            candidate = thread.get(); -            candidate_priority = thread->GetPriority(); -        } +        candidate = thread.get(); +        candidate_priority = thread->GetPriority();      }      return SharedFrom(candidate); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 95aa5d23d..c1550013a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -709,8 +709,34 @@ void ICommonStateGetter::SetCpuBoostMode(Kernel::HLERequestContext& ctx) {      apm_sys->SetCpuBoostMode(ctx);  } -IStorage::IStorage(std::vector<u8> buffer) -    : ServiceFramework("IStorage"), buffer(std::move(buffer)) { +IStorageImpl::~IStorageImpl() = default; + +class StorageDataImpl final : public IStorageImpl { +public: +    explicit StorageDataImpl(std::vector<u8>&& buffer) : buffer{std::move(buffer)} {} + +    std::vector<u8>& GetData() override { +        return buffer; +    } + +    const std::vector<u8>& GetData() const override { +        return buffer; +    } + +    std::size_t GetSize() const override { +        return buffer.size(); +    } + +private: +    std::vector<u8> buffer; +}; + +IStorage::IStorage(std::vector<u8>&& buffer) +    : ServiceFramework("IStorage"), impl{std::make_shared<StorageDataImpl>(std::move(buffer))} { +    Register(); +} + +void IStorage::Register() {      // clang-format off          static const FunctionInfo functions[] = {              {0, &IStorage::Open, "Open"}, @@ -723,8 +749,13 @@ IStorage::IStorage(std::vector<u8> buffer)  IStorage::~IStorage() = default; -const std::vector<u8>& IStorage::GetData() const { -    return buffer; +void IStorage::Open(Kernel::HLERequestContext& ctx) { +    LOG_DEBUG(Service_AM, "called"); + +    IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + +    rb.Push(RESULT_SUCCESS); +    rb.PushIpcInterface<IStorageAccessor>(*this);  }  void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { @@ -825,17 +856,16 @@ private:      void PopOutData(Kernel::HLERequestContext& ctx) {          LOG_DEBUG(Service_AM, "called"); -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -          const auto storage = applet->GetBroker().PopNormalDataToGame();          if (storage == nullptr) {              LOG_ERROR(Service_AM,                        "storage is a nullptr. There is no data in the current normal channel"); - +            IPC::ResponseBuilder rb{ctx, 2};              rb.Push(ERR_NO_DATA_IN_CHANNEL);              return;          } +        IPC::ResponseBuilder rb{ctx, 2, 0, 1};          rb.Push(RESULT_SUCCESS);          rb.PushIpcInterface<IStorage>(std::move(*storage));      } @@ -857,17 +887,16 @@ private:      void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {          LOG_DEBUG(Service_AM, "called"); -        IPC::ResponseBuilder rb{ctx, 2, 0, 1}; -          const auto storage = applet->GetBroker().PopInteractiveDataToGame();          if (storage == nullptr) {              LOG_ERROR(Service_AM,                        "storage is a nullptr. There is no data in the current interactive channel"); - +            IPC::ResponseBuilder rb{ctx, 2};              rb.Push(ERR_NO_DATA_IN_CHANNEL);              return;          } +        IPC::ResponseBuilder rb{ctx, 2, 0, 1};          rb.Push(RESULT_SUCCESS);          rb.PushIpcInterface<IStorage>(std::move(*storage));      } @@ -891,15 +920,6 @@ private:      std::shared_ptr<Applets::Applet> applet;  }; -void IStorage::Open(Kernel::HLERequestContext& ctx) { -    LOG_DEBUG(Service_AM, "called"); - -    IPC::ResponseBuilder rb{ctx, 2, 0, 1}; - -    rb.Push(RESULT_SUCCESS); -    rb.PushIpcInterface<IStorageAccessor>(*this); -} -  IStorageAccessor::IStorageAccessor(IStorage& storage)      : ServiceFramework("IStorageAccessor"), backing(storage) {      // clang-format off @@ -921,7 +941,7 @@ void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {      IPC::ResponseBuilder rb{ctx, 4};      rb.Push(RESULT_SUCCESS); -    rb.Push(static_cast<u64>(backing.buffer.size())); +    rb.Push(static_cast<u64>(backing.GetSize()));  }  void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) { @@ -932,17 +952,17 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size()); -    if (data.size() > backing.buffer.size() - offset) { +    if (data.size() > backing.GetSize() - offset) {          LOG_ERROR(Service_AM,                    "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}", -                  backing.buffer.size(), data.size(), offset); +                  backing.GetSize(), data.size(), offset);          IPC::ResponseBuilder rb{ctx, 2};          rb.Push(ERR_SIZE_OUT_OF_BOUNDS);          return;      } -    std::memcpy(backing.buffer.data() + offset, data.data(), data.size()); +    std::memcpy(backing.GetData().data() + offset, data.data(), data.size());      IPC::ResponseBuilder rb{ctx, 2};      rb.Push(RESULT_SUCCESS); @@ -956,16 +976,16 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {      LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); -    if (size > backing.buffer.size() - offset) { +    if (size > backing.GetSize() - offset) {          LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}", -                  backing.buffer.size(), size, offset); +                  backing.GetSize(), size, offset);          IPC::ResponseBuilder rb{ctx, 2};          rb.Push(ERR_SIZE_OUT_OF_BOUNDS);          return;      } -    ctx.WriteBuffer(backing.buffer.data() + offset, size); +    ctx.WriteBuffer(backing.GetData().data() + offset, size);      IPC::ResponseBuilder rb{ctx, 2};      rb.Push(RESULT_SUCCESS); @@ -1031,7 +1051,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex      rp.SetCurrentOffset(3);      const auto handle{rp.Pop<Kernel::Handle>()}; -    const auto transfer_mem = +    auto transfer_mem =          system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);      if (transfer_mem == nullptr) { @@ -1047,7 +1067,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex      IPC::ResponseBuilder rb{ctx, 2, 0, 1};      rb.Push(RESULT_SUCCESS); -    rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory))); +    rb.PushIpcInterface<IStorage>(std::move(memory));  }  IApplicationFunctions::IApplicationFunctions(Core::System& system_) @@ -1189,13 +1209,11 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {          u64 build_id{};          std::memcpy(&build_id, build_id_full.data(), sizeof(u64)); -        const auto data = -            backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id}); - +        auto data = backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});          if (data.has_value()) {              IPC::ResponseBuilder rb{ctx, 2, 0, 1};              rb.Push(RESULT_SUCCESS); -            rb.PushIpcInterface<AM::IStorage>(*data); +            rb.PushIpcInterface<IStorage>(std::move(*data));              launch_popped_application_specific = true;              return;          } @@ -1218,7 +1236,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {          std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));          std::memcpy(buffer.data(), ¶ms, buffer.size()); -        rb.PushIpcInterface<AM::IStorage>(buffer); +        rb.PushIpcInterface<IStorage>(std::move(buffer));          launch_popped_account_preselect = true;          return;      } diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 448817be9..0b9a4332d 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -12,7 +12,8 @@  namespace Kernel {  class KernelCore; -} +class TransferMemory; +} // namespace Kernel  namespace Service::NVFlinger {  class NVFlinger; @@ -188,19 +189,36 @@ private:      std::shared_ptr<AppletMessageQueue> msg_queue;  }; +class IStorageImpl { +public: +    virtual ~IStorageImpl(); +    virtual std::vector<u8>& GetData() = 0; +    virtual const std::vector<u8>& GetData() const = 0; +    virtual std::size_t GetSize() const = 0; +}; +  class IStorage final : public ServiceFramework<IStorage> {  public: -    explicit IStorage(std::vector<u8> buffer); +    explicit IStorage(std::vector<u8>&& buffer);      ~IStorage() override; -    const std::vector<u8>& GetData() const; +    std::vector<u8>& GetData() { +        return impl->GetData(); +    } + +    const std::vector<u8>& GetData() const { +        return impl->GetData(); +    } + +    std::size_t GetSize() const { +        return impl->GetSize(); +    }  private: +    void Register();      void Open(Kernel::HLERequestContext& ctx); -    std::vector<u8> buffer; - -    friend class IStorageAccessor; +    std::shared_ptr<IStorageImpl> impl;  };  class IStorageAccessor final : public ServiceFramework<IStorageAccessor> { diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp index 92f995f8f..3e97ba218 100644 --- a/src/core/hle/service/am/applets/applets.cpp +++ b/src/core/hle/service/am/applets/applets.cpp @@ -56,6 +56,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {      auto out = std::move(out_channel.front());      out_channel.pop_front(); +    pop_out_data_event.writable->Clear();      return out;  } @@ -74,6 +75,7 @@ std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {      auto out = std::move(out_interactive_channel.front());      out_interactive_channel.pop_front(); +    pop_interactive_out_data_event.writable->Clear();      return out;  } diff --git a/src/core/hle/service/am/applets/error.cpp b/src/core/hle/service/am/applets/error.cpp index eab0d42c9..e6c4e8b87 100644 --- a/src/core/hle/service/am/applets/error.cpp +++ b/src/core/hle/service/am/applets/error.cpp @@ -186,7 +186,7 @@ void Error::Execute() {  void Error::DisplayCompleted() {      complete = true; -    broker.PushNormalDataFromApplet(IStorage{{}}); +    broker.PushNormalDataFromApplet(IStorage{std::vector<u8>{}});      broker.SignalStateChanged();  } diff --git a/src/core/hle/service/am/applets/general_backend.cpp b/src/core/hle/service/am/applets/general_backend.cpp index 328438a1d..fe8400a15 100644 --- a/src/core/hle/service/am/applets/general_backend.cpp +++ b/src/core/hle/service/am/applets/general_backend.cpp @@ -148,7 +148,7 @@ void Auth::AuthFinished(bool successful) {      std::vector<u8> out(sizeof(Return));      std::memcpy(out.data(), &return_, sizeof(Return)); -    broker.PushNormalDataFromApplet(IStorage{out}); +    broker.PushNormalDataFromApplet(IStorage{std::move(out)});      broker.SignalStateChanged();  } @@ -198,7 +198,7 @@ void PhotoViewer::Execute() {  }  void PhotoViewer::ViewFinished() { -    broker.PushNormalDataFromApplet(IStorage{{}}); +    broker.PushNormalDataFromApplet(IStorage{std::vector<u8>{}});      broker.SignalStateChanged();  } diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp index 3eba696ca..91d00f72a 100644 --- a/src/core/hle/service/am/applets/profile_select.cpp +++ b/src/core/hle/service/am/applets/profile_select.cpp @@ -50,7 +50,7 @@ void ProfileSelect::ExecuteInteractive() {  void ProfileSelect::Execute() {      if (complete) { -        broker.PushNormalDataFromApplet(IStorage{final_data}); +        broker.PushNormalDataFromApplet(IStorage{std::move(final_data)});          return;      } @@ -71,7 +71,7 @@ void ProfileSelect::SelectionComplete(std::optional<Common::UUID> uuid) {      final_data = std::vector<u8>(sizeof(UserSelectionOutput));      std::memcpy(final_data.data(), &output, final_data.size()); -    broker.PushNormalDataFromApplet(IStorage{final_data}); +    broker.PushNormalDataFromApplet(IStorage{std::move(final_data)});      broker.SignalStateChanged();  } diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp index 748559cd0..964c67202 100644 --- a/src/core/hle/service/am/applets/software_keyboard.cpp +++ b/src/core/hle/service/am/applets/software_keyboard.cpp @@ -102,7 +102,8 @@ void SoftwareKeyboard::ExecuteInteractive() {  void SoftwareKeyboard::Execute() {      if (complete) { -        broker.PushNormalDataFromApplet(IStorage{final_data}); +        broker.PushNormalDataFromApplet(IStorage{std::move(final_data)}); +        broker.SignalStateChanged();          return;      } @@ -119,7 +120,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {          std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);          if (config.utf_8) { -            const u64 size = text->size() + 8; +            const u64 size = text->size() + sizeof(u64);              const auto new_text = Common::UTF16ToUTF8(*text);              std::memcpy(output_sub.data(), &size, sizeof(u64)); @@ -130,7 +131,7 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {              std::memcpy(output_main.data() + 4, new_text.data(),                          std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));          } else { -            const u64 size = text->size() * 2 + 8; +            const u64 size = text->size() * 2 + sizeof(u64);              std::memcpy(output_sub.data(), &size, sizeof(u64));              std::memcpy(output_sub.data() + 8, text->data(),                          std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8)); @@ -144,15 +145,15 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {          final_data = output_main;          if (complete) { -            broker.PushNormalDataFromApplet(IStorage{output_main}); +            broker.PushNormalDataFromApplet(IStorage{std::move(output_main)});              broker.SignalStateChanged();          } else { -            broker.PushInteractiveDataFromApplet(IStorage{output_sub}); +            broker.PushInteractiveDataFromApplet(IStorage{std::move(output_sub)});          }      } else {          output_main[0] = 1;          complete = true; -        broker.PushNormalDataFromApplet(IStorage{output_main}); +        broker.PushNormalDataFromApplet(IStorage{std::move(output_main)});          broker.SignalStateChanged();      }  } diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index 5546ef6e8..05d6b3a19 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp @@ -284,7 +284,7 @@ void WebBrowser::Finalize() {      std::vector<u8> data(sizeof(WebCommonReturnValue));      std::memcpy(data.data(), &out, sizeof(WebCommonReturnValue)); -    broker.PushNormalDataFromApplet(IStorage{data}); +    broker.PushNormalDataFromApplet(IStorage{std::move(data)});      broker.SignalStateChanged();      if (!temporary_dir.empty() && FileUtil::IsDirectory(temporary_dir)) { diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 5eb26caf8..8f1be0e48 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -50,16 +50,16 @@ private:          IPC::RequestParser rp{ctx};          const auto process_id = rp.PopRaw<u64>(); -        const auto data1 = ctx.ReadBuffer(0); -        const auto data2 = ctx.ReadBuffer(1); +        std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; +        if (Type == Core::Reporter::PlayReportType::New) { +            data.emplace_back(ctx.ReadBuffer(1)); +        } -        LOG_DEBUG(Service_PREPO, -                  "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}", -                  static_cast<u8>(Type), process_id, data1.size(), data2.size()); +        LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}", +                  static_cast<u8>(Type), process_id, data[0].size());          const auto& reporter{system.GetReporter()}; -        reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, -                                process_id); +        reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id);          IPC::ResponseBuilder rb{ctx, 2};          rb.Push(RESULT_SUCCESS); @@ -70,19 +70,19 @@ private:          IPC::RequestParser rp{ctx};          const auto user_id = rp.PopRaw<u128>();          const auto process_id = rp.PopRaw<u64>(); - -        const auto data1 = ctx.ReadBuffer(0); -        const auto data2 = ctx.ReadBuffer(1); +        std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; +        if (Type == Core::Reporter::PlayReportType::New) { +            data.emplace_back(ctx.ReadBuffer(1)); +        }          LOG_DEBUG(              Service_PREPO, -            "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}, " -            "data2_size={:016X}", -            static_cast<u8>(Type), user_id[1], user_id[0], process_id, data1.size(), data2.size()); +            "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}", +            static_cast<u8>(Type), user_id[1], user_id[0], process_id, data[0].size());          const auto& reporter{system.GetReporter()}; -        reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, -                                process_id, user_id); +        reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id, +                                user_id);          IPC::ResponseBuilder rb{ctx, 2};          rb.Push(RESULT_SUCCESS); diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 81b6d9eff..402869fde 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -624,6 +624,19 @@ enum class ShuffleOperation : u64 {      Bfly = 3, // shuffleXorNV  }; +enum class ShfType : u64 { +    Bits32 = 0, +    U64 = 2, +    S64 = 3, +}; + +enum class ShfXmode : u64 { +    None = 0, +    HI = 1, +    X = 2, +    XHI = 3, +}; +  union Instruction {      constexpr Instruction& operator=(const Instruction& instr) {          value = instr.value; @@ -776,6 +789,13 @@ union Instruction {      } shr;      union { +        BitField<37, 2, ShfType> type; +        BitField<48, 2, ShfXmode> xmode; +        BitField<50, 1, u64> wrap; +        BitField<20, 6, u64> immediate; +    } shf; + +    union {          BitField<39, 5, u64> shift_amount;          BitField<48, 1, u64> negate_b;          BitField<49, 1, u64> negate_a; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 362942e09..46a7433ea 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -248,9 +248,6 @@ void RasterizerOpenGL::SetupVertexInstances(GLuint vao) {  }  GLintptr RasterizerOpenGL::SetupIndexBuffer() { -    if (accelerate_draw != AccelDraw::Indexed) { -        return 0; -    }      MICROPROFILE_SCOPE(OpenGL_Index);      const auto& regs = system.GPU().Maxwell3D().regs;      const std::size_t size = CalculateIndexBufferSize(); @@ -546,7 +543,8 @@ void RasterizerOpenGL::Clear() {      }  } -void RasterizerOpenGL::DrawPrelude() { +void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { +    MICROPROFILE_SCOPE(OpenGL_Drawing);      auto& gpu = system.GPU().Maxwell3D();      SyncRasterizeEnable(state); @@ -567,9 +565,6 @@ void RasterizerOpenGL::DrawPrelude() {      buffer_cache.Acquire(); -    // Draw the vertex batch -    const bool is_indexed = accelerate_draw == AccelDraw::Indexed; -      std::size_t buffer_size = CalculateVertexArraysSize();      // Add space for index buffer @@ -596,7 +591,11 @@ void RasterizerOpenGL::DrawPrelude() {      // Upload vertex and index data.      SetupVertexBuffer(vao);      SetupVertexInstances(vao); -    index_buffer_offset = SetupIndexBuffer(); + +    GLintptr index_buffer_offset; +    if (is_indexed) { +        index_buffer_offset = SetupIndexBuffer(); +    }      // Prepare packed bindings.      bind_ubo_pushbuffer.Setup(); @@ -630,6 +629,7 @@ void RasterizerOpenGL::DrawPrelude() {          // As all cached buffers are invalidated, we need to recheck their state.          gpu.dirty.ResetVertexArrays();      } +    gpu.dirty.memory_general = false;      shader_program_manager->ApplyTo(state);      state.Apply(); @@ -637,106 +637,33 @@ void RasterizerOpenGL::DrawPrelude() {      if (texture_cache.TextureBarrier()) {          glTextureBarrier();      } -} - -struct DrawParams { -    bool is_indexed{}; -    bool is_instanced{}; -    GLenum primitive_mode{}; -    GLint count{}; -    GLint base_vertex{}; - -    // Indexed settings -    GLenum index_format{}; -    GLintptr index_buffer_offset{}; - -    // Instanced setting -    GLint num_instances{}; -    GLint base_instance{}; - -    void DispatchDraw() { -        if (is_indexed) { -            const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); -            if (is_instanced) { -                glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, -                                                              index_buffer_ptr, num_instances, -                                                              base_vertex, base_instance); -            } else { -                glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, -                                         base_vertex); -            } -        } else { -            if (is_instanced) { -                glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances, -                                                  base_instance); -            } else { -                glDrawArrays(primitive_mode, base_vertex, count); -            } -        } -    } -}; - -bool RasterizerOpenGL::DrawBatch(bool is_indexed) { -    accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; -    MICROPROFILE_SCOPE(OpenGL_Drawing); - -    DrawPrelude(); - -    auto& maxwell3d = system.GPU().Maxwell3D(); -    const auto& regs = maxwell3d.regs; -    const auto current_instance = maxwell3d.state.current_instance; -    DrawParams draw_call{}; -    draw_call.is_indexed = is_indexed; -    draw_call.num_instances = static_cast<GLint>(1); -    draw_call.base_instance = static_cast<GLint>(current_instance); -    draw_call.is_instanced = current_instance > 0; -    draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); -    if (draw_call.is_indexed) { -        draw_call.count = static_cast<GLint>(regs.index_array.count); -        draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); -        draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); -        draw_call.index_buffer_offset = index_buffer_offset; +    const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance); +    const GLsizei num_instances = +        static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1); +    if (is_indexed) { +        const GLenum index_format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format); +        const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base); +        const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count); +        glDrawElementsInstancedBaseVertexBaseInstance( +            primitive_mode, num_vertices, index_format, +            reinterpret_cast<const void*>(index_buffer_offset), num_instances, base_vertex, +            base_instance);      } else { -        draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); -        draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); +        const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first); +        const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count); +        glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, num_vertices, num_instances, +                                          base_instance);      } -    draw_call.DispatchDraw(); +} -    maxwell3d.dirty.memory_general = false; -    accelerate_draw = AccelDraw::Disabled; +bool RasterizerOpenGL::DrawBatch(bool is_indexed) { +    Draw(is_indexed, false);      return true;  }  bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) { -    accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; - -    MICROPROFILE_SCOPE(OpenGL_Drawing); - -    DrawPrelude(); - -    auto& maxwell3d = system.GPU().Maxwell3D(); -    const auto& regs = maxwell3d.regs; -    const auto& draw_setup = maxwell3d.mme_draw; -    DrawParams draw_call{}; -    draw_call.is_indexed = is_indexed; -    draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count); -    draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance); -    draw_call.is_instanced = draw_setup.instance_count > 1; -    draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); -    if (draw_call.is_indexed) { -        draw_call.count = static_cast<GLint>(regs.index_array.count); -        draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base); -        draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); -        draw_call.index_buffer_offset = index_buffer_offset; -    } else { -        draw_call.count = static_cast<GLint>(regs.vertex_buffer.count); -        draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first); -    } -    draw_call.DispatchDraw(); - -    maxwell3d.dirty.memory_general = false; -    accelerate_draw = AccelDraw::Disabled; +    Draw(is_indexed, true);      return true;  } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 6a27cf497..0501f3828 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -103,7 +103,7 @@ private:                             std::size_t size);      /// Syncs all the state, shaders, render targets and textures setting before a draw call. -    void DrawPrelude(); +    void Draw(bool is_indexed, bool is_instanced);      /// Configures the current textures to use for the draw command.      void SetupDrawTextures(std::size_t stage_index, const Shader& shader); @@ -220,12 +220,7 @@ private:      GLintptr SetupIndexBuffer(); -    GLintptr index_buffer_offset; -      void SetupShaders(GLenum primitive_mode); - -    enum class AccelDraw { Disabled, Arrays, Indexed }; -    AccelDraw accelerate_draw = AccelDraw::Disabled;  };  } // namespace OpenGL diff --git a/src/video_core/shader/decode/shift.cpp b/src/video_core/shader/decode/shift.cpp index d419e9c45..3b391d3e6 100644 --- a/src/video_core/shader/decode/shift.cpp +++ b/src/video_core/shader/decode/shift.cpp @@ -10,8 +10,80 @@  namespace VideoCommon::Shader { +using std::move;  using Tegra::Shader::Instruction;  using Tegra::Shader::OpCode; +using Tegra::Shader::ShfType; +using Tegra::Shader::ShfXmode; + +namespace { + +Node IsFull(Node shift) { +    return Operation(OperationCode::LogicalIEqual, move(shift), Immediate(32)); +} + +Node Shift(OperationCode opcode, Node value, Node shift) { +    Node is_full = Operation(OperationCode::LogicalIEqual, shift, Immediate(32)); +    Node shifted = Operation(opcode, move(value), shift); +    return Operation(OperationCode::Select, IsFull(move(shift)), Immediate(0), move(shifted)); +} + +Node ClampShift(Node shift, s32 size = 32) { +    shift = Operation(OperationCode::IMax, move(shift), Immediate(0)); +    return Operation(OperationCode::IMin, move(shift), Immediate(size)); +} + +Node WrapShift(Node shift, s32 size = 32) { +    return Operation(OperationCode::UBitwiseAnd, move(shift), Immediate(size - 1)); +} + +Node ShiftRight(Node low, Node high, Node shift, Node low_shift, ShfType type) { +    // These values are used when the shift value is less than 32 +    Node less_low = Shift(OperationCode::ILogicalShiftRight, low, shift); +    Node less_high = Shift(OperationCode::ILogicalShiftLeft, high, low_shift); +    Node less = Operation(OperationCode::IBitwiseOr, move(less_high), move(less_low)); + +    if (type == ShfType::Bits32) { +        // On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits +        return Operation(OperationCode::Select, IsFull(move(shift)), move(high), move(less)); +    } + +    // And these when it's larger than or 32 +    const bool is_signed = type == ShfType::S64; +    const auto opcode = SignedToUnsignedCode(OperationCode::IArithmeticShiftRight, is_signed); +    Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32)); +    Node greater = Shift(opcode, high, move(reduced)); + +    Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32)); +    Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0)); + +    Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater)); +    return Operation(OperationCode::Select, move(is_zero), move(high), move(value)); +} + +Node ShiftLeft(Node low, Node high, Node shift, Node low_shift, ShfType type) { +    // These values are used when the shift value is less than 32 +    Node less_low = Operation(OperationCode::ILogicalShiftRight, low, low_shift); +    Node less_high = Operation(OperationCode::ILogicalShiftLeft, high, shift); +    Node less = Operation(OperationCode::IBitwiseOr, move(less_low), move(less_high)); + +    if (type == ShfType::Bits32) { +        // On 32 bit shifts we are either full (shifting 32) or shifting less than 32 bits +        return Operation(OperationCode::Select, IsFull(move(shift)), move(low), move(less)); +    } + +    // And these when it's larger than or 32 +    Node reduced = Operation(OperationCode::IAdd, shift, Immediate(-32)); +    Node greater = Shift(OperationCode::ILogicalShiftLeft, move(low), move(reduced)); + +    Node is_less = Operation(OperationCode::LogicalILessThan, shift, Immediate(32)); +    Node is_zero = Operation(OperationCode::LogicalIEqual, move(shift), Immediate(0)); + +    Node value = Operation(OperationCode::Select, move(is_less), move(less), move(greater)); +    return Operation(OperationCode::Select, move(is_zero), move(high), move(value)); +} + +} // Anonymous namespace  u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {      const Instruction instr = {program_code[pc]}; @@ -28,29 +100,48 @@ u32 ShaderIR::DecodeShift(NodeBlock& bb, u32 pc) {          }      }(); -    switch (opcode->get().GetId()) { +    switch (const auto opid = opcode->get().GetId(); opid) {      case OpCode::Id::SHR_C:      case OpCode::Id::SHR_R:      case OpCode::Id::SHR_IMM: { -        if (instr.shr.wrap) { -            op_b = Operation(OperationCode::UBitwiseAnd, std::move(op_b), Immediate(0x1f)); -        } else { -            op_b = Operation(OperationCode::IMax, std::move(op_b), Immediate(0)); -            op_b = Operation(OperationCode::IMin, std::move(op_b), Immediate(31)); -        } +        op_b = instr.shr.wrap ? WrapShift(move(op_b)) : ClampShift(move(op_b));          Node value = SignedOperation(OperationCode::IArithmeticShiftRight, instr.shift.is_signed, -                                     std::move(op_a), std::move(op_b)); +                                     move(op_a), move(op_b));          SetInternalFlagsFromInteger(bb, value, instr.generates_cc); -        SetRegister(bb, instr.gpr0, std::move(value)); +        SetRegister(bb, instr.gpr0, move(value));          break;      }      case OpCode::Id::SHL_C:      case OpCode::Id::SHL_R:      case OpCode::Id::SHL_IMM: { -        const Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b); +        Node value = Operation(OperationCode::ILogicalShiftLeft, op_a, op_b);          SetInternalFlagsFromInteger(bb, value, instr.generates_cc); -        SetRegister(bb, instr.gpr0, value); +        SetRegister(bb, instr.gpr0, move(value)); +        break; +    } +    case OpCode::Id::SHF_RIGHT_R: +    case OpCode::Id::SHF_RIGHT_IMM: +    case OpCode::Id::SHF_LEFT_R: +    case OpCode::Id::SHF_LEFT_IMM: { +        UNIMPLEMENTED_IF(instr.generates_cc); +        UNIMPLEMENTED_IF_MSG(instr.shf.xmode != ShfXmode::None, "xmode={}", +                             static_cast<int>(instr.shf.xmode.Value())); + +        if (instr.is_b_imm) { +            op_b = Immediate(static_cast<u32>(instr.shf.immediate)); +        } +        const s32 size = instr.shf.type == ShfType::Bits32 ? 32 : 64; +        Node shift = instr.shf.wrap ? WrapShift(move(op_b), size) : ClampShift(move(op_b), size); + +        Node negated_shift = Operation(OperationCode::INegate, shift); +        Node low_shift = Operation(OperationCode::IAdd, move(negated_shift), Immediate(32)); + +        const bool is_right = opid == OpCode::Id::SHF_RIGHT_R || opid == OpCode::Id::SHF_RIGHT_IMM; +        Node value = (is_right ? ShiftRight : ShiftLeft)( +            move(op_a), GetRegister(instr.gpr39), move(shift), move(low_shift), instr.shf.type); + +        SetRegister(bb, instr.gpr0, move(value));          break;      }      default: | 
