mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-16 12:09:07 +00:00
Merge remote-tracking branch 'upstream/main' into quasifs
This commit is contained in:
commit
fb1bd330ca
@ -5,6 +5,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
#include <Zydis/Zydis.h>
|
#include <Zydis/Zydis.h>
|
||||||
#include <xbyak/xbyak.h>
|
#include <xbyak/xbyak.h>
|
||||||
#include <xbyak/xbyak_util.h>
|
#include <xbyak/xbyak_util.h>
|
||||||
@ -122,6 +123,30 @@ static void GenerateTcbAccess(void* /* address */, const ZydisDecodedOperand* op
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool FilterStackCheck(const ZydisDecodedOperand* operands) {
|
||||||
|
const auto& dst_op = operands[0];
|
||||||
|
const auto& src_op = operands[1];
|
||||||
|
|
||||||
|
// Some compilers emit stack checks by starting a function with
|
||||||
|
// 'mov (64-bit register), fs:[0x28]', then checking with `xor (64-bit register), fs:[0x28]`
|
||||||
|
return src_op.type == ZYDIS_OPERAND_TYPE_MEMORY && src_op.mem.segment == ZYDIS_REGISTER_FS &&
|
||||||
|
src_op.mem.base == ZYDIS_REGISTER_NONE && src_op.mem.index == ZYDIS_REGISTER_NONE &&
|
||||||
|
src_op.mem.disp.value == 0x28 && dst_op.reg.value >= ZYDIS_REGISTER_RAX &&
|
||||||
|
dst_op.reg.value <= ZYDIS_REGISTER_R15;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GenerateStackCheck(void* /* address */, const ZydisDecodedOperand* operands,
|
||||||
|
Xbyak::CodeGenerator& c) {
|
||||||
|
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||||
|
c.xor_(dst, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GenerateStackCanary(void* /* address */, const ZydisDecodedOperand* operands,
|
||||||
|
Xbyak::CodeGenerator& c) {
|
||||||
|
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||||
|
c.mov(dst, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
|
static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
|
||||||
Cpu cpu;
|
Cpu cpu;
|
||||||
return !cpu.has(Cpu::tSSE4a);
|
return !cpu.has(Cpu::tSSE4a);
|
||||||
@ -440,18 +465,26 @@ struct PatchInfo {
|
|||||||
bool trampoline;
|
bool trampoline;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::unordered_map<ZydisMnemonic, PatchInfo> Patches = {
|
static const std::unordered_map<ZydisMnemonic, std::vector<PatchInfo>> Patches = {
|
||||||
// SSE4a
|
// SSE4a
|
||||||
{ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
|
{ZYDIS_MNEMONIC_EXTRQ, {{FilterNoSSE4a, GenerateEXTRQ, true}}},
|
||||||
{ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
|
{ZYDIS_MNEMONIC_INSERTQ, {{FilterNoSSE4a, GenerateINSERTQ, true}}},
|
||||||
{ZYDIS_MNEMONIC_MOVNTSS, {FilterNoSSE4a, ReplaceMOVNTSS, false}},
|
{ZYDIS_MNEMONIC_MOVNTSS, {{FilterNoSSE4a, ReplaceMOVNTSS, false}}},
|
||||||
{ZYDIS_MNEMONIC_MOVNTSD, {FilterNoSSE4a, ReplaceMOVNTSD, false}},
|
{ZYDIS_MNEMONIC_MOVNTSD, {{FilterNoSSE4a, ReplaceMOVNTSD, false}}},
|
||||||
|
|
||||||
|
#if !defined(__APPLE__)
|
||||||
|
// FS segment patches
|
||||||
|
// These first two patches are for accesses to the stack canary, fs:[0x28]
|
||||||
|
{ZYDIS_MNEMONIC_XOR, {{FilterStackCheck, GenerateStackCheck, false}}},
|
||||||
|
{ZYDIS_MNEMONIC_MOV,
|
||||||
|
{{FilterStackCheck, GenerateStackCanary, false},
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
// Windows needs a trampoline.
|
// Windows needs a trampoline for Tcb accesses.
|
||||||
{ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, true}},
|
{FilterTcbAccess, GenerateTcbAccess, true}
|
||||||
#elif !defined(__APPLE__)
|
#else
|
||||||
{ZYDIS_MNEMONIC_MOV, {FilterTcbAccess, GenerateTcbAccess, false}},
|
{FilterTcbAccess, GenerateTcbAccess, false}
|
||||||
|
#endif
|
||||||
|
}},
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -503,51 +536,53 @@ static std::pair<bool, u64> TryPatch(u8* code, PatchModule* module) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Patches.contains(instruction.mnemonic)) {
|
if (Patches.contains(instruction.mnemonic)) {
|
||||||
const auto& patch_info = Patches.at(instruction.mnemonic);
|
const auto& patches = Patches.at(instruction.mnemonic);
|
||||||
bool needs_trampoline = patch_info.trampoline;
|
for (const auto& patch_info : patches) {
|
||||||
if (patch_info.filter(operands)) {
|
bool needs_trampoline = patch_info.trampoline;
|
||||||
auto& patch_gen = module->patch_gen;
|
if (patch_info.filter(operands)) {
|
||||||
|
auto& patch_gen = module->patch_gen;
|
||||||
|
|
||||||
if (needs_trampoline && instruction.length < 5) {
|
if (needs_trampoline && instruction.length < 5) {
|
||||||
// Trampoline is needed but instruction is too short to patch.
|
// Trampoline is needed but instruction is too short to patch.
|
||||||
// Return false and length to signal to AOT compilation that this instruction
|
// Return false and length to signal to AOT compilation that this instruction
|
||||||
// should be skipped and handled at runtime.
|
// should be skipped and handled at runtime.
|
||||||
return std::make_pair(false, instruction.length);
|
return std::make_pair(false, instruction.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset state and move to current code position.
|
// Reset state and move to current code position.
|
||||||
patch_gen.reset();
|
patch_gen.reset();
|
||||||
patch_gen.setSize(code - patch_gen.getCode());
|
patch_gen.setSize(code - patch_gen.getCode());
|
||||||
|
|
||||||
if (needs_trampoline) {
|
if (needs_trampoline) {
|
||||||
auto& trampoline_gen = module->trampoline_gen;
|
auto& trampoline_gen = module->trampoline_gen;
|
||||||
const auto trampoline_ptr = trampoline_gen.getCurr();
|
const auto trampoline_ptr = trampoline_gen.getCurr();
|
||||||
|
|
||||||
patch_info.generator(code, operands, trampoline_gen);
|
patch_info.generator(code, operands, trampoline_gen);
|
||||||
|
|
||||||
// Return to the following instruction at the end of the trampoline.
|
// Return to the following instruction at the end of the trampoline.
|
||||||
trampoline_gen.jmp(code + instruction.length);
|
trampoline_gen.jmp(code + instruction.length);
|
||||||
|
|
||||||
// Replace instruction with near jump to the trampoline.
|
// Replace instruction with near jump to the trampoline.
|
||||||
patch_gen.jmp(trampoline_ptr, Xbyak::CodeGenerator::LabelType::T_NEAR);
|
patch_gen.jmp(trampoline_ptr, Xbyak::CodeGenerator::LabelType::T_NEAR);
|
||||||
} else {
|
} else {
|
||||||
patch_info.generator(code, operands, patch_gen);
|
patch_info.generator(code, operands, patch_gen);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto patch_size = patch_gen.getCurr() - code;
|
const auto patch_size = patch_gen.getCurr() - code;
|
||||||
if (patch_size > 0) {
|
if (patch_size > 0) {
|
||||||
ASSERT_MSG(instruction.length >= patch_size,
|
ASSERT_MSG(instruction.length >= patch_size,
|
||||||
"Instruction {} with length {} is too short to replace at: {}",
|
"Instruction {} with length {} is too short to replace at: {}",
|
||||||
ZydisMnemonicGetString(instruction.mnemonic), instruction.length,
|
ZydisMnemonicGetString(instruction.mnemonic), instruction.length,
|
||||||
fmt::ptr(code));
|
fmt::ptr(code));
|
||||||
|
|
||||||
// Fill remaining space with nops.
|
// Fill remaining space with nops.
|
||||||
patch_gen.nop(instruction.length - patch_size);
|
patch_gen.nop(instruction.length - patch_size);
|
||||||
|
|
||||||
module->patched.insert(code);
|
module->patched.insert(code);
|
||||||
LOG_DEBUG(Core, "Patched instruction '{}' at: {}",
|
LOG_DEBUG(Core, "Patched instruction '{}' at: {}",
|
||||||
ZydisMnemonicGetString(instruction.mnemonic), fmt::ptr(code));
|
ZydisMnemonicGetString(instruction.mnemonic), fmt::ptr(code));
|
||||||
return std::make_pair(true, instruction.length);
|
return std::make_pair(true, instruction.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -173,6 +173,8 @@ void RegisterException(Core::Loader::SymbolsResolver* sym) {
|
|||||||
LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", sceKernelDebugRaiseException);
|
LIB_FUNCTION("OMDRKKAZ8I4", "libkernel", 1, "libkernel", sceKernelDebugRaiseException);
|
||||||
LIB_FUNCTION("zE-wXIZjLoM", "libkernel", 1, "libkernel",
|
LIB_FUNCTION("zE-wXIZjLoM", "libkernel", 1, "libkernel",
|
||||||
sceKernelDebugRaiseExceptionOnReleaseMode);
|
sceKernelDebugRaiseExceptionOnReleaseMode);
|
||||||
|
LIB_FUNCTION("WkwEd3N7w0Y", "libkernel", 1, "libkernel", sceKernelInstallExceptionHandler);
|
||||||
|
LIB_FUNCTION("Qhv5ARAoOEc", "libkernel", 1, "libkernel", sceKernelRemoveExceptionHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libraries::Kernel
|
} // namespace Libraries::Kernel
|
||||||
|
|||||||
@ -712,8 +712,61 @@ int PS4_SYSV_ABI sceHttpUriCopy() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpUriEscape() {
|
int PS4_SYSV_ABI sceHttpUriEscape(char* out, u64* require, u64 prepare, const char* in) {
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
LOG_TRACE(Lib_Http, "called");
|
||||||
|
|
||||||
|
if (!in) {
|
||||||
|
LOG_ERROR(Lib_Http, "Invalid input string");
|
||||||
|
return ORBIS_HTTP_ERROR_INVALID_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto IsUnreserved = [](unsigned char c) -> bool {
|
||||||
|
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
|
||||||
|
c == '-' || c == '_' || c == '.' || c == '~';
|
||||||
|
};
|
||||||
|
|
||||||
|
u64 needed = 0;
|
||||||
|
const char* src = in;
|
||||||
|
while (*src) {
|
||||||
|
unsigned char c = static_cast<unsigned char>(*src);
|
||||||
|
if (IsUnreserved(c)) {
|
||||||
|
needed++;
|
||||||
|
} else {
|
||||||
|
needed += 3; // %XX format
|
||||||
|
}
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
needed++; // null terminator
|
||||||
|
|
||||||
|
if (require) {
|
||||||
|
*require = needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!out) {
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prepare < needed) {
|
||||||
|
LOG_ERROR(Lib_Http, "Buffer too small: need {} but only {} available", needed, prepare);
|
||||||
|
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char hex_chars[] = "0123456789ABCDEF";
|
||||||
|
src = in;
|
||||||
|
char* dst = out;
|
||||||
|
while (*src) {
|
||||||
|
unsigned char c = static_cast<unsigned char>(*src);
|
||||||
|
if (IsUnreserved(c)) {
|
||||||
|
*dst++ = *src;
|
||||||
|
} else {
|
||||||
|
*dst++ = '%';
|
||||||
|
*dst++ = hex_chars[(c >> 4) & 0x0F];
|
||||||
|
*dst++ = hex_chars[c & 0x0F];
|
||||||
|
}
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
*dst = '\0';
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1072,12 +1125,163 @@ int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, v
|
|||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize) {
|
int PS4_SYSV_ABI sceHttpUriSweepPath(char* dst, const char* src, u64 srcSize) {
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
LOG_TRACE(Lib_Http, "called");
|
||||||
|
|
||||||
|
if (!dst || !src) {
|
||||||
|
LOG_ERROR(Lib_Http, "Invalid parameters");
|
||||||
|
return ORBIS_HTTP_ERROR_INVALID_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (srcSize == 0) {
|
||||||
|
dst[0] = '\0';
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 len = 0;
|
||||||
|
while (len < srcSize && src[len] != '\0') {
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u64 i = 0; i < len; i++) {
|
||||||
|
dst[i] = src[i];
|
||||||
|
}
|
||||||
|
dst[len] = '\0';
|
||||||
|
|
||||||
|
char* read = dst;
|
||||||
|
char* write = dst;
|
||||||
|
|
||||||
|
while (*read) {
|
||||||
|
if (read[0] == '.' && read[1] == '.' && read[2] == '/') {
|
||||||
|
read += 3;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read[0] == '.' && read[1] == '/') {
|
||||||
|
read += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read[0] == '/' && read[1] == '.' && read[2] == '/') {
|
||||||
|
read += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read[0] == '/' && read[1] == '.' && read[2] == '\0') {
|
||||||
|
if (write == dst) {
|
||||||
|
*write++ = '/';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_dotdot_mid = (read[0] == '/' && read[1] == '.' && read[2] == '.' && read[3] == '/');
|
||||||
|
bool is_dotdot_end =
|
||||||
|
(read[0] == '/' && read[1] == '.' && read[2] == '.' && read[3] == '\0');
|
||||||
|
|
||||||
|
if (is_dotdot_mid || is_dotdot_end) {
|
||||||
|
if (write > dst) {
|
||||||
|
if (*(write - 1) == '/') {
|
||||||
|
write--;
|
||||||
|
}
|
||||||
|
while (write > dst && *(write - 1) != '/') {
|
||||||
|
write--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_dotdot_mid && write > dst) {
|
||||||
|
write--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_dotdot_mid) {
|
||||||
|
read += 3;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((read[0] == '.' && read[1] == '\0') ||
|
||||||
|
(read[0] == '.' && read[1] == '.' && read[2] == '\0')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read[0] == '/') {
|
||||||
|
*write++ = *read++;
|
||||||
|
}
|
||||||
|
while (*read && *read != '/') {
|
||||||
|
*write++ = *read++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*write = '\0';
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in) {
|
int PS4_SYSV_ABI sceHttpUriUnescape(char* out, u64* require, u64 prepare, const char* in) {
|
||||||
LOG_ERROR(Lib_Http, "(STUBBED) called");
|
LOG_TRACE(Lib_Http, "called");
|
||||||
|
|
||||||
|
if (!in) {
|
||||||
|
LOG_ERROR(Lib_Http, "Invalid input string");
|
||||||
|
return ORBIS_HTTP_ERROR_INVALID_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locale-independent hex digit check
|
||||||
|
auto IsHex = [](char c) -> bool {
|
||||||
|
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert hex char to int value
|
||||||
|
auto HexToInt = [](char c) -> int {
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for valid percent-encoded sequence (%XX)
|
||||||
|
auto IsValidPercentSequence = [&](const char* s) -> bool {
|
||||||
|
return s[0] == '%' && s[1] != '\0' && s[2] != '\0' && IsHex(s[1]) && IsHex(s[2]);
|
||||||
|
};
|
||||||
|
|
||||||
|
u64 needed = 0;
|
||||||
|
const char* src = in;
|
||||||
|
while (*src) {
|
||||||
|
if (IsValidPercentSequence(src)) {
|
||||||
|
src += 3;
|
||||||
|
} else {
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
needed++;
|
||||||
|
}
|
||||||
|
needed++; // null terminator
|
||||||
|
|
||||||
|
if (require) {
|
||||||
|
*require = needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!out) {
|
||||||
|
return ORBIS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prepare < needed) {
|
||||||
|
LOG_ERROR(Lib_Http, "Buffer too small: need {} but only {} available", needed, prepare);
|
||||||
|
return ORBIS_HTTP_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
src = in;
|
||||||
|
char* dst = out;
|
||||||
|
while (*src) {
|
||||||
|
if (IsValidPercentSequence(src)) {
|
||||||
|
*dst++ = static_cast<char>((HexToInt(src[1]) << 4) | HexToInt(src[2]));
|
||||||
|
src += 3;
|
||||||
|
} else {
|
||||||
|
*dst++ = *src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*dst = '\0';
|
||||||
|
|
||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -148,7 +148,7 @@ int PS4_SYSV_ABI sceHttpUnsetEpoll();
|
|||||||
int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare,
|
int PS4_SYSV_ABI sceHttpUriBuild(char* out, u64* require, u64 prepare,
|
||||||
const OrbisHttpUriElement* srcElement, u32 option);
|
const OrbisHttpUriElement* srcElement, u32 option);
|
||||||
int PS4_SYSV_ABI sceHttpUriCopy();
|
int PS4_SYSV_ABI sceHttpUriCopy();
|
||||||
int PS4_SYSV_ABI sceHttpUriEscape();
|
int PS4_SYSV_ABI sceHttpUriEscape(char* out, u64* require, u64 prepare, const char* in);
|
||||||
int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require,
|
int PS4_SYSV_ABI sceHttpUriMerge(char* mergedUrl, char* url, char* relativeUri, u64* require,
|
||||||
u64 prepare, u32 option);
|
u64 prepare, u32 option);
|
||||||
int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool,
|
int PS4_SYSV_ABI sceHttpUriParse(OrbisHttpUriElement* out, const char* srcUri, void* pool,
|
||||||
|
|||||||
@ -1050,7 +1050,14 @@ void Translator::V_CMP_U32(ConditionOp op, bool is_signed, bool set_exec, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Translator::V_CMP_U64(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst) {
|
void Translator::V_CMP_U64(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst) {
|
||||||
ASSERT(inst.src[1].field == OperandField::ConstZero);
|
const bool is_zero = inst.src[1].field == OperandField::ConstZero;
|
||||||
|
const bool is_neg_one = inst.src[1].field == OperandField::SignedConstIntNeg;
|
||||||
|
ASSERT(is_zero || is_neg_one);
|
||||||
|
if (is_neg_one) {
|
||||||
|
ASSERT_MSG(-s32(inst.src[1].code) + SignedConstIntNegMin - 1 == -1,
|
||||||
|
"SignedConstIntNeg must be -1");
|
||||||
|
}
|
||||||
|
|
||||||
const IR::U1 src0 = [&] {
|
const IR::U1 src0 = [&] {
|
||||||
switch (inst.src[0].field) {
|
switch (inst.src[0].field) {
|
||||||
case OperandField::ScalarGPR:
|
case OperandField::ScalarGPR:
|
||||||
@ -1064,10 +1071,11 @@ void Translator::V_CMP_U64(ConditionOp op, bool is_signed, bool set_exec, const
|
|||||||
const IR::U1 result = [&] {
|
const IR::U1 result = [&] {
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case ConditionOp::EQ:
|
case ConditionOp::EQ:
|
||||||
return ir.LogicalNot(src0);
|
return is_zero ? ir.LogicalNot(src0) : src0;
|
||||||
case ConditionOp::LG: // NE
|
case ConditionOp::LG: // NE
|
||||||
return src0;
|
return is_zero ? src0 : ir.LogicalNot(src0);
|
||||||
case ConditionOp::GT:
|
case ConditionOp::GT:
|
||||||
|
ASSERT(is_zero);
|
||||||
return ir.GroupAny(ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[0].code)));
|
return ir.GroupAny(ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[0].code)));
|
||||||
default:
|
default:
|
||||||
UNREACHABLE_MSG("Unsupported V_CMP_U64 condition operation: {}", u32(op));
|
UNREACHABLE_MSG("Unsupported V_CMP_U64 condition operation: {}", u32(op));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user