From 62a834d15edf15c956b825dc999d294cc2f6f42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 00:12:31 +0200 Subject: [PATCH 01/22] Split up FindTransferFramebuffers --- GPU/Common/FramebufferManagerCommon.cpp | 133 ++++++++++++++---------- GPU/Common/FramebufferManagerCommon.h | 5 +- 2 files changed, 82 insertions(+), 56 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 581d485e6e..bc3bc1751b 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1640,8 +1640,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } } -// Can't be const, in case it has to create a vfb unfortunately. -void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp) { +void FramebufferManagerCommon::FindTransferFramebufferSrc(VirtualFramebuffer *&srcBuffer, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int bpp) { u32 dstYOffset = -1; u32 dstXOffset = -1; u32 srcYOffset = -1; @@ -1649,9 +1648,56 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst int width = srcWidth; int height = srcHeight; - dstBasePtr &= 0x3FFFFFFF; srcBasePtr &= 0x3FFFFFFF; + for (size_t i = 0; i < vfbs_.size(); ++i) { + VirtualFramebuffer *vfb = vfbs_[i]; + const u32 vfb_address = vfb->fb_address & 0x3FFFFFFF; + const u32 vfb_size = ColorBufferByteSize(vfb); + const u32 vfb_bpp = BufferFormatBytesPerPixel(vfb->fb_format); + const u32 vfb_byteStride = vfb->fb_stride * vfb_bpp; + const u32 vfb_byteWidth = vfb->width * vfb_bpp; + + if (vfb_address <= srcBasePtr && srcBasePtr < vfb_address + vfb_size) { + const u32 byteOffset = srcBasePtr - vfb_address; + const u32 byteStride = srcStride * bpp; + const u32 yOffset = byteOffset / byteStride; + bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->bufferHeight - srcHeight; + if (match && vfb_byteStride != byteStride) { + if (width != srcStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) { + match = false; + } else { + srcWidth = byteStride * height / vfb_bpp; + srcHeight = 1; + } + } else if (match) { + srcWidth = width; + srcHeight = height; + } + if (match) { + srcYOffset = yOffset; + srcXOffset = srcStride == 0 ? 0 : (byteOffset / bpp) % srcStride; + srcBuffer = vfb; + } + } + } + + if (srcYOffset != (u32)-1) { + srcY += srcYOffset; + srcX += srcXOffset; + } +} + +void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&dstBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, int &dstWidth, int &dstHeight, int bpp) { + u32 dstYOffset = -1; + u32 dstXOffset = -1; + u32 srcYOffset = -1; + u32 srcXOffset = -1; + int width = dstWidth; + int height = dstHeight; + + dstBasePtr &= 0x3FFFFFFF; + for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; const u32 vfb_address = vfb->fb_address & 0x3FFFFFFF; @@ -1698,62 +1744,12 @@ void FramebufferManagerCommon::FindTransferFramebuffers(VirtualFramebuffer *&dst dstBuffer = vfb; } } - if (vfb_address <= srcBasePtr && srcBasePtr < vfb_address + vfb_size) { - const u32 byteOffset = srcBasePtr - vfb_address; - const u32 byteStride = srcStride * bpp; - const u32 yOffset = byteOffset / byteStride; - bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->bufferHeight - srcHeight; - if (match && vfb_byteStride != byteStride) { - if (width != srcStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) { - match = false; - } else { - srcWidth = byteStride * height / vfb_bpp; - srcHeight = 1; - } - } else if (match) { - srcWidth = width; - srcHeight = height; - } - if (match) { - srcYOffset = yOffset; - srcXOffset = srcStride == 0 ? 0 : (byteOffset / bpp) % srcStride; - srcBuffer = vfb; - } - } } - if (srcBuffer && !dstBuffer) { - if (PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB || - (PSP_CoreParameter().compat.flags().IntraVRAMBlockTransferAllowCreateFB && - Memory::IsVRAMAddress(srcBuffer->fb_address) && Memory::IsVRAMAddress(dstBasePtr))) { - GEBufferFormat ramFormat; - // Try to guess the appropriate format. We only know the bpp from the block transfer command (16 or 32 bit). - if (bpp == 4) { - // Only one possibility unless it's doing split pixel tricks (which we could detect through stride maybe). - ramFormat = GE_FORMAT_8888; - } else if (srcBuffer->fb_format != GE_FORMAT_8888) { - // We guess that the game will interpret the data the same as it was in the source of the copy. - // Seems like a likely good guess, and works in Test Drive Unlimited. - ramFormat = srcBuffer->fb_format; - } else { - // No info left - just fall back to something. But this is definitely split pixel tricks. - ramFormat = GE_FORMAT_5551; - } - dstBuffer = CreateRAMFramebuffer(dstBasePtr, dstWidth, dstHeight, dstStride, ramFormat); - } - } - - if (dstBuffer) - dstBuffer->last_frame_used = gpuStats.numFlips; - if (dstYOffset != (u32)-1) { dstY += dstYOffset; dstX += dstXOffset; } - if (srcYOffset != (u32)-1) { - srcY += srcYOffset; - srcX += srcXOffset; - } } VirtualFramebuffer *FramebufferManagerCommon::CreateRAMFramebuffer(uint32_t fbAddress, int width, int height, int stride, GEBufferFormat format) { @@ -1957,7 +1953,32 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst int dstHeight = height; // This looks at the compat flags BlockTransferAllowCreateFB*. - FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, dstWidth, dstHeight, bpp); + FindTransferFramebufferSrc(srcBuffer, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, bpp); + FindTransferFramebufferDst(dstBuffer, dstBasePtr, dstStride, dstX, dstY, dstWidth, dstHeight, bpp); + + if (srcBuffer && !dstBuffer) { + if (PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB || + (PSP_CoreParameter().compat.flags().IntraVRAMBlockTransferAllowCreateFB && + Memory::IsVRAMAddress(srcBuffer->fb_address) && Memory::IsVRAMAddress(dstBasePtr))) { + GEBufferFormat ramFormat; + // Try to guess the appropriate format. We only know the bpp from the block transfer command (16 or 32 bit). + if (bpp == 4) { + // Only one possibility unless it's doing split pixel tricks (which we could detect through stride maybe). + ramFormat = GE_FORMAT_8888; + } else if (srcBuffer->fb_format != GE_FORMAT_8888) { + // We guess that the game will interpret the data the same as it was in the source of the copy. + // Seems like a likely good guess, and works in Test Drive Unlimited. + ramFormat = srcBuffer->fb_format; + } else { + // No info left - just fall back to something. But this is definitely split pixel tricks. + ramFormat = GE_FORMAT_5551; + } + dstBuffer = CreateRAMFramebuffer(dstBasePtr, dstWidth, dstHeight, dstStride, ramFormat); + } + } + + if (dstBuffer) + dstBuffer->last_frame_used = gpuStats.numFlips; if (dstBuffer && srcBuffer) { if (srcBuffer == dstBuffer) { @@ -2032,13 +2053,15 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS } if (MayIntersectFramebuffer(srcBasePtr) || MayIntersectFramebuffer(dstBasePtr)) { + // TODO: Figure out how we can avoid repeating the search here. VirtualFramebuffer *dstBuffer = 0; VirtualFramebuffer *srcBuffer = 0; int srcWidth = width; int srcHeight = height; int dstWidth = width; int dstHeight = height; - FindTransferFramebuffers(dstBuffer, srcBuffer, dstBasePtr, dstStride, dstX, dstY, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, dstWidth, dstHeight, bpp); + FindTransferFramebufferSrc(srcBuffer, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, bpp); + FindTransferFramebufferDst(dstBuffer, dstBasePtr, dstStride, dstX, dstY, dstWidth, dstHeight, bpp); // A few games use this INSTEAD of actually drawing the video image to the screen, they just blast it to // the backbuffer. Detect this and have the framebuffermanager draw the pixels. diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 4775d07e98..b30600cdc8 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -412,7 +412,10 @@ protected: bool ShouldDownloadFramebuffer(const VirtualFramebuffer *vfb) const; void DownloadFramebufferOnSwitch(VirtualFramebuffer *vfb); - void FindTransferFramebuffers(VirtualFramebuffer *&dstBuffer, VirtualFramebuffer *&srcBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &dstWidth, int &dstHeight, int bpp); + + void FindTransferFramebufferSrc(VirtualFramebuffer *&srcBuffer, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int bpp); + void FindTransferFramebufferDst(VirtualFramebuffer *&dstBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, int &dstWidth, int &dstHeight, int bpp); + VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb); virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {} From d0713d7fff6007972d8318e4ba3fbe1314f20877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 00:17:01 +0200 Subject: [PATCH 02/22] Rename variables to make the two functions match --- GPU/Common/FramebufferManagerCommon.cpp | 74 ++++++++++++------------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index bc3bc1751b..807c4cb8a5 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1640,15 +1640,13 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } } -void FramebufferManagerCommon::FindTransferFramebufferSrc(VirtualFramebuffer *&srcBuffer, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int bpp) { - u32 dstYOffset = -1; - u32 dstXOffset = -1; +void FramebufferManagerCommon::FindTransferFramebufferSrc(VirtualFramebuffer *&srcBuffer, u32 basePtr, int stride, int &x, int &y, int &width, int &height, int bpp) { u32 srcYOffset = -1; u32 srcXOffset = -1; - int width = srcWidth; - int height = srcHeight; + int transferWidth = width; + int transferHeight = height; - srcBasePtr &= 0x3FFFFFFF; + basePtr &= 0x3FFFFFFF; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; @@ -1658,45 +1656,43 @@ void FramebufferManagerCommon::FindTransferFramebufferSrc(VirtualFramebuffer *&s const u32 vfb_byteStride = vfb->fb_stride * vfb_bpp; const u32 vfb_byteWidth = vfb->width * vfb_bpp; - if (vfb_address <= srcBasePtr && srcBasePtr < vfb_address + vfb_size) { - const u32 byteOffset = srcBasePtr - vfb_address; - const u32 byteStride = srcStride * bpp; + if (vfb_address <= basePtr && basePtr < vfb_address + vfb_size) { + const u32 byteOffset = basePtr - vfb_address; + const u32 byteStride = stride * bpp; const u32 yOffset = byteOffset / byteStride; - bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->bufferHeight - srcHeight; + bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->bufferHeight - height; if (match && vfb_byteStride != byteStride) { - if (width != srcStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) { + if (transferWidth != stride || (byteStride * transferHeight != vfb_byteStride && byteStride * transferHeight != vfb_byteWidth)) { match = false; } else { - srcWidth = byteStride * height / vfb_bpp; - srcHeight = 1; + width = byteStride * transferHeight / vfb_bpp; + height = 1; } } else if (match) { - srcWidth = width; - srcHeight = height; + width = transferWidth; + height = transferHeight; } if (match) { srcYOffset = yOffset; - srcXOffset = srcStride == 0 ? 0 : (byteOffset / bpp) % srcStride; + srcXOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride; srcBuffer = vfb; } } } if (srcYOffset != (u32)-1) { - srcY += srcYOffset; - srcX += srcXOffset; + y += srcYOffset; + x += srcXOffset; } } -void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&dstBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, int &dstWidth, int &dstHeight, int bpp) { +void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&dstBuffer, u32 basePtr, int stride, int &x, int &y, int &width, int &height, int bpp) { u32 dstYOffset = -1; u32 dstXOffset = -1; - u32 srcYOffset = -1; - u32 srcXOffset = -1; - int width = dstWidth; - int height = dstHeight; + int transferWidth = width; + int transferHeight = height; - dstBasePtr &= 0x3FFFFFFF; + basePtr &= 0x3FFFFFFF; for (size_t i = 0; i < vfbs_.size(); ++i) { VirtualFramebuffer *vfb = vfbs_[i]; @@ -1710,45 +1706,45 @@ void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&d // The goal is to avoid using GPU block transfers for things that ought to be memory. // Maybe we should even check for textures at these places instead? - if (vfb_address <= dstBasePtr && dstBasePtr < vfb_address + vfb_size) { - const u32 byteOffset = dstBasePtr - vfb_address; - const u32 byteStride = dstStride * bpp; + if (vfb_address <= basePtr && basePtr < vfb_address + vfb_size) { + const u32 byteOffset = basePtr - vfb_address; + const u32 byteStride = stride * bpp; const u32 yOffset = byteOffset / byteStride; - // Some games use mismatching bitdepths. But make sure the stride matches. + // Some games use mismatching bitdepths. But make sure the stride matches. // If it doesn't, generally this means we detected the framebuffer with too large a height. // Use bufferHeight in case of buffers that resize up and down often per frame (Valkyrie Profile.) - bool match = yOffset < dstYOffset && (int)yOffset <= (int)vfb->bufferHeight - dstHeight; + bool match = yOffset < dstYOffset && (int)yOffset <= (int)vfb->bufferHeight - height; if (match && vfb_byteStride != byteStride) { // Grand Knights History copies with a mismatching stride but a full line at a time. // Makes it hard to detect the wrong transfers in e.g. God of War. - if (width != dstStride || (byteStride * height != vfb_byteStride && byteStride * height != vfb_byteWidth)) { + if (transferWidth != stride || (byteStride * transferHeight != vfb_byteStride && byteStride * transferHeight != vfb_byteWidth)) { // However, some other games write cluts to framebuffers. // Let's catch this and upload. Otherwise reject the match. match = (vfb->usageFlags & FB_USAGE_CLUT) != 0; if (match) { - dstWidth = byteStride * height / vfb_bpp; - dstHeight = 1; + width = byteStride * transferHeight / vfb_bpp; + height = 1; } } else { - dstWidth = byteStride * height / vfb_bpp; - dstHeight = 1; + width = byteStride * transferHeight / vfb_bpp; + height = 1; } } else if (match) { - dstWidth = width; - dstHeight = height; + width = transferWidth; + height = transferHeight; } if (match) { dstYOffset = yOffset; - dstXOffset = dstStride == 0 ? 0 : (byteOffset / bpp) % dstStride; + dstXOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride; dstBuffer = vfb; } } } if (dstYOffset != (u32)-1) { - dstY += dstYOffset; - dstX += dstXOffset; + y += dstYOffset; + x += dstXOffset; } } From 9feb61e7faa409e08b6ef70250118affa2a6410c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 00:19:08 +0200 Subject: [PATCH 03/22] Additional renaming --- GPU/Common/FramebufferManagerCommon.cpp | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 807c4cb8a5..25ae260941 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1641,8 +1641,8 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } void FramebufferManagerCommon::FindTransferFramebufferSrc(VirtualFramebuffer *&srcBuffer, u32 basePtr, int stride, int &x, int &y, int &width, int &height, int bpp) { - u32 srcYOffset = -1; - u32 srcXOffset = -1; + u32 yOffset = -1; + u32 xOffset = -1; int transferWidth = width; int transferHeight = height; @@ -1659,8 +1659,8 @@ void FramebufferManagerCommon::FindTransferFramebufferSrc(VirtualFramebuffer *&s if (vfb_address <= basePtr && basePtr < vfb_address + vfb_size) { const u32 byteOffset = basePtr - vfb_address; const u32 byteStride = stride * bpp; - const u32 yOffset = byteOffset / byteStride; - bool match = yOffset < srcYOffset && (int)yOffset <= (int)vfb->bufferHeight - height; + const u32 memYOffset = byteOffset / byteStride; + bool match = memYOffset < yOffset && (int)memYOffset <= (int)vfb->bufferHeight - height; if (match && vfb_byteStride != byteStride) { if (transferWidth != stride || (byteStride * transferHeight != vfb_byteStride && byteStride * transferHeight != vfb_byteWidth)) { match = false; @@ -1673,22 +1673,22 @@ void FramebufferManagerCommon::FindTransferFramebufferSrc(VirtualFramebuffer *&s height = transferHeight; } if (match) { - srcYOffset = yOffset; - srcXOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride; + yOffset = memYOffset; + xOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride; srcBuffer = vfb; } } } - if (srcYOffset != (u32)-1) { - y += srcYOffset; - x += srcXOffset; + if (yOffset != (u32)-1) { + y += yOffset; + x += xOffset; } } void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&dstBuffer, u32 basePtr, int stride, int &x, int &y, int &width, int &height, int bpp) { - u32 dstYOffset = -1; - u32 dstXOffset = -1; + u32 yOffset = -1; + u32 xOffset = -1; int transferWidth = width; int transferHeight = height; @@ -1709,12 +1709,12 @@ void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&d if (vfb_address <= basePtr && basePtr < vfb_address + vfb_size) { const u32 byteOffset = basePtr - vfb_address; const u32 byteStride = stride * bpp; - const u32 yOffset = byteOffset / byteStride; + const u32 memYOffset = byteOffset / byteStride; // Some games use mismatching bitdepths. But make sure the stride matches. // If it doesn't, generally this means we detected the framebuffer with too large a height. // Use bufferHeight in case of buffers that resize up and down often per frame (Valkyrie Profile.) - bool match = yOffset < dstYOffset && (int)yOffset <= (int)vfb->bufferHeight - height; + bool match = memYOffset < yOffset && (int)memYOffset <= (int)vfb->bufferHeight - height; if (match && vfb_byteStride != byteStride) { // Grand Knights History copies with a mismatching stride but a full line at a time. // Makes it hard to detect the wrong transfers in e.g. God of War. @@ -1735,16 +1735,16 @@ void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&d height = transferHeight; } if (match) { - dstYOffset = yOffset; - dstXOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride; + yOffset = memYOffset; + xOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride; dstBuffer = vfb; } } } - if (dstYOffset != (u32)-1) { - y += dstYOffset; - x += dstXOffset; + if (yOffset != (u32)-1) { + y += yOffset; + x += xOffset; } } From bbd92e634530c179da62ba371ca24ae750b4320a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 00:22:33 +0200 Subject: [PATCH 04/22] Merge the two find FindTransferFramebuffer* methods --- GPU/Common/FramebufferManagerCommon.cpp | 78 +++++-------------------- GPU/Common/FramebufferManagerCommon.h | 3 +- 2 files changed, 17 insertions(+), 64 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 25ae260941..7222da03aa 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1640,7 +1640,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } } -void FramebufferManagerCommon::FindTransferFramebufferSrc(VirtualFramebuffer *&srcBuffer, u32 basePtr, int stride, int &x, int &y, int &width, int &height, int bpp) { +void FramebufferManagerCommon::FindTransferFramebuffer(VirtualFramebuffer *&buffer, u32 basePtr, int stride, int &x, int &y, int &width, int &height, int bpp, bool destination) { u32 yOffset = -1; u32 xOffset = -1; int transferWidth = width; @@ -1656,56 +1656,6 @@ void FramebufferManagerCommon::FindTransferFramebufferSrc(VirtualFramebuffer *&s const u32 vfb_byteStride = vfb->fb_stride * vfb_bpp; const u32 vfb_byteWidth = vfb->width * vfb_bpp; - if (vfb_address <= basePtr && basePtr < vfb_address + vfb_size) { - const u32 byteOffset = basePtr - vfb_address; - const u32 byteStride = stride * bpp; - const u32 memYOffset = byteOffset / byteStride; - bool match = memYOffset < yOffset && (int)memYOffset <= (int)vfb->bufferHeight - height; - if (match && vfb_byteStride != byteStride) { - if (transferWidth != stride || (byteStride * transferHeight != vfb_byteStride && byteStride * transferHeight != vfb_byteWidth)) { - match = false; - } else { - width = byteStride * transferHeight / vfb_bpp; - height = 1; - } - } else if (match) { - width = transferWidth; - height = transferHeight; - } - if (match) { - yOffset = memYOffset; - xOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride; - srcBuffer = vfb; - } - } - } - - if (yOffset != (u32)-1) { - y += yOffset; - x += xOffset; - } -} - -void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&dstBuffer, u32 basePtr, int stride, int &x, int &y, int &width, int &height, int bpp) { - u32 yOffset = -1; - u32 xOffset = -1; - int transferWidth = width; - int transferHeight = height; - - basePtr &= 0x3FFFFFFF; - - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - const u32 vfb_address = vfb->fb_address & 0x3FFFFFFF; - const u32 vfb_size = ColorBufferByteSize(vfb); - const u32 vfb_bpp = BufferFormatBytesPerPixel(vfb->fb_format); - const u32 vfb_byteStride = vfb->fb_stride * vfb_bpp; - const u32 vfb_byteWidth = vfb->width * vfb_bpp; - - // These heuristics are a bit annoying. - // The goal is to avoid using GPU block transfers for things that ought to be memory. - // Maybe we should even check for textures at these places instead? - if (vfb_address <= basePtr && basePtr < vfb_address + vfb_size) { const u32 byteOffset = basePtr - vfb_address; const u32 byteStride = stride * bpp; @@ -1719,12 +1669,16 @@ void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&d // Grand Knights History copies with a mismatching stride but a full line at a time. // Makes it hard to detect the wrong transfers in e.g. God of War. if (transferWidth != stride || (byteStride * transferHeight != vfb_byteStride && byteStride * transferHeight != vfb_byteWidth)) { - // However, some other games write cluts to framebuffers. - // Let's catch this and upload. Otherwise reject the match. - match = (vfb->usageFlags & FB_USAGE_CLUT) != 0; - if (match) { - width = byteStride * transferHeight / vfb_bpp; - height = 1; + if (destination) { + // However, some other games write cluts to framebuffers. + // Let's catch this and upload. Otherwise reject the match. + match = (vfb->usageFlags & FB_USAGE_CLUT) != 0; + if (match) { + width = byteStride * transferHeight / vfb_bpp; + height = 1; + } + } else { + match = false; } } else { width = byteStride * transferHeight / vfb_bpp; @@ -1737,7 +1691,7 @@ void FramebufferManagerCommon::FindTransferFramebufferDst(VirtualFramebuffer *&d if (match) { yOffset = memYOffset; xOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride; - dstBuffer = vfb; + buffer = vfb; } } } @@ -1949,8 +1903,8 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst int dstHeight = height; // This looks at the compat flags BlockTransferAllowCreateFB*. - FindTransferFramebufferSrc(srcBuffer, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, bpp); - FindTransferFramebufferDst(dstBuffer, dstBasePtr, dstStride, dstX, dstY, dstWidth, dstHeight, bpp); + FindTransferFramebuffer(srcBuffer, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, bpp, false); + FindTransferFramebuffer(dstBuffer, dstBasePtr, dstStride, dstX, dstY, dstWidth, dstHeight, bpp, true); if (srcBuffer && !dstBuffer) { if (PSP_CoreParameter().compat.flags().BlockTransferAllowCreateFB || @@ -2056,8 +2010,8 @@ void FramebufferManagerCommon::NotifyBlockTransferAfter(u32 dstBasePtr, int dstS int srcHeight = height; int dstWidth = width; int dstHeight = height; - FindTransferFramebufferSrc(srcBuffer, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, bpp); - FindTransferFramebufferDst(dstBuffer, dstBasePtr, dstStride, dstX, dstY, dstWidth, dstHeight, bpp); + FindTransferFramebuffer(srcBuffer, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, bpp, false); + FindTransferFramebuffer(dstBuffer, dstBasePtr, dstStride, dstX, dstY, dstWidth, dstHeight, bpp, true); // A few games use this INSTEAD of actually drawing the video image to the screen, they just blast it to // the backbuffer. Detect this and have the framebuffermanager draw the pixels. diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index b30600cdc8..618d3e2ed5 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -413,8 +413,7 @@ protected: bool ShouldDownloadFramebuffer(const VirtualFramebuffer *vfb) const; void DownloadFramebufferOnSwitch(VirtualFramebuffer *vfb); - void FindTransferFramebufferSrc(VirtualFramebuffer *&srcBuffer, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int bpp); - void FindTransferFramebufferDst(VirtualFramebuffer *&dstBuffer, u32 dstBasePtr, int dstStride, int &dstX, int &dstY, int &dstWidth, int &dstHeight, int bpp); + void FindTransferFramebuffer(VirtualFramebuffer *&srcBuffer, u32 srcBasePtr, int srcStride, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int bpp, bool destination); VirtualFramebuffer *FindDownloadTempBuffer(VirtualFramebuffer *vfb); virtual void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) {} From f79acd651df2788a5717e53c886a0bd575a58571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 00:25:53 +0200 Subject: [PATCH 05/22] Comment updates --- GPU/Common/FramebufferManagerCommon.cpp | 5 ++++- GPU/Common/FramebufferManagerCommon.h | 5 +++++ GPU/GPUCommon.cpp | 9 --------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 7222da03aa..2458b53608 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1664,6 +1664,9 @@ void FramebufferManagerCommon::FindTransferFramebuffer(VirtualFramebuffer *&buff // Some games use mismatching bitdepths. But make sure the stride matches. // If it doesn't, generally this means we detected the framebuffer with too large a height. // Use bufferHeight in case of buffers that resize up and down often per frame (Valkyrie Profile.) + + // TODO: Surely this first comparison should be <= ? + // Or does the exact match (byteOffset == 0) case get handled elsewhere? bool match = memYOffset < yOffset && (int)memYOffset <= (int)vfb->bufferHeight - height; if (match && vfb_byteStride != byteStride) { // Grand Knights History copies with a mismatching stride but a full line at a time. @@ -1902,7 +1905,7 @@ bool FramebufferManagerCommon::NotifyBlockTransferBefore(u32 dstBasePtr, int dst int dstWidth = width; int dstHeight = height; - // This looks at the compat flags BlockTransferAllowCreateFB*. + // These modify the X/Y/W/H parameters depending on the memory offset of the base pointers from the actual buffers. FindTransferFramebuffer(srcBuffer, srcBasePtr, srcStride, srcX, srcY, srcWidth, srcHeight, bpp, false); FindTransferFramebuffer(dstBuffer, dstBasePtr, dstStride, dstX, dstY, dstWidth, dstHeight, bpp, true); diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index 618d3e2ed5..cf7bf4e6c7 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -275,10 +275,15 @@ public: void UpdateFromMemory(u32 addr, int size, bool safe); void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor); bool PerformStencilUpload(u32 addr, int size, StencilUpload flags); + // Returns true if it's sure this is a direct FBO->FBO transfer and it has already handle it. // In that case we hardly need to actually copy the bytes in VRAM, they will be wrong anyway (unless // read framebuffers is on, in which case this should always return false). + // If this returns false, a memory copy will happen and NotifyBlockTransferAfter will be called. bool NotifyBlockTransferBefore(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason); + + // This gets called after the memory copy, in case NotifyBlockTransferBefore returned false. + // Otherwise it doesn't get called. void NotifyBlockTransferAfter(u32 dstBasePtr, int dstStride, int dstX, int dstY, u32 srcBasePtr, int srcStride, int srcX, int srcY, int w, int h, int bpp, u32 skipDrawReason); bool BindFramebufferAsColorTexture(int stage, VirtualFramebuffer *framebuffer, int flags); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 8ceac22d47..4f1ebadf2e 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -2773,15 +2773,6 @@ void GPUCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat f } void GPUCommon::DoBlockTransfer(u32 skipDrawReason) { - // TODO: This is used a lot to copy data around between render targets and textures, - // and also to quickly load textures from RAM to VRAM. So we should do checks like the following: - // * Does dstBasePtr point to an existing texture? If so maybe reload it immediately. - // - // * Does srcBasePtr point to a render target, and dstBasePtr to a texture? If so - // either copy between rt and texture or reassign the texture to point to the render target - // - // etc.... - u32 srcBasePtr = gstate.getTransferSrcAddress(); u32 srcStride = gstate.getTransferSrcStride(); From 272019f23a0b9618b0ff19acad532a063228f5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 00:47:19 +0200 Subject: [PATCH 06/22] Comments --- GPU/Common/FramebufferManagerCommon.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 2458b53608..ed2f6306d1 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -1531,6 +1531,8 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, dst &= 0x3FFFFFFF; src &= 0x3FFFFFFF; + // TODO: Merge the below into FindTransferFramebuffer + VirtualFramebuffer *dstBuffer = 0; VirtualFramebuffer *srcBuffer = 0; u32 dstY = (u32)-1; @@ -1670,6 +1672,7 @@ void FramebufferManagerCommon::FindTransferFramebuffer(VirtualFramebuffer *&buff bool match = memYOffset < yOffset && (int)memYOffset <= (int)vfb->bufferHeight - height; if (match && vfb_byteStride != byteStride) { // Grand Knights History copies with a mismatching stride but a full line at a time. + // That's why we multiply by height, not width - this copy is a rectangle with the wrong stride but a line with the correct one. // Makes it hard to detect the wrong transfers in e.g. God of War. if (transferWidth != stride || (byteStride * transferHeight != vfb_byteStride && byteStride * transferHeight != vfb_byteWidth)) { if (destination) { From 753062b2a172d5b3a8f9fc74cd5d0d398b3a1dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 00:52:45 +0200 Subject: [PATCH 07/22] More comment fixes, cleanups --- GPU/Common/FramebufferManagerCommon.cpp | 38 ++++++++++--------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index ed2f6306d1..e03d1ddadd 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -113,12 +113,11 @@ void FramebufferManagerCommon::SetDisplayFramebuffer(u32 framebuf, u32 stride, G VirtualFramebuffer *FramebufferManagerCommon::GetVFBAt(u32 addr) const { addr &= 0x3FFFFFFF; VirtualFramebuffer *match = nullptr; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *v = vfbs_[i]; - if (v->fb_address == addr) { + for (auto vfb : vfbs_) { + if (vfb->fb_address == addr) { // Could check w too but whatever (actually, might very well make sense to do so, depending on context). - if (!match || v->last_frame_render > match->last_frame_render) { - match = v; + if (!match || vfb->last_frame_render > match->last_frame_render) { + match = vfb; } } } @@ -182,8 +181,8 @@ void FramebufferManagerCommon::EstimateDrawingSize(u32 fb_address, int fb_stride if (viewport_width != region_width) { // The majority of the time, these are equal. If not, let's check what we know. u32 nearest_address = 0xFFFFFFFF; - for (size_t i = 0; i < vfbs_.size(); ++i) { - const u32 other_address = vfbs_[i]->fb_address & 0x3FFFFFFF; + for (auto vfb : vfbs_) { + const u32 other_address = vfb->fb_address & 0x3FFFFFFF; if (other_address > fb_address && other_address < nearest_address) { nearest_address = other_address; } @@ -319,9 +318,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame // Find a matching framebuffer VirtualFramebuffer *vfb = nullptr; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *v = vfbs_[i]; - + for (auto v : vfbs_) { const u32 bpp = BufferFormatBytesPerPixel(v->fb_format); if (params.fb_address == v->fb_address && params.fb_format == v->fb_format && params.fb_stride == v->fb_stride) { @@ -460,7 +457,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame // TODO: Is it worth trying to upload the depth buffer (only if it wasn't copied above..?) } - // Let's check for depth buffer overlap. Might be interesting. + // Let's check for depth buffer overlap. Might be interesting (not that interesting anymore..) bool sharingReported = false; for (size_t i = 0, end = vfbs_.size(); i < end; ++i) { if (vfbs_[i]->z_stride != 0 && params.fb_address == vfbs_[i]->z_address) { @@ -1255,8 +1252,7 @@ void FramebufferManagerCommon::CopyDisplayToOutput(bool reallyDirty) { // "framebuffers" sitting in RAM (created from block transfer or similar) so we only take off the kernel // and uncached bits of the address when comparing. const u32 addr = fbaddr & 0x3FFFFFFF; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *v = vfbs_[i]; + for (auto v : vfbs_) { const u32 v_addr = v->fb_address & 0x3FFFFFFF; const u32 v_size = ColorBufferByteSize(v); if (addr >= v_addr && addr < v_addr + v_size) { @@ -1539,8 +1535,7 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, u32 dstH = 0; u32 srcY = (u32)-1; u32 srcH = 0; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; + for (auto vfb : vfbs_) { if (vfb->fb_stride == 0) { continue; } @@ -1643,15 +1638,14 @@ bool FramebufferManagerCommon::NotifyFramebufferCopy(u32 src, u32 dst, int size, } void FramebufferManagerCommon::FindTransferFramebuffer(VirtualFramebuffer *&buffer, u32 basePtr, int stride, int &x, int &y, int &width, int &height, int bpp, bool destination) { - u32 yOffset = -1; u32 xOffset = -1; + u32 yOffset = -1; int transferWidth = width; int transferHeight = height; basePtr &= 0x3FFFFFFF; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; + for (auto vfb : vfbs_) { const u32 vfb_address = vfb->fb_address & 0x3FFFFFFF; const u32 vfb_size = ColorBufferByteSize(vfb); const u32 vfb_bpp = BufferFormatBytesPerPixel(vfb->fb_format); @@ -1695,16 +1689,16 @@ void FramebufferManagerCommon::FindTransferFramebuffer(VirtualFramebuffer *&buff height = transferHeight; } if (match) { - yOffset = memYOffset; xOffset = stride == 0 ? 0 : (byteOffset / bpp) % stride; + yOffset = memYOffset; buffer = vfb; } } } if (yOffset != (u32)-1) { - y += yOffset; x += xOffset; + y += yOffset; } } @@ -2478,9 +2472,7 @@ void FramebufferManagerCommon::RebindFramebuffer(const char *tag) { std::vector FramebufferManagerCommon::GetFramebufferList() const { std::vector list; - for (size_t i = 0; i < vfbs_.size(); ++i) { - VirtualFramebuffer *vfb = vfbs_[i]; - + for (auto vfb : vfbs_) { FramebufferInfo info; info.fb_address = vfb->fb_address; info.z_address = vfb->z_address; From 90c58ae9bb61d9395dd3e52399ff7d0848366e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 01:11:36 +0200 Subject: [PATCH 08/22] Add missing terminator to VR settings array. Better logging. --- Core/Config.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Core/Config.cpp b/Core/Config.cpp index 055a9036d9..29d4dfab04 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -331,7 +331,7 @@ struct ConfigSetting { section->Get(ini5_, &ptr_.customButton->repeat, default_.customButton.repeat); return true; default: - _dbg_assert_msg_(false, "Unexpected ini setting type"); + _dbg_assert_msg_(false, "Get(%s): Unexpected ini setting type: %d", iniKey_, (int)type_); return false; } } @@ -375,7 +375,7 @@ struct ConfigSetting { section->Set(ini5_, ptr_.customButton->repeat); return; default: - _dbg_assert_msg_(false, "Unexpected ini setting type"); + _dbg_assert_msg_(false, "Set(%s): Unexpected ini setting type: %d", iniKey_, (int)type_); return; } } @@ -406,22 +406,22 @@ struct ConfigSetting { // Doesn't report. return; default: - _dbg_assert_msg_(false, "Unexpected ini setting type"); + _dbg_assert_msg_(false, "Report(%s): Unexpected ini setting type: %d", iniKey_, (int)type_); return; } } - const char *iniKey_; - const char *ini2_; - const char *ini3_; - const char *ini4_; - const char *ini5_; + const char *iniKey_ = nullptr; + const char *ini2_ = nullptr; + const char *ini3_ = nullptr; + const char *ini4_ = nullptr; + const char *ini5_ = nullptr; Type type_; bool report_; bool save_; bool perGame_; SettingPtr ptr_; - DefaultValue default_; + DefaultValue default_{}; Callback cb_; // We only support transform for ints. @@ -1207,6 +1207,8 @@ static ConfigSetting vrSettings[] = { ConfigSetting("VREnable6DoF", &g_Config.bEnable6DoF, true), ConfigSetting("VRCanvasDistance", &g_Config.iCanvasDistance, 6), ConfigSetting("VRFieldOfView", &g_Config.iFieldOfViewPercentage, 100), + + ConfigSetting(false), }; static ConfigSectionSettings sections[] = { From 656932397b0cb56b13edab8f76929cb6604302aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 18:56:04 +0200 Subject: [PATCH 09/22] Revert minimum stride --- GPU/Common/FramebufferManagerCommon.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index e03d1ddadd..2ed2ec794c 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -305,7 +305,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame // As there are no clear "framebuffer width" and "framebuffer height" registers, // we need to infer the size of the current framebuffer somehow. int drawing_width, drawing_height; - EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)16), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height); + EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height); gstate_c.SetCurRTOffset(0, 0); bool vfbStrideChanged = false; @@ -859,6 +859,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_CREATED); + // Ugly... if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) { gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE); From e6bb512a3990a600a01bb04cf32cfa67363a26d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Thu, 25 Aug 2022 18:58:35 +0200 Subject: [PATCH 10/22] Remove support for framebuffers changing stride (already unreachable). Also fixes a compiler warning --- GPU/Common/FramebufferManagerCommon.cpp | 38 +++---------------------- GPU/Common/FramebufferManagerCommon.h | 2 +- UI/GameSettingsScreen.cpp | 2 +- 3 files changed, 6 insertions(+), 36 deletions(-) diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index 2ed2ec794c..a75b0d4b85 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -308,7 +308,6 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame EstimateDrawingSize(params.fb_address, std::max(params.fb_stride, (u16)4), params.fb_format, params.viewportWidth, params.viewportHeight, params.regionWidth, params.regionHeight, params.scissorWidth, params.scissorHeight, drawing_width, drawing_height); gstate_c.SetCurRTOffset(0, 0); - bool vfbStrideChanged = false; if (params.fb_address == params.z_address) { // Most likely Z will not be used in this pass, as that would wreak havoc (undefined behavior for sure) @@ -324,16 +323,6 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame if (params.fb_address == v->fb_address && params.fb_format == v->fb_format && params.fb_stride == v->fb_stride) { vfb = v; - // Update fb stride in case it changed. - // - // In reality, this is probably a new different framebuffer... Can't really share - // data between framebuffers with different strides! (or well, we can, with complex - // conversion shaders mapping back to and from memory addresses). - if (vfb->fb_stride != params.fb_stride) { - vfb->fb_stride = params.fb_stride; - vfbStrideChanged = true; - } - if (vfb->z_address == 0 && vfb->z_stride == 0 && params.z_stride != 0) { // Got one that was created by CreateRAMFramebuffer. Since it has no depth buffer, // we just recreate it immediately. @@ -507,7 +496,7 @@ VirtualFramebuffer *FramebufferManagerCommon::DoSetRenderFrameBuffer(const Frame vfb->dirtyAfterDisplay = true; if ((skipDrawReason & SKIPDRAW_SKIPFRAME) == 0) vfb->reallyDirtyAfterDisplay = true; - NotifyRenderFramebufferUpdated(vfb, vfbStrideChanged); + NotifyRenderFramebufferUpdated(vfb); } vfb->colorBindSeq = GetBindSeqCount(); @@ -859,22 +848,10 @@ void FramebufferManagerCommon::NotifyRenderFramebufferCreated(VirtualFramebuffer textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_CREATED); - - // Ugly... - if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) { - gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE); - } - if (gstate_c.curRTRenderWidth != vfb->renderWidth || gstate_c.curRTRenderHeight != vfb->renderHeight) { - gstate_c.Dirty(DIRTY_PROJMATRIX); - gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX); - } + NotifyRenderFramebufferUpdated(vfb); } -void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbStrideChanged) { - if (vfbStrideChanged) { - textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_UPDATED); - } - +void FramebufferManagerCommon::NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb) { // ugly... if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) { gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE); @@ -920,14 +897,7 @@ void FramebufferManagerCommon::NotifyRenderFramebufferSwitched(VirtualFramebuffe } textureCache_->NotifyFramebuffer(vfb, NOTIFY_FB_UPDATED); - // ugly... is all this needed? - if (gstate_c.curRTWidth != vfb->width || gstate_c.curRTHeight != vfb->height) { - gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_CULLRANGE); - } - if (gstate_c.curRTRenderWidth != vfb->renderWidth || gstate_c.curRTRenderHeight != vfb->renderHeight) { - gstate_c.Dirty(DIRTY_PROJMATRIX); - gstate_c.Dirty(DIRTY_PROJTHROUGHMATRIX); - } + NotifyRenderFramebufferUpdated(vfb); } void FramebufferManagerCommon::NotifyVideoUpload(u32 addr, int size, int width, GEBufferFormat fmt) { diff --git a/GPU/Common/FramebufferManagerCommon.h b/GPU/Common/FramebufferManagerCommon.h index cf7bf4e6c7..eb070b8347 100644 --- a/GPU/Common/FramebufferManagerCommon.h +++ b/GPU/Common/FramebufferManagerCommon.h @@ -407,7 +407,7 @@ protected: u32 ColorBufferByteSize(const VirtualFramebuffer *vfb) const; void NotifyRenderFramebufferCreated(VirtualFramebuffer *vfb); - void NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb, bool vfbFormatChanged); + void NotifyRenderFramebufferUpdated(VirtualFramebuffer *vfb); void NotifyRenderFramebufferSwitched(VirtualFramebuffer *prevVfb, VirtualFramebuffer *vfb, bool isClearingDepth); void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 68f7d8a78a..5cd442f7b9 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -594,7 +594,7 @@ void GameSettingsScreen::CreateViews() { static const char *bufFilters[] = { "Linear", "Nearest", }; graphicsSettings->Add(new PopupMultiChoice(&g_Config.iBufFilter, gr->T("Screen Scaling Filter"), bufFilters, 1, ARRAY_SIZE(bufFilters), gr->GetName(), screenManager())); -#ifdef PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) +#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(IOS) bool showCardboardSettings = deviceType != DEVICE_TYPE_VR; #else // If you enabled it through the ini, you can see this. Useful for testing. From 9fcad8394033f65d1e58aeefe0410f94a3181889 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Aug 2022 10:12:49 -0700 Subject: [PATCH 11/22] riscv: Add initial emitter shell. --- CMakeLists.txt | 2 + Common/Common.vcxproj | 2 + Common/Common.vcxproj.filters | 2 + Common/RiscVEmitter.cpp | 216 ++++++++++++++++++++++++ Common/RiscVEmitter.h | 107 ++++++++++++ UWP/CommonUWP/CommonUWP.vcxproj | 2 + UWP/CommonUWP/CommonUWP.vcxproj.filters | 2 + 7 files changed, 333 insertions(+) create mode 100644 Common/RiscVEmitter.cpp create mode 100644 Common/RiscVEmitter.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 09cae88753..389b2a3983 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -475,6 +475,8 @@ source_group(MIPS FILES ${CommonMIPS}) set(CommonRISCV64 ${CommonJIT} Common/RiscVCPUDetect.cpp + Common/RiscVEmitter.cpp + Common/RiscVEmitter.h Core/MIPS/fake/FakeJit.cpp Core/MIPS/fake/FakeJit.h ) diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 411eda5a1f..341ce1e394 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -475,6 +475,7 @@ + @@ -896,6 +897,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 4e1156f830..cbd9c417b6 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -421,6 +421,7 @@ GPU\Vulkan + @@ -798,6 +799,7 @@ GPU\Vulkan + diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp new file mode 100644 index 0000000000..d1a4fbe7ac --- /dev/null +++ b/Common/RiscVEmitter.cpp @@ -0,0 +1,216 @@ +// Copyright (c) 2022- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "ppsspp_config.h" +#include "Common/RiscVEmitter.h" + +namespace RiscVGen { + +static inline bool SupportsCompressed() { + // TODO + return true; +} + +enum class Opcode32 { + // Note: invalid, just used for FixupBranch. + ZERO = 0b0000000, + SYSTEM = 0b1110011, +}; + +enum class Funct3 { + // Note: invalid, just used for FixupBranch. + ZERO = 0b000, + PRIV = 0b000, +}; + +enum class Funct2 { + // TODO: 0b00, +}; + +enum class Funct7 { + // TODO: 0b0000000, +}; + +enum class Funct12 { + ECALL = 0b000000000000, + EBREAK = 0b000000000001, +}; + +static inline u32 EncodeR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct7 funct7) { + return (u32)opcode | ((u32)rd << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)funct7 << 25); +} + +static inline u32 EncodeR4(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct2 funct2, RiscVReg rs3) { + return (u32)opcode | ((u32)rd << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)funct2 << 25) | ((u32)rs3 << 27); +} + +static inline u32 EncodeI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, s32 simm12) { + _assert_msg_(((simm12 << 20) >> 20) == simm12, "I immediate must be signed s11.0"); + return (u32)opcode | ((u32)rd << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)simm12 << 20); +} + +static inline u32 EncodeI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, Funct12 funct12) { + return EncodeI(opcode, rd, funct3, rs1, (s32)funct12); +} + +static inline u32 EncodeS(Opcode32 opcode, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, s32 simm12) { + _assert_msg_(((simm12 << 20) >> 20) == simm12, "S immediate must be signed s11.0"); + u32 imm4_0 = simm12 & 0x1F; + u32 imm11_5 = (simm12 >> 5) & 0x7F; + return (u32)opcode | ((u32)imm4_0 << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)imm11_5 << 25); +} + +static inline u32 EncodeB(Opcode32 opcode, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, s32 simm13) { + _assert_msg_(((simm13 << 19) >> 19) == simm13, "B immediate must be signed s12.0"); + _assert_msg_((simm13 & 1) == 0, "B immediate must be even"); + u32 imm11 = (simm13 >> 11) & 1; + u32 imm12 = (simm13 >> 12) & 1; + // This weird encoding scheme is to keep most bits the same as S, but keep sign at 31. + u32 imm4_1_11 = (simm13 & 0x1E) | imm11; + u32 imm12_10_5 = (imm12 << 6) | ((simm13 >> 5) & 0x3F); + return (u32)opcode | ((u32)imm4_1_11 << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)imm12_10_5 << 25); +} + +static inline u32 EncodeU(Opcode32 opcode, RiscVReg rd, s32 simm32) { + _assert_msg_((simm32 & 0x0FFF) == 0, "U immediate must not have lower 12 bits set"); + return (u32)opcode | ((u32)rd << 7) | (u32)simm32; +} + +static inline u32 EncodeJ(Opcode32 opcode, RiscVReg rd, s32 simm21) { + _assert_msg_(((simm21 << 11) >> 11) == simm21, "J immediate must be signed s20.0"); + _assert_msg_((simm21 & 1) == 0, "J immediate must be even"); + u32 imm11 = (simm21 >> 11) & 1; + u32 imm20 = (simm21 >> 20) & 1; + u32 imm10_1 = (simm21 >> 1) & 0x03FF; + u32 imm19_12 = (simm21 >> 12) & 0x00FF; + // This encoding scheme tries to keep the bits from B in the same places, plus sign. + u32 imm20_10_1_11_19_12 = (imm20 << 19) | (imm10_1 << 9) | (imm11 << 8) | imm19_12; + return (u32)opcode | ((u32)rd << 7) | ((u32)imm20_10_1_11_19_12 << 12); +} + +RiscVEmitter::RiscVEmitter(const u8 *ptr, u8 *writePtr) { + SetCodePointer(ptr, writePtr); +} + +void RiscVEmitter::SetCodePointer(const u8 *ptr, u8 *writePtr) { + code_ = ptr; + writable_ = writePtr; + lastCacheFlushEnd_ = ptr; +} + +const u8 *RiscVEmitter::GetCodePointer() const { + return code_; +} + +u8 *RiscVEmitter::GetWritableCodePtr() { + return writable_; +} + +void RiscVEmitter::ReserveCodeSpace(u32 bytes) { + _assert_msg_((bytes & 1) == 0, "Code space should be aligned"); + _assert_msg_((bytes & 3) == 0 || SupportsCompressed(), "Code space should be aligned (no compressed)"); + for (u32 i = 0; i < bytes / 4; i++) + EBREAK(); + if (bytes & 2) + Write16(0); +} + +const u8 *RiscVEmitter::AlignCode16() { + int c = int((u64)code_ & 15); + if (c) + ReserveCodeSpace(16 - c); + return code_; +} + +const u8 *RiscVEmitter::AlignCodePage() { + int page_size = GetMemoryProtectPageSize(); + int c = int((u64)code_ & (page_size - 1)); + if (c) + ReserveCodeSpace(page_size - c); + return code_; +} + +void RiscVEmitter::FlushIcache() { + FlushIcacheSection(lastCacheFlushEnd_, code_); + lastCacheFlushEnd_ = code_; +} + +void RiscVEmitter::FlushIcacheSection(const u8 *start, const u8 *end) { +#if PPSSPP_ARCH(RISCV64) + __builtin___clear_cache(start, end); +#endif +} + +void RiscVEmitter::SetJumpTarget(const FixupBranch &branch) { + SetJumpTarget(branch, code_); +} + +void RiscVEmitter::SetJumpTarget(const FixupBranch &branch, const void *dst) { + const intptr_t srcp = (intptr_t)branch.ptr; + const intptr_t dstp = (intptr_t)dst; + const ptrdiff_t writable_delta = writable_ - code_; + u32 *fixup = (u32 *)(branch.ptr + writable_delta); + + _assert_msg_((dstp & 1) == 0, "Destination should be aligned"); + _assert_msg_((dstp & 3) == 0 || SupportsCompressed(), "Destination should be aligned (no compressed)"); + + ptrdiff_t distance = dstp - srcp; + _assert_msg_((distance & 1) == 0, "Distance should be aligned"); + _assert_msg_((distance & 3) == 0 || SupportsCompressed(), "Distance should be aligned (no compressed)"); + + switch (branch.type) { + case FixupBranchType::B: + _assert_msg_(BInRange(branch.ptr, dst), "B destination is too far away (%p -> %p)", branch.ptr, dst); + *fixup = (*fixup & 0x01FFF07F) | EncodeB(Opcode32::ZERO, Funct3::ZERO, R_ZERO, R_ZERO, (s32)distance); + break; + + case FixupBranchType::J: + _assert_msg_(JInRange(branch.ptr, dst), "J destination is too far away (%p -> %p)", branch.ptr, dst); + *fixup = (*fixup & 0x00000FFF) | EncodeJ(Opcode32::ZERO, R_ZERO, (s32)distance); + break; + } +} + +bool RiscVEmitter::BInRange(const void *func) const { + return BInRange(code_, func); +} + +bool RiscVEmitter::JInRange(const void *func) const { + return JInRange(code_, func); +} + +bool RiscVEmitter::BInRange(const void *src, const void *dst) const { + const intptr_t srcp = (intptr_t)src; + const intptr_t dstp = (intptr_t)dst; + ptrdiff_t distance = dstp - srcp; + + return distance <= 0x00000FFE && -distance <= 0x00000FFE; +} + +bool RiscVEmitter::JInRange(const void *src, const void *dst) const { + const intptr_t srcp = (intptr_t)src; + const intptr_t dstp = (intptr_t)dst; + ptrdiff_t distance = dstp - srcp; + + return distance <= 0x000FFFFE && -distance <= 0x000FFFFE; +} + +void RiscVEmitter::EBREAK() { + Write32(EncodeI(Opcode32::SYSTEM, R_ZERO, Funct3::PRIV, R_ZERO, Funct12::EBREAK)); +} + +}; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h new file mode 100644 index 0000000000..41b915d434 --- /dev/null +++ b/Common/RiscVEmitter.h @@ -0,0 +1,107 @@ +// Copyright (c) 2022- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include "Common/CodeBlock.h" +#include "Common/CommonTypes.h" + +namespace RiscVGen { + +enum RiscVReg { + X0 = 0, X1, X2, X3, X4, X5, X6, X7, + X8, X9, X10, X11, X12, X13, X14, X15, + X16, X17, X18, X19, X20, X21, X22, X23, + X24, X25, X26, X27, X28, X29, X30, X31, + + R_ZERO = 0, + R_RA = 1, + R_SP = 2, + R_GP = 3, + R_TP = 4, + R_FP = 8, + + F0 = 0x20, F1, F2, F3, F4, F5, F6, F7, + F8, F9, F10, F11, F12, F13, F14, F15, + F16, F17, F18, F19, F20, F21, F22, F23, + F24, F25, F26, F27, F28, F29, F30, F31, +}; + +inline RiscVReg DecodeReg(RiscVReg reg) { return (RiscVReg)(reg & 0x1F); } + +enum class FixupBranchType { + B, + J, +}; + +struct FixupBranch { + FixupBranch(const u8 *p, FixupBranchType t) : ptr(p), type(t) {} + + const u8 *ptr; + FixupBranchType type; +}; + +class RiscVEmitter { +public: + RiscVEmitter() {} + RiscVEmitter(const u8 *codePtr, u8 *writablePtr); + virtual ~RiscVEmitter() {} + + void SetCodePointer(const u8 *ptr, u8 *writePtr); + const u8 *GetCodePointer() const; + u8 *GetWritableCodePtr(); + + void ReserveCodeSpace(u32 bytes); + const u8 *AlignCode16(); + const u8 *AlignCodePage(); + void FlushIcache(); + void FlushIcacheSection(const u8 *start, const u8 *end); + + void SetJumpTarget(const FixupBranch &branch); + bool BInRange(const void *func) const; + bool JInRange(const void *func) const; + + void EBREAK(); + +private: + void SetJumpTarget(const FixupBranch &branch, const void *dst); + bool BInRange(const void *src, const void *dst) const; + bool JInRange(const void *src, const void *dst) const; + + inline void Write32(u32 value) { + *(u32 *)writable_ = value; + code_ += 4; + writable_ += 4; + } + inline void Write16(u16 value) { + *(u16 *)writable_ = value; + code_ += 2; + writable_ += 2; + } + + const u8 *code_ = nullptr; + u8 *writable_ = nullptr; + const u8 *lastCacheFlushEnd_ = nullptr; +}; + +class MIPSCodeBlock : public CodeBlock { +private: + void PoisonMemory(int offset) override; +}; + +}; diff --git a/UWP/CommonUWP/CommonUWP.vcxproj b/UWP/CommonUWP/CommonUWP.vcxproj index fa40f7130c..eea439c9fd 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj +++ b/UWP/CommonUWP/CommonUWP.vcxproj @@ -479,6 +479,7 @@ + @@ -594,6 +595,7 @@ + diff --git a/UWP/CommonUWP/CommonUWP.vcxproj.filters b/UWP/CommonUWP/CommonUWP.vcxproj.filters index b663fdb11c..2c14f4a533 100644 --- a/UWP/CommonUWP/CommonUWP.vcxproj.filters +++ b/UWP/CommonUWP/CommonUWP.vcxproj.filters @@ -107,6 +107,7 @@ + @@ -404,6 +405,7 @@ + From 1dab9d187522827ec27da3350cfbe36b6e23b573 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Aug 2022 18:01:03 -0700 Subject: [PATCH 12/22] riscv: Emit standard B/U/J type ops. --- Common/RiscVEmitter.cpp | 119 ++++++++++++++++++++++++++++++++++++++++ Common/RiscVEmitter.h | 34 ++++++++++++ 2 files changed, 153 insertions(+) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index d1a4fbe7ac..a32067009f 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -28,13 +28,26 @@ static inline bool SupportsCompressed() { enum class Opcode32 { // Note: invalid, just used for FixupBranch. ZERO = 0b0000000, + AUIPC = 0b0010111, + BRANCH = 0b1100011, + LUI = 0b0110111, + JALR = 0b1100111, + JAL = 0b1101111, SYSTEM = 0b1110011, }; enum class Funct3 { // Note: invalid, just used for FixupBranch. ZERO = 0b000, + PRIV = 0b000, + + BEQ = 0b000, + BNE = 0b001, + BLT = 0b100, + BGE = 0b101, + BLTU = 0b110, + BGEU = 0b111, }; enum class Funct2 { @@ -213,4 +226,110 @@ void RiscVEmitter::EBREAK() { Write32(EncodeI(Opcode32::SYSTEM, R_ZERO, Funct3::PRIV, R_ZERO, Funct12::EBREAK)); } +void RiscVEmitter::LUI(RiscVReg rd, s32 simm32) { + _assert_(rd != R_ZERO); + Write32(EncodeU(Opcode32::LUI, rd, simm32)); +} + +void RiscVEmitter::AUIPC(RiscVReg rd, s32 simm32) { + _assert_(rd != R_ZERO); + Write32(EncodeU(Opcode32::AUIPC, rd, simm32)); +} + +void RiscVEmitter::JAL(RiscVReg rd, const void *dst) { + _assert_msg_(JInRange(GetCodePointer(), dst), "JAL destination is too far away (%p -> %p)", GetCodePointer(), dst); + _assert_msg_(((intptr_t)dst & 1) == 0, "JAL destination should be aligned"); + _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "JAL destination should be aligned (no compressed)"); + ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); + Write32(EncodeJ(Opcode32::JAL, rd, (s32)distance)); +} + +void RiscVEmitter::JALR(RiscVReg rd, RiscVReg rs1, s32 simm12) { + Write32(EncodeI(Opcode32::JALR, rd, Funct3::ZERO, rs1, simm12)); +} + +FixupBranch RiscVEmitter::JAL(RiscVReg rd) { + FixupBranch fixup{ GetCodePointer(), FixupBranchType::J }; + Write32(EncodeJ(Opcode32::JAL, rd, 0)); + return fixup; +} + +void RiscVEmitter::BEQ(RiscVReg rs1, RiscVReg rs2, const void *dst) { + _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); + _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); + ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); + Write32(EncodeB(Opcode32::BRANCH, Funct3::BEQ, rs1, rs2, (s32)distance)); +} + +void RiscVEmitter::BNE(RiscVReg rs1, RiscVReg rs2, const void *dst) { + _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); + _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); + ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); + Write32(EncodeB(Opcode32::BRANCH, Funct3::BNE, rs1, rs2, (s32)distance)); +} + +void RiscVEmitter::BLT(RiscVReg rs1, RiscVReg rs2, const void *dst) { + _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); + _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); + ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); + Write32(EncodeB(Opcode32::BRANCH, Funct3::BLT, rs1, rs2, (s32)distance)); +} + +void RiscVEmitter::BGE(RiscVReg rs1, RiscVReg rs2, const void *dst) { + _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); + _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); + ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); + Write32(EncodeB(Opcode32::BRANCH, Funct3::BGE, rs1, rs2, (s32)distance)); +} + +void RiscVEmitter::BLTU(RiscVReg rs1, RiscVReg rs2, const void *dst) { + _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); + _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); + ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); + Write32(EncodeB(Opcode32::BRANCH, Funct3::BLTU, rs1, rs2, (s32)distance)); +} + +void RiscVEmitter::BGEU(RiscVReg rs1, RiscVReg rs2, const void *dst) { + _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); + _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); + ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); + Write32(EncodeB(Opcode32::BRANCH, Funct3::BGEU, rs1, rs2, (s32)distance)); +} + +FixupBranch RiscVEmitter::BEQ(RiscVReg rs1, RiscVReg rs2) { + FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; + Write32(EncodeB(Opcode32::BRANCH, Funct3::BEQ, rs1, rs2, 0)); + return fixup; +} + +FixupBranch RiscVEmitter::BNE(RiscVReg rs1, RiscVReg rs2) { + FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; + Write32(EncodeB(Opcode32::BRANCH, Funct3::BNE, rs1, rs2, 0)); + return fixup; +} + +FixupBranch RiscVEmitter::BLT(RiscVReg rs1, RiscVReg rs2) { + FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; + Write32(EncodeB(Opcode32::BRANCH, Funct3::BLT, rs1, rs2, 0)); + return fixup; +} + +FixupBranch RiscVEmitter::BGE(RiscVReg rs1, RiscVReg rs2) { + FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; + Write32(EncodeB(Opcode32::BRANCH, Funct3::BGE, rs1, rs2, 0)); + return fixup; +} + +FixupBranch RiscVEmitter::BLTU(RiscVReg rs1, RiscVReg rs2) { + FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; + Write32(EncodeB(Opcode32::BRANCH, Funct3::BLTU, rs1, rs2, 0)); + return fixup; +} + +FixupBranch RiscVEmitter::BGEU(RiscVReg rs1, RiscVReg rs2) { + FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; + Write32(EncodeB(Opcode32::BRANCH, Funct3::BGEU, rs1, rs2, 0)); + return fixup; +} + }; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 41b915d434..0205a1a4a4 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -78,6 +78,40 @@ public: void EBREAK(); + void LUI(RiscVReg rd, s32 simm32); + void AUIPC(RiscVReg rd, s32 simm32); + + void JAL(RiscVReg rd, const void *dst); + void JALR(RiscVReg rd, RiscVReg rs1, s32 simm12); + FixupBranch JAL(RiscVReg rd); + + // Psuedo-instructions for convenience/clarity. + void J(const void *dst) { + JAL(R_ZERO, dst); + } + void JR(RiscVReg rs1, u32 simm12 = 0) { + JALR(R_ZERO, rs1, simm12); + } + void RET() { + JR(R_RA); + } + FixupBranch J() { + return JAL(R_ZERO); + } + + void BEQ(RiscVReg rs1, RiscVReg rs2, const void *dst); + void BNE(RiscVReg rs1, RiscVReg rs2, const void *dst); + void BLT(RiscVReg rs1, RiscVReg rs2, const void *dst); + void BGE(RiscVReg rs1, RiscVReg rs2, const void *dst); + void BLTU(RiscVReg rs1, RiscVReg rs2, const void *dst); + void BGEU(RiscVReg rs1, RiscVReg rs2, const void *dst); + FixupBranch BEQ(RiscVReg rs1, RiscVReg rs2); + FixupBranch BNE(RiscVReg rs1, RiscVReg rs2); + FixupBranch BLT(RiscVReg rs1, RiscVReg rs2); + FixupBranch BGE(RiscVReg rs1, RiscVReg rs2); + FixupBranch BLTU(RiscVReg rs1, RiscVReg rs2); + FixupBranch BGEU(RiscVReg rs1, RiscVReg rs2); + private: void SetJumpTarget(const FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const; From 1c35cfb325f035f19a41910ffec289c22ee3cda1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Aug 2022 19:19:32 -0700 Subject: [PATCH 13/22] riscv: Emit 32-bit load/store and imm ops. --- Common/RiscVEmitter.cpp | 111 +++++++++++++++++++++++++++++++++++++++- Common/RiscVEmitter.h | 31 +++++++++++ 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index a32067009f..e509d2a05d 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -25,10 +25,18 @@ static inline bool SupportsCompressed() { return true; } +static inline uint8_t BitsSupported() { + // TODO + return 64; +} + enum class Opcode32 { // Note: invalid, just used for FixupBranch. ZERO = 0b0000000, + LOAD = 0b0000011, + OP_IMM = 0b0010011, AUIPC = 0b0010111, + STORE = 0b0100011, BRANCH = 0b1100011, LUI = 0b0110111, JALR = 0b1100111, @@ -48,6 +56,21 @@ enum class Funct3 { BGE = 0b101, BLTU = 0b110, BGEU = 0b111, + + LS_B = 0b000, + LS_H = 0b001, + LS_W = 0b010, + LS_BU = 0b100, + LS_HU = 0b101, + + ADDI = 0b000, + SLLI = 0b001, + SLTI = 0b010, + SLTIU = 0b011, + XORI = 0b100, + SRLI = 0b101, + ORI = 0b110, + ANDI = 0b111, }; enum class Funct2 { @@ -227,12 +250,12 @@ void RiscVEmitter::EBREAK() { } void RiscVEmitter::LUI(RiscVReg rd, s32 simm32) { - _assert_(rd != R_ZERO); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); Write32(EncodeU(Opcode32::LUI, rd, simm32)); } void RiscVEmitter::AUIPC(RiscVReg rd, s32 simm32) { - _assert_(rd != R_ZERO); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); Write32(EncodeU(Opcode32::AUIPC, rd, simm32)); } @@ -332,4 +355,88 @@ FixupBranch RiscVEmitter::BGEU(RiscVReg rs1, RiscVReg rs2) { return fixup; } +void RiscVEmitter::LB(RiscVReg rd, RiscVReg rs1, s32 simm12) { + Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_B, rs1, simm12)); +} + +void RiscVEmitter::LH(RiscVReg rd, RiscVReg rs1, s32 simm12) { + Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_H, rs1, simm12)); +} + +void RiscVEmitter::LW(RiscVReg rd, RiscVReg rs1, s32 simm12) { + Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_W, rs1, simm12)); +} + +void RiscVEmitter::LBU(RiscVReg rd, RiscVReg rs1, s32 simm12) { + Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_BU, rs1, simm12)); +} + +void RiscVEmitter::LHU(RiscVReg rd, RiscVReg rs1, s32 simm12) { + Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_HU, rs1, simm12)); +} + +void RiscVEmitter::SB(RiscVReg rs2, RiscVReg rs1, s32 simm12) { + Write32(EncodeS(Opcode32::STORE, Funct3::LS_B, rs1, rs2, simm12)); +} + +void RiscVEmitter::SH(RiscVReg rs2, RiscVReg rs1, s32 simm12) { + Write32(EncodeS(Opcode32::STORE, Funct3::LS_H, rs1, rs2, simm12)); +} + +void RiscVEmitter::SW(RiscVReg rs2, RiscVReg rs1, s32 simm12) { + Write32(EncodeS(Opcode32::STORE, Funct3::LS_W, rs1, rs2, simm12)); +} + +void RiscVEmitter::ADDI(RiscVReg rd, RiscVReg rs1, s32 simm12) { + // Allow NOP form of ADDI. + _assert_msg_(rd != R_ZERO || (rs1 == R_ZERO && simm12 == 0), "%s write to zero is a HINT", __func__); + Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::ADDI, rs1, simm12)); +} + +void RiscVEmitter::SLTI(RiscVReg rd, RiscVReg rs1, s32 simm12) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SLTI, rs1, simm12)); +} + +void RiscVEmitter::SLTIU(RiscVReg rd, RiscVReg rs1, s32 simm12) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SLTIU, rs1, simm12)); +} + +void RiscVEmitter::XORI(RiscVReg rd, RiscVReg rs1, s32 simm12) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::XORI, rs1, simm12)); +} + +void RiscVEmitter::ORI(RiscVReg rd, RiscVReg rs1, s32 simm12) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::ORI, rs1, simm12)); +} + +void RiscVEmitter::ANDI(RiscVReg rd, RiscVReg rs1, s32 simm12) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::ANDI, rs1, simm12)); +} + +void RiscVEmitter::SLLI(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + // Not sure if shamt=0 is legal or not, let's play it safe. + _assert_msg_(shamt > 0 && shamt < BitsSupported(), "Shift out of range"); + Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SLLI, rs1, shamt)); +} + +void RiscVEmitter::SRLI(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + // Not sure if shamt=0 is legal or not, let's play it safe. + _assert_msg_(shamt > 0 && shamt < BitsSupported(), "Shift out of range"); + Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SRLI, rs1, shamt)); +} + +void RiscVEmitter::SRAI(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + // Not sure if shamt=0 is legal or not, let's play it safe. + _assert_msg_(shamt > 0 && shamt < BitsSupported(), "Shift out of range"); + Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SRLI, rs1, shamt | (1 << 10))); +} + }; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 0205a1a4a4..ec8de871d1 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -112,6 +112,37 @@ public: FixupBranch BLTU(RiscVReg rs1, RiscVReg rs2); FixupBranch BGEU(RiscVReg rs1, RiscVReg rs2); + void LB(RiscVReg rd, RiscVReg rs1, s32 simm12); + void LH(RiscVReg rd, RiscVReg rs1, s32 simm12); + void LW(RiscVReg rd, RiscVReg rs1, s32 simm12); + void LBU(RiscVReg rd, RiscVReg rs1, s32 simm12); + void LHU(RiscVReg rd, RiscVReg rs1, s32 simm12); + + void SB(RiscVReg rs2, RiscVReg rs1, s32 simm12); + void SH(RiscVReg rs2, RiscVReg rs1, s32 simm12); + void SW(RiscVReg rs2, RiscVReg rs1, s32 simm12); + + void ADDI(RiscVReg rd, RiscVReg rs1, s32 simm12); + void SLTI(RiscVReg rd, RiscVReg rs1, s32 simm12); + void SLTIU(RiscVReg rd, RiscVReg rs1, s32 simm12); + void XORI(RiscVReg rd, RiscVReg rs1, s32 simm12); + void ORI(RiscVReg rd, RiscVReg rs1, s32 simm12); + void ANDI(RiscVReg rd, RiscVReg rs1, s32 simm12); + + void NOP() { + ADDI(R_ZERO, R_ZERO, 0); + } + void MV(RiscVReg rd, RiscVReg rs1) { + ADDI(rd, rs1, 0); + } + void NOT(RiscVReg rd, RiscVReg rs1) { + XORI(rd, rs1, -1); + } + + void SLLI(RiscVReg rd, RiscVReg rs1, u32 shamt); + void SRLI(RiscVReg rd, RiscVReg rs1, u32 shamt); + void SRAI(RiscVReg rd, RiscVReg rs1, u32 shamt); + private: void SetJumpTarget(const FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const; From 49ec8a207d8d146f2d6a050592151eee8ed2570a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Aug 2022 19:44:29 -0700 Subject: [PATCH 14/22] riscv: Emit 32-bit 3-op instructions, validate GPR. --- Common/RiscVEmitter.cpp | 201 ++++++++++++++++++++++++++++++---------- Common/RiscVEmitter.h | 13 ++- 2 files changed, 161 insertions(+), 53 deletions(-) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index e509d2a05d..ed02fdac7c 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -37,8 +37,9 @@ enum class Opcode32 { OP_IMM = 0b0010011, AUIPC = 0b0010111, STORE = 0b0100011, - BRANCH = 0b1100011, + OP = 0b0110011, LUI = 0b0110111, + BRANCH = 0b1100011, JALR = 0b1100111, JAL = 0b1101111, SYSTEM = 0b1110011, @@ -63,14 +64,14 @@ enum class Funct3 { LS_BU = 0b100, LS_HU = 0b101, - ADDI = 0b000, - SLLI = 0b001, - SLTI = 0b010, - SLTIU = 0b011, - XORI = 0b100, - SRLI = 0b101, - ORI = 0b110, - ANDI = 0b111, + ADD = 0b000, + SLL = 0b001, + SLT = 0b010, + SLTU = 0b011, + XOR = 0b100, + SRL = 0b101, + OR = 0b110, + AND = 0b111, }; enum class Funct2 { @@ -78,7 +79,10 @@ enum class Funct2 { }; enum class Funct7 { - // TODO: 0b0000000, + ZERO = 0b0000000, + + SUB = 0b0100000, + SRA = 0b0100000, }; enum class Funct12 { @@ -86,28 +90,57 @@ enum class Funct12 { EBREAK = 0b000000000001, }; +static inline RiscVReg DecodeReg(RiscVReg reg) { return (RiscVReg)(reg & 0x1F); } +static inline bool IsGPR(RiscVReg reg) { return reg < 0x20; } +static inline bool IsFPR(RiscVReg reg) { return (reg & 0x20) != 0 && (int)reg < 0x40; } + static inline u32 EncodeR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct7 funct7) { - return (u32)opcode | ((u32)rd << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)funct7 << 25); + return (u32)opcode | ((u32)DecodeReg(rd) << 7) | ((u32)funct3 << 12) | ((u32)DecodeReg(rs1) << 15) | ((u32)DecodeReg(rs2) << 20) | ((u32)funct7 << 25); +} + +static inline u32 EncodeGR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct7 funct7) { + _assert_msg_(IsGPR(rd), "R instruction rd must be GPR"); + _assert_msg_(IsGPR(rs1), "R instruction rs1 must be GPR"); + _assert_msg_(IsGPR(rs2), "R instruction rs2 must be GPR"); + return EncodeR(opcode, rd, funct3, rs1, rs2, funct7); } static inline u32 EncodeR4(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct2 funct2, RiscVReg rs3) { - return (u32)opcode | ((u32)rd << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)funct2 << 25) | ((u32)rs3 << 27); + return (u32)opcode | ((u32)DecodeReg(rd) << 7) | ((u32)funct3 << 12) | ((u32)DecodeReg(rs1) << 15) | ((u32)DecodeReg(rs2) << 20) | ((u32)funct2 << 25) | ((u32)DecodeReg(rs3) << 27); } static inline u32 EncodeI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, s32 simm12) { _assert_msg_(((simm12 << 20) >> 20) == simm12, "I immediate must be signed s11.0"); - return (u32)opcode | ((u32)rd << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)simm12 << 20); + return (u32)opcode | ((u32)DecodeReg(rd) << 7) | ((u32)funct3 << 12) | ((u32)DecodeReg(rs1) << 15) | ((u32)simm12 << 20); +} + +static inline u32 EncodeGI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, s32 simm12) { + _assert_msg_(IsGPR(rd), "I instruction rd must be GPR"); + _assert_msg_(IsGPR(rs1), "I instruction rs1 must be GPR"); + return EncodeI(opcode, rd, funct3, rs1, simm12); } static inline u32 EncodeI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, Funct12 funct12) { return EncodeI(opcode, rd, funct3, rs1, (s32)funct12); } +static inline u32 EncodeGI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, Funct12 funct12) { + _assert_msg_(IsGPR(rd), "I instruction rd must be GPR"); + _assert_msg_(IsGPR(rs1), "I instruction rs1 must be GPR"); + return EncodeI(opcode, rd, funct3, rs1, funct12); +} + static inline u32 EncodeS(Opcode32 opcode, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, s32 simm12) { _assert_msg_(((simm12 << 20) >> 20) == simm12, "S immediate must be signed s11.0"); u32 imm4_0 = simm12 & 0x1F; u32 imm11_5 = (simm12 >> 5) & 0x7F; - return (u32)opcode | ((u32)imm4_0 << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)imm11_5 << 25); + return (u32)opcode | ((u32)imm4_0 << 7) | ((u32)funct3 << 12) | ((u32)DecodeReg(rs1) << 15) | ((u32)DecodeReg(rs2) << 20) | ((u32)imm11_5 << 25); +} + +static inline u32 EncodeGS(Opcode32 opcode, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, s32 simm12) { + _assert_msg_(IsGPR(rs1), "S instruction rs1 must be GPR"); + _assert_msg_(IsGPR(rs2), "S instruction rs2 must be GPR"); + return EncodeS(opcode, funct3, rs1, rs2, simm12); } static inline u32 EncodeB(Opcode32 opcode, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, s32 simm13) { @@ -118,12 +151,23 @@ static inline u32 EncodeB(Opcode32 opcode, Funct3 funct3, RiscVReg rs1, RiscVReg // This weird encoding scheme is to keep most bits the same as S, but keep sign at 31. u32 imm4_1_11 = (simm13 & 0x1E) | imm11; u32 imm12_10_5 = (imm12 << 6) | ((simm13 >> 5) & 0x3F); - return (u32)opcode | ((u32)imm4_1_11 << 7) | ((u32)funct3 << 12) | ((u32)rs1 << 15) | ((u32)rs2 << 20) | ((u32)imm12_10_5 << 25); + return (u32)opcode | ((u32)imm4_1_11 << 7) | ((u32)funct3 << 12) | ((u32)DecodeReg(rs1) << 15) | ((u32)DecodeReg(rs2) << 20) | ((u32)imm12_10_5 << 25); +} + +static inline u32 EncodeGB(Opcode32 opcode, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, s32 simm13) { + _assert_msg_(IsGPR(rs1), "B instruction rs1 must be GPR"); + _assert_msg_(IsGPR(rs2), "B instruction rs2 must be GPR"); + return EncodeB(opcode, funct3, rs1, rs2, simm13); } static inline u32 EncodeU(Opcode32 opcode, RiscVReg rd, s32 simm32) { _assert_msg_((simm32 & 0x0FFF) == 0, "U immediate must not have lower 12 bits set"); - return (u32)opcode | ((u32)rd << 7) | (u32)simm32; + return (u32)opcode | ((u32)DecodeReg(rd) << 7) | (u32)simm32; +} + +static inline u32 EncodeGU(Opcode32 opcode, RiscVReg rd, s32 simm32) { + _assert_msg_(IsGPR(rd), "I instruction rd must be GPR"); + return EncodeU(opcode, rd, simm32); } static inline u32 EncodeJ(Opcode32 opcode, RiscVReg rd, s32 simm21) { @@ -135,7 +179,12 @@ static inline u32 EncodeJ(Opcode32 opcode, RiscVReg rd, s32 simm21) { u32 imm19_12 = (simm21 >> 12) & 0x00FF; // This encoding scheme tries to keep the bits from B in the same places, plus sign. u32 imm20_10_1_11_19_12 = (imm20 << 19) | (imm10_1 << 9) | (imm11 << 8) | imm19_12; - return (u32)opcode | ((u32)rd << 7) | ((u32)imm20_10_1_11_19_12 << 12); + return (u32)opcode | ((u32)DecodeReg(rd) << 7) | ((u32)imm20_10_1_11_19_12 << 12); +} + +static inline u32 EncodeGJ(Opcode32 opcode, RiscVReg rd, s32 simm21) { + _assert_msg_(IsGPR(rd), "J instruction rd must be GPR"); + return EncodeJ(opcode, rd, simm21); } RiscVEmitter::RiscVEmitter(const u8 *ptr, u8 *writePtr) { @@ -251,12 +300,12 @@ void RiscVEmitter::EBREAK() { void RiscVEmitter::LUI(RiscVReg rd, s32 simm32) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); - Write32(EncodeU(Opcode32::LUI, rd, simm32)); + Write32(EncodeGU(Opcode32::LUI, rd, simm32)); } void RiscVEmitter::AUIPC(RiscVReg rd, s32 simm32) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); - Write32(EncodeU(Opcode32::AUIPC, rd, simm32)); + Write32(EncodeGU(Opcode32::AUIPC, rd, simm32)); } void RiscVEmitter::JAL(RiscVReg rd, const void *dst) { @@ -264,16 +313,16 @@ void RiscVEmitter::JAL(RiscVReg rd, const void *dst) { _assert_msg_(((intptr_t)dst & 1) == 0, "JAL destination should be aligned"); _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "JAL destination should be aligned (no compressed)"); ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); - Write32(EncodeJ(Opcode32::JAL, rd, (s32)distance)); + Write32(EncodeGJ(Opcode32::JAL, rd, (s32)distance)); } void RiscVEmitter::JALR(RiscVReg rd, RiscVReg rs1, s32 simm12) { - Write32(EncodeI(Opcode32::JALR, rd, Funct3::ZERO, rs1, simm12)); + Write32(EncodeGI(Opcode32::JALR, rd, Funct3::ZERO, rs1, simm12)); } FixupBranch RiscVEmitter::JAL(RiscVReg rd) { FixupBranch fixup{ GetCodePointer(), FixupBranchType::J }; - Write32(EncodeJ(Opcode32::JAL, rd, 0)); + Write32(EncodeGJ(Opcode32::JAL, rd, 0)); return fixup; } @@ -281,162 +330,212 @@ void RiscVEmitter::BEQ(RiscVReg rs1, RiscVReg rs2, const void *dst) { _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); - Write32(EncodeB(Opcode32::BRANCH, Funct3::BEQ, rs1, rs2, (s32)distance)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BEQ, rs1, rs2, (s32)distance)); } void RiscVEmitter::BNE(RiscVReg rs1, RiscVReg rs2, const void *dst) { _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); - Write32(EncodeB(Opcode32::BRANCH, Funct3::BNE, rs1, rs2, (s32)distance)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BNE, rs1, rs2, (s32)distance)); } void RiscVEmitter::BLT(RiscVReg rs1, RiscVReg rs2, const void *dst) { _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); - Write32(EncodeB(Opcode32::BRANCH, Funct3::BLT, rs1, rs2, (s32)distance)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BLT, rs1, rs2, (s32)distance)); } void RiscVEmitter::BGE(RiscVReg rs1, RiscVReg rs2, const void *dst) { _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); - Write32(EncodeB(Opcode32::BRANCH, Funct3::BGE, rs1, rs2, (s32)distance)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BGE, rs1, rs2, (s32)distance)); } void RiscVEmitter::BLTU(RiscVReg rs1, RiscVReg rs2, const void *dst) { _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); - Write32(EncodeB(Opcode32::BRANCH, Funct3::BLTU, rs1, rs2, (s32)distance)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BLTU, rs1, rs2, (s32)distance)); } void RiscVEmitter::BGEU(RiscVReg rs1, RiscVReg rs2, const void *dst) { _assert_msg_(BInRange(GetCodePointer(), dst), "%s destination is too far away (%p -> %p)", __func__, GetCodePointer(), dst); _assert_msg_(((intptr_t)dst & 3) == 0 || SupportsCompressed(), "%s destination should be aligned (no compressed)", __func__); ptrdiff_t distance = (intptr_t)dst - (intptr_t)GetCodePointer(); - Write32(EncodeB(Opcode32::BRANCH, Funct3::BGEU, rs1, rs2, (s32)distance)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BGEU, rs1, rs2, (s32)distance)); } FixupBranch RiscVEmitter::BEQ(RiscVReg rs1, RiscVReg rs2) { FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; - Write32(EncodeB(Opcode32::BRANCH, Funct3::BEQ, rs1, rs2, 0)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BEQ, rs1, rs2, 0)); return fixup; } FixupBranch RiscVEmitter::BNE(RiscVReg rs1, RiscVReg rs2) { FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; - Write32(EncodeB(Opcode32::BRANCH, Funct3::BNE, rs1, rs2, 0)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BNE, rs1, rs2, 0)); return fixup; } FixupBranch RiscVEmitter::BLT(RiscVReg rs1, RiscVReg rs2) { FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; - Write32(EncodeB(Opcode32::BRANCH, Funct3::BLT, rs1, rs2, 0)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BLT, rs1, rs2, 0)); return fixup; } FixupBranch RiscVEmitter::BGE(RiscVReg rs1, RiscVReg rs2) { FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; - Write32(EncodeB(Opcode32::BRANCH, Funct3::BGE, rs1, rs2, 0)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BGE, rs1, rs2, 0)); return fixup; } FixupBranch RiscVEmitter::BLTU(RiscVReg rs1, RiscVReg rs2) { FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; - Write32(EncodeB(Opcode32::BRANCH, Funct3::BLTU, rs1, rs2, 0)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BLTU, rs1, rs2, 0)); return fixup; } FixupBranch RiscVEmitter::BGEU(RiscVReg rs1, RiscVReg rs2) { FixupBranch fixup{ GetCodePointer(), FixupBranchType::B }; - Write32(EncodeB(Opcode32::BRANCH, Funct3::BGEU, rs1, rs2, 0)); + Write32(EncodeGB(Opcode32::BRANCH, Funct3::BGEU, rs1, rs2, 0)); return fixup; } void RiscVEmitter::LB(RiscVReg rd, RiscVReg rs1, s32 simm12) { - Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_B, rs1, simm12)); + Write32(EncodeGI(Opcode32::LOAD, rd, Funct3::LS_B, rs1, simm12)); } void RiscVEmitter::LH(RiscVReg rd, RiscVReg rs1, s32 simm12) { - Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_H, rs1, simm12)); + Write32(EncodeGI(Opcode32::LOAD, rd, Funct3::LS_H, rs1, simm12)); } void RiscVEmitter::LW(RiscVReg rd, RiscVReg rs1, s32 simm12) { - Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_W, rs1, simm12)); + Write32(EncodeGI(Opcode32::LOAD, rd, Funct3::LS_W, rs1, simm12)); } void RiscVEmitter::LBU(RiscVReg rd, RiscVReg rs1, s32 simm12) { - Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_BU, rs1, simm12)); + Write32(EncodeGI(Opcode32::LOAD, rd, Funct3::LS_BU, rs1, simm12)); } void RiscVEmitter::LHU(RiscVReg rd, RiscVReg rs1, s32 simm12) { - Write32(EncodeI(Opcode32::LOAD, rd, Funct3::LS_HU, rs1, simm12)); + Write32(EncodeGI(Opcode32::LOAD, rd, Funct3::LS_HU, rs1, simm12)); } void RiscVEmitter::SB(RiscVReg rs2, RiscVReg rs1, s32 simm12) { - Write32(EncodeS(Opcode32::STORE, Funct3::LS_B, rs1, rs2, simm12)); + Write32(EncodeGS(Opcode32::STORE, Funct3::LS_B, rs1, rs2, simm12)); } void RiscVEmitter::SH(RiscVReg rs2, RiscVReg rs1, s32 simm12) { - Write32(EncodeS(Opcode32::STORE, Funct3::LS_H, rs1, rs2, simm12)); + Write32(EncodeGS(Opcode32::STORE, Funct3::LS_H, rs1, rs2, simm12)); } void RiscVEmitter::SW(RiscVReg rs2, RiscVReg rs1, s32 simm12) { - Write32(EncodeS(Opcode32::STORE, Funct3::LS_W, rs1, rs2, simm12)); + Write32(EncodeGS(Opcode32::STORE, Funct3::LS_W, rs1, rs2, simm12)); } void RiscVEmitter::ADDI(RiscVReg rd, RiscVReg rs1, s32 simm12) { // Allow NOP form of ADDI. _assert_msg_(rd != R_ZERO || (rs1 == R_ZERO && simm12 == 0), "%s write to zero is a HINT", __func__); - Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::ADDI, rs1, simm12)); + Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::ADD, rs1, simm12)); } void RiscVEmitter::SLTI(RiscVReg rd, RiscVReg rs1, s32 simm12) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); - Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SLTI, rs1, simm12)); + Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::SLT, rs1, simm12)); } void RiscVEmitter::SLTIU(RiscVReg rd, RiscVReg rs1, s32 simm12) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); - Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SLTIU, rs1, simm12)); + Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::SLTU, rs1, simm12)); } void RiscVEmitter::XORI(RiscVReg rd, RiscVReg rs1, s32 simm12) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); - Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::XORI, rs1, simm12)); + Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::XOR, rs1, simm12)); } void RiscVEmitter::ORI(RiscVReg rd, RiscVReg rs1, s32 simm12) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); - Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::ORI, rs1, simm12)); + Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::OR, rs1, simm12)); } void RiscVEmitter::ANDI(RiscVReg rd, RiscVReg rs1, s32 simm12) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); - Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::ANDI, rs1, simm12)); + Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::AND, rs1, simm12)); } void RiscVEmitter::SLLI(RiscVReg rd, RiscVReg rs1, u32 shamt) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); // Not sure if shamt=0 is legal or not, let's play it safe. _assert_msg_(shamt > 0 && shamt < BitsSupported(), "Shift out of range"); - Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SLLI, rs1, shamt)); + Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::SLL, rs1, shamt)); } void RiscVEmitter::SRLI(RiscVReg rd, RiscVReg rs1, u32 shamt) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); // Not sure if shamt=0 is legal or not, let's play it safe. _assert_msg_(shamt > 0 && shamt < BitsSupported(), "Shift out of range"); - Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SRLI, rs1, shamt)); + Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::SRL, rs1, shamt)); } void RiscVEmitter::SRAI(RiscVReg rd, RiscVReg rs1, u32 shamt) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); // Not sure if shamt=0 is legal or not, let's play it safe. _assert_msg_(shamt > 0 && shamt < BitsSupported(), "Shift out of range"); - Write32(EncodeI(Opcode32::OP_IMM, rd, Funct3::SRLI, rs1, shamt | (1 << 10))); + Write32(EncodeGI(Opcode32::OP_IMM, rd, Funct3::SRL, rs1, shamt | (1 << 10))); +} + +void RiscVEmitter::ADD(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::ADD, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::SUB(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::ADD, rs1, rs2, Funct7::SUB)); +} + +void RiscVEmitter::SLL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::SLL, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::SLT(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::SLT, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::SLTU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::SLTU, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::XOR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::XOR, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::SRL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::SRL, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::SRA(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::SRL, rs1, rs2, Funct7::SRA)); +} + +void RiscVEmitter::OR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::OR, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::AND(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::AND, rs1, rs2, Funct7::ZERO)); } }; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index ec8de871d1..4a78a68c51 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -42,8 +42,6 @@ enum RiscVReg { F24, F25, F26, F27, F28, F29, F30, F31, }; -inline RiscVReg DecodeReg(RiscVReg reg) { return (RiscVReg)(reg & 0x1F); } - enum class FixupBranchType { B, J, @@ -143,6 +141,17 @@ public: void SRLI(RiscVReg rd, RiscVReg rs1, u32 shamt); void SRAI(RiscVReg rd, RiscVReg rs1, u32 shamt); + void ADD(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SUB(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SLL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SLT(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SLTU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void XOR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SRL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SRA(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void OR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void AND(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + private: void SetJumpTarget(const FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const; From 00280ab2ad35148f93577b4f9600f51a631b326a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Aug 2022 20:33:36 -0700 Subject: [PATCH 15/22] riscv: Emit fence and 64-bit instructions. --- Common/RiscVEmitter.cpp | 105 ++++++++++++++++++++++++++++++++++++++-- Common/RiscVEmitter.h | 42 ++++++++++++++-- 2 files changed, 140 insertions(+), 7 deletions(-) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index ed02fdac7c..48eda83ee0 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -34,11 +34,14 @@ enum class Opcode32 { // Note: invalid, just used for FixupBranch. ZERO = 0b0000000, LOAD = 0b0000011, + MISC_MEM = 0b0001111, OP_IMM = 0b0010011, AUIPC = 0b0010111, + OP_IMM_32 = 0b0011011, STORE = 0b0100011, OP = 0b0110011, LUI = 0b0110111, + OP_32 = 0b0111011, BRANCH = 0b1100011, JALR = 0b1100111, JAL = 0b1101111, @@ -51,6 +54,8 @@ enum class Funct3 { PRIV = 0b000, + FENCE = 0b000, + BEQ = 0b000, BNE = 0b001, BLT = 0b100, @@ -61,8 +66,10 @@ enum class Funct3 { LS_B = 0b000, LS_H = 0b001, LS_W = 0b010, + LS_D = 0b011, LS_BU = 0b100, LS_HU = 0b101, + LS_WU = 0b110, ADD = 0b000, SLL = 0b001, @@ -294,10 +301,6 @@ bool RiscVEmitter::JInRange(const void *src, const void *dst) const { return distance <= 0x000FFFFE && -distance <= 0x000FFFFE; } -void RiscVEmitter::EBREAK() { - Write32(EncodeI(Opcode32::SYSTEM, R_ZERO, Funct3::PRIV, R_ZERO, Funct12::EBREAK)); -} - void RiscVEmitter::LUI(RiscVReg rd, s32 simm32) { _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); Write32(EncodeGU(Opcode32::LUI, rd, simm32)); @@ -538,4 +541,98 @@ void RiscVEmitter::AND(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { Write32(EncodeGR(Opcode32::OP, rd, Funct3::AND, rs1, rs2, Funct7::ZERO)); } +void RiscVEmitter::FENCE(Fence predecessor, Fence successor) { + _assert_msg_((u32)predecessor != 0 && (u32)successor != 0, "FENCE missing pred/succ"); + s32 simm12 = ((u32)predecessor << 4) | (u32)successor; + Write32(EncodeI(Opcode32::MISC_MEM, R_ZERO, Funct3::FENCE, R_ZERO, simm12)); +} + +void RiscVEmitter::FENCE_TSO() { + s32 simm12 = (0b1000 << 28) | ((u32)Fence::RW << 4) | (u32)Fence::RW; + Write32(EncodeI(Opcode32::MISC_MEM, R_ZERO, Funct3::FENCE, R_ZERO, simm12)); +} + +void RiscVEmitter::ECALL() { + Write32(EncodeI(Opcode32::SYSTEM, R_ZERO, Funct3::PRIV, R_ZERO, Funct12::ECALL)); +} + +void RiscVEmitter::EBREAK() { + Write32(EncodeI(Opcode32::SYSTEM, R_ZERO, Funct3::PRIV, R_ZERO, Funct12::EBREAK)); +} + +void RiscVEmitter::LWU(RiscVReg rd, RiscVReg rs1, s32 simm12) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + Write32(EncodeGI(Opcode32::LOAD, rd, Funct3::LS_WU, rs1, simm12)); +} + +void RiscVEmitter::LD(RiscVReg rd, RiscVReg rs1, s32 simm12) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + Write32(EncodeGI(Opcode32::LOAD, rd, Funct3::LS_D, rs1, simm12)); +} + +void RiscVEmitter::SD(RiscVReg rs2, RiscVReg rs1, s32 simm12) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + Write32(EncodeGS(Opcode32::STORE, Funct3::LS_D, rs1, rs2, simm12)); +} + +void RiscVEmitter::ADDIW(RiscVReg rd, RiscVReg rs1, s32 simm12) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGI(Opcode32::OP_IMM_32, rd, Funct3::ADD, rs1, simm12)); +} + +void RiscVEmitter::SLLIW(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + // Not sure if shamt=0 is legal or not, let's play it safe. + _assert_msg_(shamt > 0 && shamt < 32, "Shift out of range"); + Write32(EncodeGI(Opcode32::OP_IMM_32, rd, Funct3::SLL, rs1, shamt)); +} + +void RiscVEmitter::SRLIW(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + // Not sure if shamt=0 is legal or not, let's play it safe. + _assert_msg_(shamt > 0 && shamt < 32, "Shift out of range"); + Write32(EncodeGI(Opcode32::OP_IMM_32, rd, Funct3::SRL, rs1, shamt)); +} + +void RiscVEmitter::SRAIW(RiscVReg rd, RiscVReg rs1, u32 shamt) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + // Not sure if shamt=0 is legal or not, let's play it safe. + _assert_msg_(shamt > 0 && shamt < 32, "Shift out of range"); + Write32(EncodeGI(Opcode32::OP_IMM_32, rd, Funct3::SRL, rs1, shamt | (1 << 10))); +} + +void RiscVEmitter::ADDW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::ADD, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::SUBW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::ADD, rs1, rs2, Funct7::SUB)); +} + +void RiscVEmitter::SLLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::SLL, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::SRLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::SRL, rs1, rs2, Funct7::ZERO)); +} + +void RiscVEmitter::SRAW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64, "%s is only valid with R64I", __func__); + _assert_msg_(rd != R_ZERO, "%s write to zero is a HINT", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::SRL, rs1, rs2, Funct7::SRA)); +} + }; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 4a78a68c51..1b35097c12 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -19,7 +19,7 @@ #include #include "Common/CodeBlock.h" -#include "Common/CommonTypes.h" +#include "Common/Common.h" namespace RiscVGen { @@ -47,6 +47,16 @@ enum class FixupBranchType { J, }; +enum class Fence { + I = 0b1000, + O = 0b0100, + R = 0b0010, + W = 0b0001, + RW = R | W, + IO = I | O, +}; +ENUM_CLASS_BITOPS(Fence); + struct FixupBranch { FixupBranch(const u8 *p, FixupBranchType t) : ptr(p), type(t) {} @@ -74,8 +84,6 @@ public: bool BInRange(const void *func) const; bool JInRange(const void *func) const; - void EBREAK(); - void LUI(RiscVReg rd, s32 simm32); void AUIPC(RiscVReg rd, s32 simm32); @@ -152,6 +160,34 @@ public: void OR(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); void AND(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void NEG(RiscVReg rd, RiscVReg rs) { + SUB(rd, R_ZERO, rs); + } + + void FENCE(Fence predecessor, Fence successor); + void FENCE_TSO(); + + void ECALL(); + void EBREAK(); + + // 64-bit instructions - oens ending in W sign extend result to 32 bits. + void LWU(RiscVReg rd, RiscVReg rs1, s32 simm12); + void LD(RiscVReg rd, RiscVReg rs1, s32 simm12); + void SD(RiscVReg rs2, RiscVReg rs1, s32 simm12); + void ADDIW(RiscVReg rd, RiscVReg rs1, s32 simm12); + void SLLIW(RiscVReg rd, RiscVReg rs1, u32 shamt); + void SRLIW(RiscVReg rd, RiscVReg rs1, u32 shamt); + void SRAIW(RiscVReg rd, RiscVReg rs1, u32 shamt); + void ADDW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SUBW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SLLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SRLW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void SRAW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + + void NEGW(RiscVReg rd, RiscVReg rs) { + SUBW(rd, R_ZERO, rs); + } + private: void SetJumpTarget(const FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const; From c66d02d4db5bb65f343bcc5bad3acf6e1d0448e2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Aug 2022 21:05:19 -0700 Subject: [PATCH 16/22] riscv: Emit mul/div instructions. --- Common/RiscVEmitter.cpp | 108 ++++++++++++++++++++++++++++++++++++++++ Common/RiscVEmitter.h | 16 ++++++ 2 files changed, 124 insertions(+) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index 48eda83ee0..3e4d65605c 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -30,6 +30,11 @@ static inline uint8_t BitsSupported() { return 64; } +static inline bool SupportsMulDiv() { + // TODO + return true; +} + enum class Opcode32 { // Note: invalid, just used for FixupBranch. ZERO = 0b0000000, @@ -55,6 +60,7 @@ enum class Funct3 { PRIV = 0b000, FENCE = 0b000, + FENCE_I = 0b001, BEQ = 0b000, BNE = 0b001, @@ -79,6 +85,15 @@ enum class Funct3 { SRL = 0b101, OR = 0b110, AND = 0b111, + + MUL = 0b000, + MULH = 0b001, + MULHSU = 0b010, + MULHU = 0b011, + DIV = 0b100, + DIVU = 0b101, + REM = 0b110, + REMU = 0b111, }; enum class Funct2 { @@ -90,6 +105,8 @@ enum class Funct7 { SUB = 0b0100000, SRA = 0b0100000, + + MULDIV = 0b0000001, }; enum class Funct12 { @@ -635,4 +652,95 @@ void RiscVEmitter::SRAW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::SRL, rs1, rs2, Funct7::SRA)); } +void RiscVEmitter::MUL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::MUL, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::MULH(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::MULH, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::MULHSU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::MULHSU, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::MULHU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::MULHU, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::DIV(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::DIV, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::DIVU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::DIVU, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::REM(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::REM, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::REMU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(SupportsMulDiv(), "%s is only valid with R32M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP, rd, Funct3::REMU, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::MULW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%s is only valid with R64M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::MUL, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::DIVW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%s is only valid with R64M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::DIV, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::DIVUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%s is only valid with R64M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::DIVU, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::REMW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%s is only valid with R64M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::REM, rs1, rs2, Funct7::MULDIV)); +} + +void RiscVEmitter::REMUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(BitsSupported() >= 64 && SupportsMulDiv(), "%s is only valid with R64M", __func__); + // Not explicitly a HINT, but seems sensible to restrict just in case. + _assert_msg_(rd != R_ZERO, "%s write to zero", __func__); + Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::REMU, rs1, rs2, Funct7::MULDIV)); +} + }; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 1b35097c12..8cf27ad2a1 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -188,6 +188,22 @@ public: SUBW(rd, R_ZERO, rs); } + // Integer multiplication and division. + void MUL(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void MULH(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void MULHSU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void MULHU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void DIV(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void DIVU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void REM(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void REMU(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + // 64-bit only multiply and divide. + void MULW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void DIVW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void DIVUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void REMW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void REMUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + private: void SetJumpTarget(const FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const; From 53693072d25ae48d03230898df7f21dde1196d1b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Aug 2022 22:58:00 -0700 Subject: [PATCH 17/22] riscv: Emit atomic operations. --- Common/RiscVEmitter.cpp | 96 +++++++++++++++++++++++++++++++++++++++++ Common/RiscVEmitter.h | 42 +++++++++++++----- 2 files changed, 127 insertions(+), 11 deletions(-) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index 3e4d65605c..3f244b898c 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -35,6 +35,11 @@ static inline bool SupportsMulDiv() { return true; } +static inline bool SupportsAtomic() { + // TODO + return true; +} + enum class Opcode32 { // Note: invalid, just used for FixupBranch. ZERO = 0b0000000, @@ -44,6 +49,7 @@ enum class Opcode32 { AUIPC = 0b0010111, OP_IMM_32 = 0b0011011, STORE = 0b0100011, + AMO = 0b0101111, OP = 0b0110011, LUI = 0b0110111, OP_32 = 0b0111011, @@ -109,6 +115,20 @@ enum class Funct7 { MULDIV = 0b0000001, }; +enum class Funct5 { + AMOADD = 0b00000, + AMOSWAP = 0b00001, + LR = 0b00010, + SC = 0b00011, + AMOXOR = 0b00100, + AMOAND = 0b01100, + AMOOR = 0b01000, + AMOMIN = 0b10000, + AMOMAX = 0b10100, + AMOMINU = 0b11000, + AMOMAXU = 0b11100, +}; + enum class Funct12 { ECALL = 0b000000000000, EBREAK = 0b000000000001, @@ -129,6 +149,11 @@ static inline u32 EncodeGR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg return EncodeR(opcode, rd, funct3, rs1, rs2, funct7); } +static inline u32 EncodeAtomicR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Atomic ordering, Funct5 funct5) { + u32 funct7 = ((u32)funct5 << 2) | (u32)ordering; + return EncodeGR(opcode, rd, funct3, rs1, rs2, (Funct7)funct7); +} + static inline u32 EncodeR4(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct2 funct2, RiscVReg rs3) { return (u32)opcode | ((u32)DecodeReg(rd) << 7) | ((u32)funct3 << 12) | ((u32)DecodeReg(rs1) << 15) | ((u32)DecodeReg(rs2) << 20) | ((u32)funct2 << 25) | ((u32)DecodeReg(rs3) << 27); } @@ -211,6 +236,19 @@ static inline u32 EncodeGJ(Opcode32 opcode, RiscVReg rd, s32 simm21) { return EncodeJ(opcode, rd, simm21); } +static inline Funct3 BitsToFunct3(int bits) { + switch (bits) { + case 32: + return Funct3::LS_W; + case 64: + _assert_msg_(BitsSupported() >= 64, "Cannot use funct3 width %d", bits); + return Funct3::LS_D; + default: + _assert_msg_(false, "Invalid funct3 width %d", bits); + return Funct3::LS_W; + } +} + RiscVEmitter::RiscVEmitter(const u8 *ptr, u8 *writePtr) { SetCodePointer(ptr, writePtr); } @@ -743,4 +781,62 @@ void RiscVEmitter::REMUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { Write32(EncodeGR(Opcode32::OP_32, rd, Funct3::REMU, rs1, rs2, Funct7::MULDIV)); } +void RiscVEmitter::LR(int bits, RiscVReg rd, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + _assert_msg_(ordering != Atomic::RELEASE, "%s should not use RELEASE ordering", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, R_ZERO, ordering, Funct5::LR)); +} + +void RiscVEmitter::SC(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + _assert_msg_(ordering != Atomic::ACQUIRE, "%s should not use ACQUIRE ordering", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::SC)); +} + +void RiscVEmitter::AMOSWAP(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOSWAP)); +} + +void RiscVEmitter::AMOADD(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOADD)); +} + +void RiscVEmitter::AMOAND(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOAND)); +} + +void RiscVEmitter::AMOOR(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOOR)); +} + +void RiscVEmitter::AMOXOR(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOXOR)); +} + +void RiscVEmitter::AMOMIN(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOMIN)); +} + +void RiscVEmitter::AMOMAX(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOMAX)); +} + +void RiscVEmitter::AMOMINU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOMINU)); +} + +void RiscVEmitter::AMOMAXU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, Atomic ordering) { + _assert_msg_(SupportsAtomic(), "%s is only valid with R32A", __func__); + Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOMAXU)); +} + + }; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 8cf27ad2a1..04e8a8a56a 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -57,6 +57,13 @@ enum class Fence { }; ENUM_CLASS_BITOPS(Fence); +enum class Atomic { + NONE = 0b00, + ACQUIRE = 0b10, + RELEASE = 0b01, + SEQUENTIAL = 0b11, +}; + struct FixupBranch { FixupBranch(const u8 *p, FixupBranchType t) : ptr(p), type(t) {} @@ -118,15 +125,15 @@ public: FixupBranch BLTU(RiscVReg rs1, RiscVReg rs2); FixupBranch BGEU(RiscVReg rs1, RiscVReg rs2); - void LB(RiscVReg rd, RiscVReg rs1, s32 simm12); - void LH(RiscVReg rd, RiscVReg rs1, s32 simm12); - void LW(RiscVReg rd, RiscVReg rs1, s32 simm12); - void LBU(RiscVReg rd, RiscVReg rs1, s32 simm12); - void LHU(RiscVReg rd, RiscVReg rs1, s32 simm12); + void LB(RiscVReg rd, RiscVReg addr, s32 simm12); + void LH(RiscVReg rd, RiscVReg addr, s32 simm12); + void LW(RiscVReg rd, RiscVReg addr, s32 simm12); + void LBU(RiscVReg rd, RiscVReg addr, s32 simm12); + void LHU(RiscVReg rd, RiscVReg addr, s32 simm12); - void SB(RiscVReg rs2, RiscVReg rs1, s32 simm12); - void SH(RiscVReg rs2, RiscVReg rs1, s32 simm12); - void SW(RiscVReg rs2, RiscVReg rs1, s32 simm12); + void SB(RiscVReg rs2, RiscVReg addr, s32 simm12); + void SH(RiscVReg rs2, RiscVReg addr, s32 simm12); + void SW(RiscVReg rs2, RiscVReg addr, s32 simm12); void ADDI(RiscVReg rd, RiscVReg rs1, s32 simm12); void SLTI(RiscVReg rd, RiscVReg rs1, s32 simm12); @@ -171,9 +178,9 @@ public: void EBREAK(); // 64-bit instructions - oens ending in W sign extend result to 32 bits. - void LWU(RiscVReg rd, RiscVReg rs1, s32 simm12); - void LD(RiscVReg rd, RiscVReg rs1, s32 simm12); - void SD(RiscVReg rs2, RiscVReg rs1, s32 simm12); + void LWU(RiscVReg rd, RiscVReg addr, s32 simm12); + void LD(RiscVReg rd, RiscVReg addr, s32 simm12); + void SD(RiscVReg rs2, RiscVReg addr, s32 simm12); void ADDIW(RiscVReg rd, RiscVReg rs1, s32 simm12); void SLLIW(RiscVReg rd, RiscVReg rs1, u32 shamt); void SRLIW(RiscVReg rd, RiscVReg rs1, u32 shamt); @@ -204,6 +211,19 @@ public: void REMW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); void REMUW(RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + // Atomic memory operations. + void LR(int bits, RiscVReg rd, RiscVReg addr, Atomic ordering); + void SC(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + void AMOSWAP(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + void AMOADD(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + void AMOAND(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + void AMOOR(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + void AMOXOR(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + void AMOMIN(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + void AMOMAX(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + void AMOMINU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + void AMOMAXU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + private: void SetJumpTarget(const FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const; From 591de6be1d7f6bf56dd159dcf5e0d717b284ca1f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 25 Aug 2022 19:26:15 -0700 Subject: [PATCH 18/22] riscv: Emit float instructions. --- Common/RiscVEmitter.cpp | 250 +++++++++++++++++++++++++++++++++++++++- Common/RiscVEmitter.h | 72 ++++++++++++ 2 files changed, 319 insertions(+), 3 deletions(-) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index 3f244b898c..d316b4e448 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "ppsspp_config.h" +#include #include "Common/RiscVEmitter.h" namespace RiscVGen { @@ -30,6 +31,11 @@ static inline uint8_t BitsSupported() { return 64; } +static inline uint8_t FloatBitsSupported() { + // TODO: 0 if not. + return 64; +} + static inline bool SupportsMulDiv() { // TODO return true; @@ -44,15 +50,22 @@ enum class Opcode32 { // Note: invalid, just used for FixupBranch. ZERO = 0b0000000, LOAD = 0b0000011, + LOAD_FP = 0b0000111, MISC_MEM = 0b0001111, OP_IMM = 0b0010011, AUIPC = 0b0010111, OP_IMM_32 = 0b0011011, STORE = 0b0100011, + STORE_FP = 0b0100111, AMO = 0b0101111, OP = 0b0110011, LUI = 0b0110111, OP_32 = 0b0111011, + FMADD = 0b1000011, + FMSUB = 0b1000111, + FNMSUB = 0b1001011, + FNMADD = 0b1001111, + OP_FP = 0b1010011, BRANCH = 0b1100011, JALR = 0b1100111, JAL = 0b1101111, @@ -100,10 +113,26 @@ enum class Funct3 { DIVU = 0b101, REM = 0b110, REMU = 0b111, + + FSGNJ = 0b000, + FSGNJN = 0b001, + FSGNJX = 0b010, + + FMIN = 0b000, + FMAX = 0b001, + + FMV = 0b000, + FCLASS = 0b001, + + FLE = 0b000, + FLT = 0b001, + FEQ = 0b010, }; enum class Funct2 { - // TODO: 0b00, + S = 0b00, + D = 0b01, + Q = 0b11, }; enum class Funct7 { @@ -127,6 +156,20 @@ enum class Funct5 { AMOMAX = 0b10100, AMOMINU = 0b11000, AMOMAXU = 0b11100, + + FADD = 0b00000, + FSUB = 0b00001, + FMUL = 0b00010, + FDIV = 0b00011, + FSGNJ = 0b00100, + FMINMAX = 0b00101, + FCVT_SZ = 0b01000, + FSQRT = 0b01011, + FCMP = 0b10100, + FCVT_TOX = 0b11000, + FCVT_FROMX = 0b11010, + FMV_TOX = 0b11100, + FMV_FROMX = 0b11110, }; enum class Funct12 { @@ -158,6 +201,25 @@ static inline u32 EncodeR4(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg return (u32)opcode | ((u32)DecodeReg(rd) << 7) | ((u32)funct3 << 12) | ((u32)DecodeReg(rs1) << 15) | ((u32)DecodeReg(rs2) << 20) | ((u32)funct2 << 25) | ((u32)DecodeReg(rs3) << 27); } +static inline u32 EncodeFR4(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct2 funct2, RiscVReg rs3) { + _assert_msg_(IsFPR(rd), "R4 instruction rd must be FPR"); + _assert_msg_(IsFPR(rs1), "R4 instruction rs1 must be FPR"); + _assert_msg_(IsFPR(rs2), "R4 instruction rs2 must be FPR"); + _assert_msg_(IsFPR(rs3), "R4 instruction rs3 must be FPR"); + return EncodeR4(opcode, rd, funct3, rs1, rs2, funct2, rs3); +} + +static inline u32 EncodeR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct2 funct2, Funct5 funct5) { + return EncodeR(opcode, rd, funct3, rs1, rs2, (Funct7)(((u32)funct5 << 2) | (u32)funct2)); +} + +static inline u32 EncodeFR(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, RiscVReg rs2, Funct2 funct2, Funct5 funct5) { + _assert_msg_(IsFPR(rd), "R4 instruction rd must be FPR"); + _assert_msg_(IsFPR(rs1), "R4 instruction rs1 must be FPR"); + _assert_msg_(IsFPR(rs2), "R4 instruction rs2 must be FPR"); + return EncodeR(opcode, rd, funct3, rs1, rs2, (Funct7)(((u32)funct5 << 2) | (u32)funct2)); +} + static inline u32 EncodeI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, s32 simm12) { _assert_msg_(((simm12 << 20) >> 20) == simm12, "I immediate must be signed s11.0"); return (u32)opcode | ((u32)DecodeReg(rd) << 7) | ((u32)funct3 << 12) | ((u32)DecodeReg(rs1) << 15) | ((u32)simm12 << 20); @@ -236,12 +298,13 @@ static inline u32 EncodeGJ(Opcode32 opcode, RiscVReg rd, s32 simm21) { return EncodeJ(opcode, rd, simm21); } -static inline Funct3 BitsToFunct3(int bits) { +static inline Funct3 BitsToFunct3(int bits, bool useFloat = false) { + int bitsSupported = useFloat ? FloatBitsSupported() : BitsSupported(); + _assert_msg_(bitsSupported >= bits, "Cannot use funct3 width %d, only have %d", bits, bitsSupported); switch (bits) { case 32: return Funct3::LS_W; case 64: - _assert_msg_(BitsSupported() >= 64, "Cannot use funct3 width %d", bits); return Funct3::LS_D; default: _assert_msg_(false, "Invalid funct3 width %d", bits); @@ -249,6 +312,56 @@ static inline Funct3 BitsToFunct3(int bits) { } } +static inline Funct2 BitsToFunct2(int bits) { + _assert_msg_(FloatBitsSupported() >= bits, "Cannot use funct2 width %d, only have %d", bits, FloatBitsSupported()); + switch (bits) { + case 32: + return Funct2::S; + case 64: + return Funct2::D; + case 128: + return Funct2::Q; + default: + _assert_msg_(false, "Invalid funct2 width %d", bits); + return Funct2::S; + } +} + +static inline int FConvToFloatBits(FConv c) { + switch (c) { + case FConv::W: + case FConv::WU: + case FConv::L: + case FConv::LU: + break; + + case FConv::S: + return 32; + case FConv::D: + return 64; + case FConv::Q: + return 128; + } + return 0; +} + +static inline int FConvToIntegerBits(FConv c) { + switch (c) { + case FConv::S: + case FConv::D: + case FConv::Q: + break; + + case FConv::W: + case FConv::WU: + return 32; + case FConv::L: + case FConv::LU: + return 64; + } + return 0; +} + RiscVEmitter::RiscVEmitter(const u8 *ptr, u8 *writePtr) { SetCodePointer(ptr, writePtr); } @@ -838,5 +951,136 @@ void RiscVEmitter::AMOMAXU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg rs1, At Write32(EncodeAtomicR(Opcode32::AMO, rd, BitsToFunct3(bits), rs1, rs2, ordering, Funct5::AMOMAXU)); } +void RiscVEmitter::FL(int bits, RiscVReg rd, RiscVReg rs1, s32 simm12) { + _assert_msg_(IsGPR(rs1) && IsFPR(rd), "FL with incorrect register types"); + Write32(EncodeI(Opcode32::LOAD_FP, rd, BitsToFunct3(bits, true), rs1, simm12)); +} + +void RiscVEmitter::FS(int bits, RiscVReg rs2, RiscVReg rs1, s32 simm12) { + _assert_msg_(IsGPR(rs1) && IsFPR(rs2), "FS with incorrect register types"); + Write32(EncodeS(Opcode32::STORE_FP, BitsToFunct3(bits, true), rs1, rs2, simm12)); +} + +void RiscVEmitter::FMADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm) { + Write32(EncodeFR4(Opcode32::FMADD, rd, (Funct3)rm, rs1, rs2, BitsToFunct2(bits), rs3)); +} + +void RiscVEmitter::FMSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm) { + Write32(EncodeFR4(Opcode32::FMSUB, rd, (Funct3)rm, rs1, rs2, BitsToFunct2(bits), rs3)); +} + +void RiscVEmitter::FNMSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm) { + Write32(EncodeFR4(Opcode32::FNMSUB, rd, (Funct3)rm, rs1, rs2, BitsToFunct2(bits), rs3)); +} + +void RiscVEmitter::FNMADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm) { + Write32(EncodeFR4(Opcode32::FNMADD, rd, (Funct3)rm, rs1, rs2, BitsToFunct2(bits), rs3)); +} + +void RiscVEmitter::FADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm) { + Write32(EncodeFR(Opcode32::OP_FP, rd, (Funct3)rm, rs1, rs2, BitsToFunct2(bits), Funct5::FADD)); +} + +void RiscVEmitter::FSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm) { + Write32(EncodeFR(Opcode32::OP_FP, rd, (Funct3)rm, rs1, rs2, BitsToFunct2(bits), Funct5::FSUB)); +} + +void RiscVEmitter::FMUL(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm) { + Write32(EncodeFR(Opcode32::OP_FP, rd, (Funct3)rm, rs1, rs2, BitsToFunct2(bits), Funct5::FMUL)); +} + +void RiscVEmitter::FDIV(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm) { + Write32(EncodeFR(Opcode32::OP_FP, rd, (Funct3)rm, rs1, rs2, BitsToFunct2(bits), Funct5::FDIV)); +} + +void RiscVEmitter::FSQRT(int bits, RiscVReg rd, RiscVReg rs1, Round rm) { + Write32(EncodeFR(Opcode32::OP_FP, rd, (Funct3)rm, rs1, F0, BitsToFunct2(bits), Funct5::FSQRT)); +} + +void RiscVEmitter::FSGNJ(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + Write32(EncodeFR(Opcode32::OP_FP, rd, Funct3::FSGNJ, rs1, rs2, BitsToFunct2(bits), Funct5::FSGNJ)); +} + +void RiscVEmitter::FSGNJN(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + Write32(EncodeFR(Opcode32::OP_FP, rd, Funct3::FSGNJN, rs1, rs2, BitsToFunct2(bits), Funct5::FSGNJ)); +} + +void RiscVEmitter::FSGNJX(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + Write32(EncodeFR(Opcode32::OP_FP, rd, Funct3::FSGNJX, rs1, rs2, BitsToFunct2(bits), Funct5::FSGNJ)); +} + +void RiscVEmitter::FMIN(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + Write32(EncodeFR(Opcode32::OP_FP, rd, Funct3::FMIN, rs1, rs2, BitsToFunct2(bits), Funct5::FMINMAX)); +} + +void RiscVEmitter::FMAX(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + Write32(EncodeFR(Opcode32::OP_FP, rd, Funct3::FMAX, rs1, rs2, BitsToFunct2(bits), Funct5::FMINMAX)); +} + +void RiscVEmitter::FCVT(FConv to, FConv from, RiscVReg rd, RiscVReg rs1, Round rm) { + int floatBits = std::max(FConvToFloatBits(from), FConvToFloatBits(to)); + int integerBits = std::max(FConvToIntegerBits(from), FConvToIntegerBits(to)); + + _assert_msg_(floatBits > 0, "FCVT can't be used with only GPRs"); + _assert_msg_(integerBits <= BitsSupported(), "FCVT for %d integer bits, only %d supported", integerBits, BitsSupported()); + _assert_msg_(floatBits <= FloatBitsSupported(), "FCVT for %d float bits, only %d supported", floatBits, FloatBitsSupported()); + + if (integerBits == 0) { + // Convert between float widths. + Funct2 fromFmt = BitsToFunct2(FConvToFloatBits(from)); + Funct2 toFmt = BitsToFunct2(FConvToFloatBits(to)); + Write32(EncodeR(Opcode32::OP_FP, rd, (Funct3)rm, rs1, (RiscVReg)fromFmt, toFmt, Funct5::FCVT_SZ)); + } else { + Funct5 funct5 = FConvToIntegerBits(to) == 0 ? Funct5::FCVT_FROMX : Funct5::FCVT_TOX; + FConv integerFmt = FConvToIntegerBits(to) == 0 ? from : to; + Funct2 floatFmt = BitsToFunct2(floatBits); + _assert_msg_(((int)integerFmt & ~3) == 0, "Got wrong integer bits"); + Write32(EncodeR(Opcode32::OP_FP, rd, (Funct3)rm, rs1, (RiscVReg)integerFmt, floatFmt, funct5)); + } +} + +void RiscVEmitter::FMV(FMv to, FMv from, RiscVReg rd, RiscVReg rs1) { + int bits = 0; + switch (to == FMv::X ? from : to) { + case FMv::D: bits = 64; break; + case FMv::W: bits = 32; break; + case FMv::X: bits = 0; break; + } + + _assert_msg_(BitsSupported() >= bits && FloatBitsSupported() >= bits, "FMV cannot be used for %d bits, only %d/%d supported", bits, BitsSupported(), FloatBitsSupported()); + _assert_msg_((to == FMv::X && from != FMv::X) || (to != FMv::X && from == FMv::X), "%s can only transfer between FPR/GPR", __func__); + _assert_msg_(to == FMv::X ? IsGPR(rd) : IsFPR(rd), "%s rd of wrong type", __func__); + _assert_msg_(from == FMv::X ? IsGPR(rs1) : IsFPR(rs1), "%s rs1 of wrong type", __func__); + + Funct5 funct5 = to == FMv::X ? Funct5::FMV_TOX : Funct5::FMV_FROMX; + Write32(EncodeR(Opcode32::OP_FP, rd, Funct3::FMV, rs1, F0, BitsToFunct2(bits), funct5)); +} + +void RiscVEmitter::FEQ(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(IsGPR(rd), "%s rd must be GPR", __func__); + _assert_msg_(IsFPR(rs1), "%s rs1 must be FPR", __func__); + _assert_msg_(IsFPR(rs2), "%s rs2 must be FPR", __func__); + Write32(EncodeR(Opcode32::OP_FP, rd, Funct3::FEQ, rs1, rs2, BitsToFunct2(bits), Funct5::FCMP)); +} + +void RiscVEmitter::FLT(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(IsGPR(rd), "%s rd must be GPR", __func__); + _assert_msg_(IsFPR(rs1), "%s rs1 must be FPR", __func__); + _assert_msg_(IsFPR(rs2), "%s rs2 must be FPR", __func__); + Write32(EncodeR(Opcode32::OP_FP, rd, Funct3::FLT, rs1, rs2, BitsToFunct2(bits), Funct5::FCMP)); +} + +void RiscVEmitter::FLE(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2) { + _assert_msg_(IsGPR(rd), "%s rd must be GPR", __func__); + _assert_msg_(IsFPR(rs1), "%s rs1 must be FPR", __func__); + _assert_msg_(IsFPR(rs2), "%s rs2 must be FPR", __func__); + Write32(EncodeR(Opcode32::OP_FP, rd, Funct3::FLE, rs1, rs2, BitsToFunct2(bits), Funct5::FCMP)); +} + +void RiscVEmitter::FCLASS(int bits, RiscVReg rd, RiscVReg rs1) { + _assert_msg_(IsGPR(rd), "%s rd must be GPR", __func__); + _assert_msg_(IsFPR(rs1), "%s rs1 must be FPR", __func__); + Write32(EncodeR(Opcode32::OP_FP, rd, Funct3::FCLASS, rs1, F0, BitsToFunct2(bits), Funct5::FMV_TOX)); +} }; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index 04e8a8a56a..e75b5561e4 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -64,6 +64,32 @@ enum class Atomic { SEQUENTIAL = 0b11, }; +enum class Round { + NEAREST_EVEN = 0b000, + TOZERO = 0b001, + DOWN = 0b010, + UP = 0b011, + NEAREST_MAX = 0b100, + DYNAMIC = 0b111, +}; + +enum class FConv { + W = 0x0000, + WU = 0x0001, + L = 0x0002, + LU = 0x0003, + + S = 0x1000, + D = 0x1001, + Q = 0x1003, +}; + +enum class FMv { + X, + W, + D, +}; + struct FixupBranch { FixupBranch(const u8 *p, FixupBranchType t) : ptr(p), type(t) {} @@ -224,6 +250,52 @@ public: void AMOMINU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); void AMOMAXU(int bits, RiscVReg rd, RiscVReg rs2, RiscVReg addr, Atomic ordering); + // Floating point (same funcs for single/double/quad, if supported.) + void FL(int bits, RiscVReg rd, RiscVReg addr, s32 simm12); + void FS(int bits, RiscVReg rs2, RiscVReg addr, s32 simm12); + void FLW(RiscVReg rd, RiscVReg addr, s32 simm12) { + FL(32, rd, addr, simm12); + } + void FSW(RiscVReg rs2, RiscVReg addr, s32 simm12) { + FS(32, rs2, addr, simm12); + } + + void FMADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC); + void FMSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC); + void FNMSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC); + void FNMADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, RiscVReg rs3, Round rm = Round::DYNAMIC); + + void FADD(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC); + void FSUB(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC); + void FMUL(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC); + void FDIV(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2, Round rm = Round::DYNAMIC); + void FSQRT(int bits, RiscVReg rd, RiscVReg rs1, Round rm = Round::DYNAMIC); + + void FSGNJ(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void FSGNJN(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void FSGNJX(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + + void FMV(int bits, RiscVReg rd, RiscVReg rs) { + FSGNJ(bits, rd, rs, rs); + } + void FNEG(int bits, RiscVReg rd, RiscVReg rs) { + FSGNJN(bits, rd, rs, rs); + } + void FABS(int bits, RiscVReg rd, RiscVReg rs) { + FSGNJX(bits, rd, rs, rs); + } + + void FMIN(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void FMAX(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + + void FCVT(FConv to, FConv from, RiscVReg rd, RiscVReg rs1, Round rm = Round::DYNAMIC); + void FMV(FMv to, FMv from, RiscVReg rd, RiscVReg rs1); + + void FEQ(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void FLT(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void FLE(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); + void FCLASS(int bits, RiscVReg rd, RiscVReg rs1); + private: void SetJumpTarget(const FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const; From b5755b6cf761933b505222e9ddaf74b35c22f696 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 25 Aug 2022 19:31:46 -0700 Subject: [PATCH 19/22] riscv: Validate FixupBranch usage better. Also avoid FixupBranch alignment issues. --- Common/RiscVEmitter.cpp | 24 +++++++++++++++++++----- Common/RiscVEmitter.h | 7 ++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index d316b4e448..b26ccec694 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -17,6 +17,7 @@ #include "ppsspp_config.h" #include +#include #include "Common/RiscVEmitter.h" namespace RiscVGen { @@ -415,15 +416,25 @@ void RiscVEmitter::FlushIcacheSection(const u8 *start, const u8 *end) { #endif } -void RiscVEmitter::SetJumpTarget(const FixupBranch &branch) { +FixupBranch::~FixupBranch() { + _assert_msg_(ptr == nullptr, "FixupBranch never set (left infinite loop)"); +} + +void RiscVEmitter::SetJumpTarget(FixupBranch &branch) { SetJumpTarget(branch, code_); } -void RiscVEmitter::SetJumpTarget(const FixupBranch &branch, const void *dst) { +void RiscVEmitter::SetJumpTarget(FixupBranch &branch, const void *dst) { + _assert_msg_(branch.ptr != nullptr, "Invalid FixupBranch (SetJumpTarget twice?)"); + const intptr_t srcp = (intptr_t)branch.ptr; const intptr_t dstp = (intptr_t)dst; const ptrdiff_t writable_delta = writable_ - code_; - u32 *fixup = (u32 *)(branch.ptr + writable_delta); + u32 *writableSrc = (u32 *)(branch.ptr + writable_delta); + + // If compressed, this may be an unaligned 32-bit value. + u32 fixup; + memcpy(&fixup, writableSrc, sizeof(u32)); _assert_msg_((dstp & 1) == 0, "Destination should be aligned"); _assert_msg_((dstp & 3) == 0 || SupportsCompressed(), "Destination should be aligned (no compressed)"); @@ -435,14 +446,17 @@ void RiscVEmitter::SetJumpTarget(const FixupBranch &branch, const void *dst) { switch (branch.type) { case FixupBranchType::B: _assert_msg_(BInRange(branch.ptr, dst), "B destination is too far away (%p -> %p)", branch.ptr, dst); - *fixup = (*fixup & 0x01FFF07F) | EncodeB(Opcode32::ZERO, Funct3::ZERO, R_ZERO, R_ZERO, (s32)distance); + fixup = (fixup & 0x01FFF07F) | EncodeB(Opcode32::ZERO, Funct3::ZERO, R_ZERO, R_ZERO, (s32)distance); break; case FixupBranchType::J: _assert_msg_(JInRange(branch.ptr, dst), "J destination is too far away (%p -> %p)", branch.ptr, dst); - *fixup = (*fixup & 0x00000FFF) | EncodeJ(Opcode32::ZERO, R_ZERO, (s32)distance); + fixup = (fixup & 0x00000FFF) | EncodeJ(Opcode32::ZERO, R_ZERO, (s32)distance); break; } + + memcpy(writableSrc, &fixup, sizeof(u32)); + branch.ptr = nullptr; } bool RiscVEmitter::BInRange(const void *func) const { diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index e75b5561e4..f025822f35 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -92,8 +92,9 @@ enum class FMv { struct FixupBranch { FixupBranch(const u8 *p, FixupBranchType t) : ptr(p), type(t) {} + ~FixupBranch(); - const u8 *ptr; + const u8 *ptr = nullptr; FixupBranchType type; }; @@ -113,7 +114,7 @@ public: void FlushIcache(); void FlushIcacheSection(const u8 *start, const u8 *end); - void SetJumpTarget(const FixupBranch &branch); + void SetJumpTarget(FixupBranch &branch); bool BInRange(const void *func) const; bool JInRange(const void *func) const; @@ -297,7 +298,7 @@ public: void FCLASS(int bits, RiscVReg rd, RiscVReg rs1); private: - void SetJumpTarget(const FixupBranch &branch, const void *dst); + void SetJumpTarget(FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const; bool JInRange(const void *src, const void *dst) const; From 4a93647acb7226066bae4cbc5fce43e6ad9b49d1 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Thu, 25 Aug 2022 20:30:01 -0700 Subject: [PATCH 20/22] riscv: Emit CSR manipulation instructions. --- Common/RiscVEmitter.cpp | 57 ++++++++++++++++++++++++++++++++++++++++- Common/RiscVEmitter.h | 35 +++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/Common/RiscVEmitter.cpp b/Common/RiscVEmitter.cpp index b26ccec694..4f64ac793c 100644 --- a/Common/RiscVEmitter.cpp +++ b/Common/RiscVEmitter.cpp @@ -47,6 +47,11 @@ static inline bool SupportsAtomic() { return true; } +static inline bool SupportsZicsr() { + // TODO + return true; +} + enum class Opcode32 { // Note: invalid, just used for FixupBranch. ZERO = 0b0000000, @@ -128,6 +133,13 @@ enum class Funct3 { FLE = 0b000, FLT = 0b001, FEQ = 0b010, + + CSRRW = 0b001, + CSRRS = 0b010, + CSRRC = 0b011, + CSRRWI = 0b101, + CSRRSI = 0b110, + CSRRCI = 0b111, }; enum class Funct2 { @@ -233,7 +245,7 @@ static inline u32 EncodeGI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg } static inline u32 EncodeI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, Funct12 funct12) { - return EncodeI(opcode, rd, funct3, rs1, (s32)funct12); + return EncodeI(opcode, rd, funct3, rs1, ((s32)funct12 << 20) >> 20); } static inline u32 EncodeGI(Opcode32 opcode, RiscVReg rd, Funct3 funct3, RiscVReg rs1, Funct12 funct12) { @@ -1043,6 +1055,10 @@ void RiscVEmitter::FCVT(FConv to, FConv from, RiscVReg rd, RiscVReg rs1, Round r // Convert between float widths. Funct2 fromFmt = BitsToFunct2(FConvToFloatBits(from)); Funct2 toFmt = BitsToFunct2(FConvToFloatBits(to)); + if (FConvToFloatBits(to) > FConvToFloatBits(from)) { + _assert_msg_(rm == Round::DYNAMIC || rm == Round::NEAREST_EVEN, "Invalid rounding mode for widening FCVT"); + rm = Round::NEAREST_EVEN; + } Write32(EncodeR(Opcode32::OP_FP, rd, (Funct3)rm, rs1, (RiscVReg)fromFmt, toFmt, Funct5::FCVT_SZ)); } else { Funct5 funct5 = FConvToIntegerBits(to) == 0 ? Funct5::FCVT_FROMX : Funct5::FCVT_TOX; @@ -1097,4 +1113,43 @@ void RiscVEmitter::FCLASS(int bits, RiscVReg rd, RiscVReg rs1) { Write32(EncodeR(Opcode32::OP_FP, rd, Funct3::FCLASS, rs1, F0, BitsToFunct2(bits), Funct5::FMV_TOX)); } +void RiscVEmitter::CSRRW(RiscVReg rd, Csr csr, RiscVReg rs1) { + _assert_msg_(SupportsZicsr(), "%s instruction not supported", __func__); + _assert_msg_((u32)csr <= 0x00000FFF, "%s with invalid CSR number", __func__); + Write32(EncodeGI(Opcode32::SYSTEM, rd, Funct3::CSRRW, rs1, (Funct12)csr)); +} + +void RiscVEmitter::CSRRS(RiscVReg rd, Csr csr, RiscVReg rs1) { + _assert_msg_(SupportsZicsr(), "%s instruction not supported", __func__); + _assert_msg_((u32)csr <= 0x00000FFF, "%s with invalid CSR number", __func__); + Write32(EncodeGI(Opcode32::SYSTEM, rd, Funct3::CSRRS, rs1, (Funct12)csr)); +} + +void RiscVEmitter::CSRRC(RiscVReg rd, Csr csr, RiscVReg rs1) { + _assert_msg_(SupportsZicsr(), "%s instruction not supported", __func__); + _assert_msg_((u32)csr <= 0x00000FFF, "%s with invalid CSR number", __func__); + Write32(EncodeGI(Opcode32::SYSTEM, rd, Funct3::CSRRC, rs1, (Funct12)csr)); +} + +void RiscVEmitter::CSRRWI(RiscVReg rd, Csr csr, u8 uimm5) { + _assert_msg_(SupportsZicsr(), "%s instruction not supported", __func__); + _assert_msg_((u32)csr <= 0x00000FFF, "%s with invalid CSR number", __func__); + _assert_msg_((u32)uimm5 <= 0x1F, "%s can only specify lowest 5 bits", __func__); + Write32(EncodeGI(Opcode32::SYSTEM, rd, Funct3::CSRRWI, (RiscVReg)uimm5, (Funct12)csr)); +} + +void RiscVEmitter::CSRRSI(RiscVReg rd, Csr csr, u8 uimm5) { + _assert_msg_(SupportsZicsr(), "%s instruction not supported", __func__); + _assert_msg_((u32)csr <= 0x00000FFF, "%s with invalid CSR number", __func__); + _assert_msg_((u32)uimm5 <= 0x1F, "%s can only set lowest 5 bits", __func__); + Write32(EncodeGI(Opcode32::SYSTEM, rd, Funct3::CSRRSI, (RiscVReg)uimm5, (Funct12)csr)); +} + +void RiscVEmitter::CSRRCI(RiscVReg rd, Csr csr, u8 uimm5) { + _assert_msg_(SupportsZicsr(), "%s instruction not supported", __func__); + _assert_msg_((u32)csr <= 0x00000FFF, "%s with invalid CSR number", __func__); + _assert_msg_((u32)uimm5 <= 0x1F, "%s can only clear lowest 5 bits", __func__); + Write32(EncodeGI(Opcode32::SYSTEM, rd, Funct3::CSRRCI, (RiscVReg)uimm5, (Funct12)csr)); +} + }; diff --git a/Common/RiscVEmitter.h b/Common/RiscVEmitter.h index f025822f35..c1d06739a6 100644 --- a/Common/RiscVEmitter.h +++ b/Common/RiscVEmitter.h @@ -90,6 +90,19 @@ enum class FMv { D, }; +enum class Csr { + FFlags = 0x001, + FRm = 0x002, + FCsr = 0x003, + + Cycle = 0xC00, + Time = 0xC01, + InstRet = 0xC02, + CycleH = 0xC80, + TimeH = 0xC81, + InstRetH = 0xC82, +}; + struct FixupBranch { FixupBranch(const u8 *p, FixupBranchType t) : ptr(p), type(t) {} ~FixupBranch(); @@ -297,6 +310,28 @@ public: void FLE(int bits, RiscVReg rd, RiscVReg rs1, RiscVReg rs2); void FCLASS(int bits, RiscVReg rd, RiscVReg rs1); + // Control state register manipulation. + void CSRRW(RiscVReg rd, Csr csr, RiscVReg rs1); + void CSRRS(RiscVReg rd, Csr csr, RiscVReg rs1); + void CSRRC(RiscVReg rd, Csr csr, RiscVReg rs1); + void CSRRWI(RiscVReg rd, Csr csr, u8 uimm5); + void CSRRSI(RiscVReg rd, Csr csr, u8 uimm5); + void CSRRCI(RiscVReg rd, Csr csr, u8 uimm5); + + void FRRM(RiscVReg rd) { + CSRRS(rd, Csr::FRm, X0); + } + void FSRM(RiscVReg rs) { + CSRRW(X0, Csr::FRm, rs); + } + void FSRMI(RiscVReg rd, Round rm) { + _assert_msg_(rm != Round::DYNAMIC, "Cannot set FRm to DYNAMIC"); + CSRRWI(rd, Csr::FRm, (uint8_t)rm); + } + void FSRMI(Round rm) { + FSRMI(X0, rm); + } + private: void SetJumpTarget(FixupBranch &branch, const void *dst); bool BInRange(const void *src, const void *dst) const; From 4390a05fe052b72d7f6c607888eb76d61df846b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 26 Aug 2022 15:13:32 +0200 Subject: [PATCH 21/22] Fix shader compilation error in depth->565 --- GPU/Common/DepalettizeShaderCommon.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/Common/DepalettizeShaderCommon.cpp b/GPU/Common/DepalettizeShaderCommon.cpp index 902b180510..e98c424858 100644 --- a/GPU/Common/DepalettizeShaderCommon.cpp +++ b/GPU/Common/DepalettizeShaderCommon.cpp @@ -104,9 +104,9 @@ void GenerateDepalShader300(ShaderWriter &writer, const DepalConfig &config) { if (config.bufferFormat == GE_FORMAT_DEPTH16 && config.textureFormat == GE_TFMT_5650) { // Convert depth to 565, without going through a CLUT. writer.C(" int idepth = int(clamp(depth, 0.0, 65535.0));\n"); - writer.C(" float r = (idepth & 31) / 31.0f;\n"); - writer.C(" float g = ((idepth >> 5) & 63) / 63.0f;\n"); - writer.C(" float b = ((idepth >> 11) & 31) / 31.0f;\n"); + writer.C(" float r = float(idepth & 31) / 31.0f;\n"); + writer.C(" float g = float((idepth >> 5) & 63) / 63.0f;\n"); + writer.C(" float b = float((idepth >> 11) & 31) / 31.0f;\n"); writer.C(" vec4 outColor = vec4(r, g, b, 1.0);\n"); return; } From 7cba231e58e72804545e8244e412843e9784bb59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 26 Aug 2022 15:47:29 +0200 Subject: [PATCH 22/22] Finish the depal refactoring. Needed for an upcoming change (since this lets us use the Draw2D uniform mechanism in depal shaders). --- GPU/Common/Draw2D.cpp | 14 ++++++++ GPU/Common/Draw2D.h | 2 ++ GPU/Common/FramebufferManagerCommon.cpp | 13 +------ GPU/Common/TextureCacheCommon.cpp | 23 ++++++++++-- GPU/Common/TextureShaderCommon.cpp | 48 ------------------------- GPU/Common/TextureShaderCommon.h | 2 -- 6 files changed, 37 insertions(+), 65 deletions(-) diff --git a/GPU/Common/Draw2D.cpp b/GPU/Common/Draw2D.cpp index 1043858891..1164eb6552 100644 --- a/GPU/Common/Draw2D.cpp +++ b/GPU/Common/Draw2D.cpp @@ -253,6 +253,20 @@ Draw2DPipeline *Draw2D::Create2DPipeline(std::function generate); void DrawStrip2D(Draw::Texture *tex, Draw2DVertex *verts, int vertexCount, bool linearFilter, Draw2DPipeline *pipeline, float texW = 0.0f, float texH = 0.0f, int scaleFactor = 0); + + void Blit(Draw2DPipeline *pipeline, float srcX1, float srcY1, float srcX2, float srcY2, float dstX1, float dstY1, float dstX2, float dstY2, float srcWidth, float srcHeight, float dstWidth, float dstHeight, bool linear, int scaleFactor); void Ensure2DResources(); private: diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index a75b0d4b85..d8c63701a9 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -2666,17 +2666,6 @@ void FramebufferManagerCommon::BlitUsingRaster( draw_->GetFramebufferDimensions(src, &srcW, &srcH); draw_->GetFramebufferDimensions(dest, &destW, &destH); - float dX = 1.0f / (float)destW; - float dY = 1.0f / (float)destH; - float sX = 1.0f / (float)srcW; - float sY = 1.0f / (float)srcH; - Draw2DVertex vtx[4] = { - { -1.0f + 2.0f * dX * destX1, -(1.0f - 2.0f * dY * destY1), sX * srcX1, sY * srcY1 }, - { -1.0f + 2.0f * dX * destX2, -(1.0f - 2.0f * dY * destY1), sX * srcX2, sY * srcY1 }, - { -1.0f + 2.0f * dX * destX1, -(1.0f - 2.0f * dY * destY2), sX * srcX1, sY * srcY2 }, - { -1.0f + 2.0f * dX * destX2, -(1.0f - 2.0f * dY * destY2), sX * srcX2, sY * srcY2 }, - }; - // Unbind the texture first to avoid the D3D11 hazard check (can't set render target to things bound as textures and vice versa, not even temporarily). draw_->BindTexture(0, nullptr); // This will get optimized away in case it's already bound (in VK and GL at least..) @@ -2687,7 +2676,7 @@ void FramebufferManagerCommon::BlitUsingRaster( draw_->SetViewports(1, &vp); draw_->SetScissorRect(0, 0, (int)dest->Width(), (int)dest->Height()); - draw2D_.DrawStrip2D(nullptr, vtx, 4, linearFilter, pipeline, src->Width(), src->Height(), renderScaleFactor_); + draw2D_.Blit(pipeline, srcX1, srcY1, srcX2, srcY2, destX1, destY1, destX2, destY2, (float)srcW, (float)srcH, (float)destW, (float)destH, linearFilter , renderScaleFactor_); gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE); } diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 87746043cb..b3cd653dbf 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -1967,9 +1967,26 @@ void TextureCacheCommon::ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer draw_->BindSamplerStates(0, 1, &nearest); draw_->BindSamplerStates(1, 1, &clutSampler); - textureShaderCache_->ApplyShader(textureShader, - framebuffer->bufferWidth, framebuffer->bufferHeight, framebuffer->renderWidth, framebuffer->renderHeight, - gstate_c.vertBounds, gstate_c.curTextureXOffset, gstate_c.curTextureYOffset); + // If min is not < max, then we don't have values (wasn't set during decode.) + const KnownVertexBounds &bounds = gstate_c.vertBounds; + float u1 = 0.0f; + float v1 = 0.0f; + float u2 = framebuffer->renderWidth; + float v2 = framebuffer->renderHeight; + if (bounds.minV < bounds.maxV) { + u1 = bounds.minU + gstate_c.curTextureXOffset; + v1 = bounds.minV + gstate_c.curTextureYOffset; + u2 = bounds.maxU + gstate_c.curTextureXOffset; + v2 = bounds.maxV + gstate_c.curTextureYOffset; + // We need to reapply the texture next time since we cropped UV. + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); + } + u1 *= framebuffer->renderScaleFactor; + v1 *= framebuffer->renderScaleFactor; + u2 *= framebuffer->renderScaleFactor; + v2 *= framebuffer->renderScaleFactor; + + draw2D_->Blit(textureShader, u1, v1, u2, v2, u1, v1, u2, v2, framebuffer->renderWidth, framebuffer->renderHeight, framebuffer->renderWidth, framebuffer->renderHeight, false, framebuffer->renderScaleFactor); draw_->BindTexture(0, nullptr); framebufferManager_->RebindFramebuffer("ApplyTextureFramebuffer"); diff --git a/GPU/Common/TextureShaderCommon.cpp b/GPU/Common/TextureShaderCommon.cpp index ab7a280a53..f88838fad6 100644 --- a/GPU/Common/TextureShaderCommon.cpp +++ b/GPU/Common/TextureShaderCommon.cpp @@ -247,51 +247,3 @@ std::string TextureShaderCache::DebugGetShaderString(std::string idstr, DebugSha return ""; } } - -void TextureShaderCache::ApplyShader(Draw2DPipeline *pipeline, float bufferW, float bufferH, int renderW, int renderH, const KnownVertexBounds &bounds, u32 uoff, u32 voff) { - Draw2DVertex verts[4] = { - {-1, -1, 0, 0 }, - { 1, -1, 1, 0 }, - {-1, 1, 0, 1 }, - { 1, 1, 1, 1 }, - }; - - // If min is not < max, then we don't have values (wasn't set during decode.) - if (bounds.minV < bounds.maxV) { - const float invWidth = 1.0f / bufferW; - const float invHeight = 1.0f / bufferH; - // Inverse of half = double. - const float invHalfWidth = invWidth * 2.0f; - const float invHalfHeight = invHeight * 2.0f; - - const int u1 = bounds.minU + uoff; - const int v1 = bounds.minV + voff; - const int u2 = bounds.maxU + uoff; - const int v2 = bounds.maxV + voff; - - const float left = u1 * invHalfWidth - 1.0f; - const float right = u2 * invHalfWidth - 1.0f; - const float top = v1 * invHalfHeight - 1.0f; - const float bottom = v2 * invHalfHeight - 1.0f; - - const float uvleft = u1 * invWidth; - const float uvright = u2 * invWidth; - const float uvtop = v1 * invHeight; - const float uvbottom = v2 * invHeight; - - // Points are: BL, BR, TR, TL. - verts[0] = Draw2DVertex{ left, bottom, uvleft, uvbottom }; - verts[1] = Draw2DVertex{ right, bottom, uvright, uvbottom }; - verts[2] = Draw2DVertex{ left, top, uvleft, uvtop }; - verts[3] = Draw2DVertex{ right, top, uvright, uvtop }; - - // We need to reapply the texture next time since we cropped UV. - gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); - } - - Draw::Viewport vp{ 0.0f, 0.0f, (float)renderW, (float)renderH, 0.0f, 1.0f }; - draw_->BindPipeline(pipeline->pipeline); - draw_->SetViewports(1, &vp); - draw_->SetScissorRect(0, 0, renderW, renderH); - draw_->DrawUP((const uint8_t *)verts, 4); -} diff --git a/GPU/Common/TextureShaderCommon.h b/GPU/Common/TextureShaderCommon.h index 5e0c5fe476..2176fea37d 100644 --- a/GPU/Common/TextureShaderCommon.h +++ b/GPU/Common/TextureShaderCommon.h @@ -48,8 +48,6 @@ public: Draw::SamplerState *GetSampler(bool linearFilter); - void ApplyShader(Draw2DPipeline *pipeline, float bufferW, float bufferH, int renderW, int renderH, const KnownVertexBounds &bounds, u32 uoff, u32 voff); - void Clear(); void Decimate(); std::vector DebugGetShaderIDs(DebugShaderType type);