[dynarmic] fix ODR violations (#3749)
Some checks are pending
tx-src / sources (push) Waiting to run
Check Strings / check-strings (push) Waiting to run

Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3749
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
lizzie 2026-03-30 04:58:30 +02:00 committed by crueter
parent 276dcdd8ea
commit 5322bce4b8
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
15 changed files with 99 additions and 100 deletions

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
@ -642,7 +642,7 @@ void EmitIR<IR::Opcode::ArithmeticShiftRight64>(oaknut::CodeGenerator& code, Emi
} }
template<> template<>
void EmitIR<IR::Opcode::RotateRight32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { void EmitIR<IR::Opcode::BitRotateRight32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@ -708,7 +708,7 @@ void EmitIR<IR::Opcode::RotateRight32>(oaknut::CodeGenerator& code, EmitContext&
} }
template<> template<>
void EmitIR<IR::Opcode::RotateRight64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { void EmitIR<IR::Opcode::BitRotateRight64>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto& operand_arg = args[0]; auto& operand_arg = args[0];
auto& shift_arg = args[1]; auto& shift_arg = args[1];

View file

@ -209,9 +209,9 @@ void CallbackOnlyEmitExclusiveWriteMemory(oaknut::CodeGenerator& code, EmitConte
ctx.reg_alloc.DefineAsRegister(inst, X0); ctx.reg_alloc.DefineAsRegister(inst, X0);
} }
constexpr size_t page_bits = 12; constexpr size_t page_table_const_bits = 12;
constexpr size_t page_size = 1 << page_bits; constexpr size_t page_table_const_size = 1 << page_table_const_bits;
constexpr size_t page_mask = (1 << page_bits) - 1; constexpr size_t page_table_const_mask = (1 << page_table_const_bits) - 1;
// This function may use Xscratch0 as a scratch register // This function may use Xscratch0 as a scratch register
// Trashes NZCV // Trashes NZCV
@ -242,28 +242,28 @@ void EmitDetectMisalignedVAddr(oaknut::CodeGenerator& code, EmitContext& ctx, oa
code.TST(Xaddr, align_mask); code.TST(Xaddr, align_mask);
code.B(NE, *fallback); code.B(NE, *fallback);
} else { } else {
// If (addr & page_mask) > page_size - byte_size, use fallback. // If (addr & page_table_const_mask) > page_table_const_size - byte_size, use fallback.
code.AND(Xscratch0, Xaddr, page_mask); code.AND(Xscratch0, Xaddr, page_table_const_mask);
code.CMP(Xscratch0, page_size - bitsize / 8); code.CMP(Xscratch0, page_table_const_size - bitsize / 8);
code.B(HI, *fallback); code.B(HI, *fallback);
} }
} }
// Outputs Xscratch0 = page_table[addr >> page_bits] // Outputs Xscratch0 = page_table[addr >> page_table_const_bits]
// May use Xscratch1 as scratch register // May use Xscratch1 as scratch register
// Address to read/write = [ret0 + ret1], ret0 is always Xscratch0 and ret1 is either Xaddr or Xscratch1 // Address to read/write = [ret0 + ret1], ret0 is always Xscratch0 and ret1 is either Xaddr or Xscratch1
// Trashes NZCV // Trashes NZCV
template<size_t bitsize> template<size_t bitsize>
std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::CodeGenerator& code, EmitContext& ctx, oaknut::XReg Xaddr, const SharedLabel& fallback) { std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::CodeGenerator& code, EmitContext& ctx, oaknut::XReg Xaddr, const SharedLabel& fallback) {
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_bits; const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_table_const_bits;
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits; const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
EmitDetectMisalignedVAddr<bitsize>(code, ctx, Xaddr, fallback); EmitDetectMisalignedVAddr<bitsize>(code, ctx, Xaddr, fallback);
if (ctx.conf.silently_mirror_page_table || unused_top_bits == 0) { if (ctx.conf.silently_mirror_page_table || unused_top_bits == 0) {
code.UBFX(Xscratch0, Xaddr, page_bits, valid_page_index_bits); code.UBFX(Xscratch0, Xaddr, page_table_const_bits, valid_page_index_bits);
} else { } else {
code.LSR(Xscratch0, Xaddr, page_bits); code.LSR(Xscratch0, Xaddr, page_table_const_bits);
code.TST(Xscratch0, u64(~u64(0)) << valid_page_index_bits); code.TST(Xscratch0, u64(~u64(0)) << valid_page_index_bits);
code.B(NE, *fallback); code.B(NE, *fallback);
} }
@ -283,7 +283,7 @@ std::pair<oaknut::XReg, oaknut::XReg> InlinePageTableEmitVAddrLookup(oaknut::Cod
if (ctx.conf.absolute_offset_page_table) { if (ctx.conf.absolute_offset_page_table) {
return std::make_pair(Xscratch0, Xaddr); return std::make_pair(Xscratch0, Xaddr);
} }
code.AND(Xscratch1, Xaddr, page_mask); code.AND(Xscratch1, Xaddr, page_table_const_mask);
return std::make_pair(Xscratch0, Xscratch1); return std::make_pair(Xscratch0, Xscratch1);
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
* Copyright (c) 2024 MerryMage * Copyright (c) 2024 MerryMage
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
@ -164,12 +167,12 @@ void EmitIR<IR::Opcode::ArithmeticShiftRight64>(biscuit::Assembler&, EmitContext
} }
template<> template<>
void EmitIR<IR::Opcode::RotateRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) { void EmitIR<IR::Opcode::BitRotateRight32>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
template<> template<>
void EmitIR<IR::Opcode::RotateRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) { void EmitIR<IR::Opcode::BitRotateRight64>(biscuit::Assembler&, EmitContext&, IR::Inst*) {
UNIMPLEMENTED(); UNIMPLEMENTED();
} }

View file

@ -663,7 +663,7 @@ void EmitX64::EmitArithmeticShiftRight64(EmitContext& ctx, IR::Inst* inst) {
} }
} }
void EmitX64::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitBitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp);
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@ -736,7 +736,7 @@ void EmitX64::EmitRotateRight32(EmitContext& ctx, IR::Inst* inst) {
} }
} }
void EmitX64::EmitRotateRight64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitBitRotateRight64(EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
auto& operand_arg = args[0]; auto& operand_arg = args[0];
auto& shift_arg = args[1]; auto& shift_arg = args[1];

View file

@ -6,6 +6,8 @@
* SPDX-License-Identifier: 0BSD * SPDX-License-Identifier: 0BSD
*/ */
#pragma once
#include <bit> #include <bit>
#include "dynarmic/backend/x64/xbyak.h" #include "dynarmic/backend/x64/xbyak.h"
@ -22,9 +24,9 @@ namespace {
using namespace Xbyak::util; using namespace Xbyak::util;
constexpr size_t page_bits = 12; constexpr size_t page_table_const_bits = 12;
constexpr size_t page_size = 1 << page_bits; constexpr size_t page_table_const_size = 1 << page_table_const_bits;
constexpr size_t page_mask = (1 << page_bits) - 1; constexpr size_t page_table_const_mask = (1 << page_table_const_bits) - 1;
template<typename EmitContext> template<typename EmitContext>
void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr, Xbyak::Reg64 tmp) { void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr, Xbyak::Reg64 tmp) {
@ -50,7 +52,7 @@ void EmitDetectMisalignedVAddr(BlockOfCode& code, EmitContext& ctx, size_t bitsi
code.test(vaddr, align_mask); code.test(vaddr, align_mask);
if (ctx.conf.only_detect_misalignment_via_page_table_on_page_boundary) { if (ctx.conf.only_detect_misalignment_via_page_table_on_page_boundary) {
const u32 page_align_mask = static_cast<u32>(page_size - 1) & ~align_mask; const u32 page_align_mask = static_cast<u32>(page_table_const_size - 1) & ~align_mask;
SharedLabel detect_boundary = GenSharedLabel(), resume = GenSharedLabel(); SharedLabel detect_boundary = GenSharedLabel(), resume = GenSharedLabel();
@ -83,7 +85,7 @@ template<>
// TODO: This code assumes vaddr has been zext from 32-bits to 64-bits. // TODO: This code assumes vaddr has been zext from 32-bits to 64-bits.
code.mov(tmp, vaddr.cvt32()); code.mov(tmp, vaddr.cvt32());
code.shr(tmp, int(page_bits)); code.shr(tmp, int(page_table_const_bits));
code.shl(tmp, int(ctx.conf.page_table_log2_stride)); code.shl(tmp, int(ctx.conf.page_table_log2_stride));
code.mov(page, qword[r14 + tmp.cvt64()]); code.mov(page, qword[r14 + tmp.cvt64()]);
if (ctx.conf.page_table_pointer_mask_bits == 0) { if (ctx.conf.page_table_pointer_mask_bits == 0) {
@ -96,13 +98,13 @@ template<>
return page + vaddr; return page + vaddr;
} }
code.mov(tmp, vaddr.cvt32()); code.mov(tmp, vaddr.cvt32());
code.and_(tmp, static_cast<u32>(page_mask)); code.and_(tmp, static_cast<u32>(page_table_const_mask));
return page + tmp.cvt64(); return page + tmp.cvt64();
} }
template<> template<>
[[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A64EmitContext>(BlockOfCode& code, A64EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) { [[maybe_unused]] Xbyak::RegExp EmitVAddrLookup<A64EmitContext>(BlockOfCode& code, A64EmitContext& ctx, size_t bitsize, Xbyak::Label& abort, Xbyak::Reg64 vaddr) {
const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_bits; const size_t valid_page_index_bits = ctx.conf.page_table_address_space_bits - page_table_const_bits;
const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits; const size_t unused_top_bits = 64 - ctx.conf.page_table_address_space_bits;
const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code); const Xbyak::Reg64 page = ctx.reg_alloc.ScratchGpr(code);
@ -112,29 +114,29 @@ template<>
if (unused_top_bits == 0) { if (unused_top_bits == 0) {
code.mov(tmp, vaddr); code.mov(tmp, vaddr);
code.shr(tmp, int(page_bits)); code.shr(tmp, int(page_table_const_bits));
} else if (ctx.conf.silently_mirror_page_table) { } else if (ctx.conf.silently_mirror_page_table) {
if (valid_page_index_bits >= 32) { if (valid_page_index_bits >= 32) {
if (code.HasHostFeature(HostFeature::BMI2)) { if (code.HasHostFeature(HostFeature::BMI2)) {
const Xbyak::Reg64 bit_count = ctx.reg_alloc.ScratchGpr(code); const Xbyak::Reg64 bit_count = ctx.reg_alloc.ScratchGpr(code);
code.mov(bit_count, unused_top_bits); code.mov(bit_count, unused_top_bits);
code.bzhi(tmp, vaddr, bit_count); code.bzhi(tmp, vaddr, bit_count);
code.shr(tmp, int(page_bits)); code.shr(tmp, int(page_table_const_bits));
ctx.reg_alloc.Release(bit_count); ctx.reg_alloc.Release(bit_count);
} else { } else {
code.mov(tmp, vaddr); code.mov(tmp, vaddr);
code.shl(tmp, int(unused_top_bits)); code.shl(tmp, int(unused_top_bits));
code.shr(tmp, int(unused_top_bits + page_bits)); code.shr(tmp, int(unused_top_bits + page_table_const_bits));
} }
} else { } else {
code.mov(tmp, vaddr); code.mov(tmp, vaddr);
code.shr(tmp, int(page_bits)); code.shr(tmp, int(page_table_const_bits));
code.and_(tmp, u32((1 << valid_page_index_bits) - 1)); code.and_(tmp, u32((1 << valid_page_index_bits) - 1));
} }
} else { } else {
ASSERT(valid_page_index_bits < 32); ASSERT(valid_page_index_bits < 32);
code.mov(tmp, vaddr); code.mov(tmp, vaddr);
code.shr(tmp, int(page_bits)); code.shr(tmp, int(page_table_const_bits));
code.test(tmp, u32(-(1 << valid_page_index_bits))); code.test(tmp, u32(-(1 << valid_page_index_bits)));
code.jnz(abort, code.T_NEAR); code.jnz(abort, code.T_NEAR);
} }
@ -151,7 +153,7 @@ template<>
return page + vaddr; return page + vaddr;
} }
code.mov(tmp, vaddr); code.mov(tmp, vaddr);
code.and_(tmp, static_cast<u32>(page_mask)); code.and_(tmp, static_cast<u32>(page_table_const_mask));
return page + tmp; return page + tmp;
} }

View file

@ -25,12 +25,12 @@ using namespace Xbyak::util;
namespace { namespace {
enum class Op { enum class SaturationOp {
Add, Add,
Sub, Sub,
}; };
template<Op op, size_t size, bool has_overflow_inst = false> template<SaturationOp op, size_t size, bool has_overflow_inst = false>
void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
@ -51,7 +51,7 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst)
// overflow now contains 0x7F... if a was positive, or 0x80... if a was negative // overflow now contains 0x7F... if a was positive, or 0x80... if a was negative
if constexpr (op == Op::Add) { if constexpr (op == SaturationOp::Add) {
code.add(result, addend); code.add(result, addend);
} else { } else {
code.sub(result, addend); code.sub(result, addend);
@ -75,16 +75,16 @@ void EmitSignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst)
ctx.reg_alloc.DefineValue(code, inst, result); ctx.reg_alloc.DefineValue(code, inst, result);
} }
template<Op op, size_t size> template<SaturationOp op, size_t size>
void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
auto args = ctx.reg_alloc.GetArgumentInfo(inst); auto args = ctx.reg_alloc.GetArgumentInfo(inst);
Xbyak::Reg op_result = ctx.reg_alloc.UseScratchGpr(code, args[0]).changeBit(size); Xbyak::Reg op_result = ctx.reg_alloc.UseScratchGpr(code, args[0]).changeBit(size);
Xbyak::Reg addend = ctx.reg_alloc.UseScratchGpr(code, args[1]).changeBit(size); Xbyak::Reg addend = ctx.reg_alloc.UseScratchGpr(code, args[1]).changeBit(size);
constexpr u64 boundary = op == Op::Add ? (std::numeric_limits<mcl::unsigned_integer_of_size<size>>::max)() : 0; constexpr u64 boundary = op == SaturationOp::Add ? (std::numeric_limits<mcl::unsigned_integer_of_size<size>>::max)() : 0;
if constexpr (op == Op::Add) { if constexpr (op == SaturationOp::Add) {
code.add(op_result, addend); code.add(op_result, addend);
} else { } else {
code.sub(op_result, addend); code.sub(op_result, addend);
@ -106,11 +106,11 @@ void EmitUnsignedSaturatedOp(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst
} // anonymous namespace } // anonymous namespace
void EmitX64::EmitSignedSaturatedAddWithFlag32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedAddWithFlag32(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Add, 32, true>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Add, 32, true>(code, ctx, inst);
} }
void EmitX64::EmitSignedSaturatedSubWithFlag32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedSubWithFlag32(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Sub, 32, true>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Sub, 32, true>(code, ctx, inst);
} }
void EmitX64::EmitSignedSaturation(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturation(EmitContext& ctx, IR::Inst* inst) {
@ -192,19 +192,19 @@ void EmitX64::EmitUnsignedSaturation(EmitContext& ctx, IR::Inst* inst) {
} }
void EmitX64::EmitSignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Add, 8>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Add, 8>(code, ctx, inst);
} }
void EmitX64::EmitSignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Add, 16>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Add, 16>(code, ctx, inst);
} }
void EmitX64::EmitSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Add, 32>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Add, 32>(code, ctx, inst);
} }
void EmitX64::EmitSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Add, 64>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Add, 64>(code, ctx, inst);
} }
void EmitX64::EmitSignedSaturatedDoublingMultiplyReturnHigh16(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedDoublingMultiplyReturnHigh16(EmitContext& ctx, IR::Inst* inst) {
@ -256,51 +256,51 @@ void EmitX64::EmitSignedSaturatedDoublingMultiplyReturnHigh32(EmitContext& ctx,
} }
void EmitX64::EmitSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Sub, 8>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Sub, 8>(code, ctx, inst);
} }
void EmitX64::EmitSignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Sub, 16>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Sub, 16>(code, ctx, inst);
} }
void EmitX64::EmitSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Sub, 32>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Sub, 32>(code, ctx, inst);
} }
void EmitX64::EmitSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
EmitSignedSaturatedOp<Op::Sub, 64>(code, ctx, inst); EmitSignedSaturatedOp<SaturationOp::Sub, 64>(code, ctx, inst);
} }
void EmitX64::EmitUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
EmitUnsignedSaturatedOp<Op::Add, 8>(code, ctx, inst); EmitUnsignedSaturatedOp<SaturationOp::Add, 8>(code, ctx, inst);
} }
void EmitX64::EmitUnsignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitUnsignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
EmitUnsignedSaturatedOp<Op::Add, 16>(code, ctx, inst); EmitUnsignedSaturatedOp<SaturationOp::Add, 16>(code, ctx, inst);
} }
void EmitX64::EmitUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
EmitUnsignedSaturatedOp<Op::Add, 32>(code, ctx, inst); EmitUnsignedSaturatedOp<SaturationOp::Add, 32>(code, ctx, inst);
} }
void EmitX64::EmitUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
EmitUnsignedSaturatedOp<Op::Add, 64>(code, ctx, inst); EmitUnsignedSaturatedOp<SaturationOp::Add, 64>(code, ctx, inst);
} }
void EmitX64::EmitUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
EmitUnsignedSaturatedOp<Op::Sub, 8>(code, ctx, inst); EmitUnsignedSaturatedOp<SaturationOp::Sub, 8>(code, ctx, inst);
} }
void EmitX64::EmitUnsignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitUnsignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
EmitUnsignedSaturatedOp<Op::Sub, 16>(code, ctx, inst); EmitUnsignedSaturatedOp<SaturationOp::Sub, 16>(code, ctx, inst);
} }
void EmitX64::EmitUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
EmitUnsignedSaturatedOp<Op::Sub, 32>(code, ctx, inst); EmitUnsignedSaturatedOp<SaturationOp::Sub, 32>(code, ctx, inst);
} }
void EmitX64::EmitUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
EmitUnsignedSaturatedOp<Op::Sub, 64>(code, ctx, inst); EmitUnsignedSaturatedOp<SaturationOp::Sub, 64>(code, ctx, inst);
} }
} // namespace Dynarmic::Backend::X64 } // namespace Dynarmic::Backend::X64

View file

@ -5854,3 +5854,5 @@ void EmitX64::EmitZeroVector(EmitContext& ctx, IR::Inst* inst) {
} }
} // namespace Dynarmic::Backend::X64 } // namespace Dynarmic::Backend::X64
#undef ICODE

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
@ -52,12 +52,12 @@ void EmitVectorSaturatedNative(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
ctx.reg_alloc.DefineValue(code, inst, result); ctx.reg_alloc.DefineValue(code, inst, result);
} }
enum class Op { enum class VectorSaturationOp {
Add, Add,
Sub, Sub,
}; };
template<Op op, size_t esize> template<VectorSaturationOp op, size_t esize>
void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
static_assert(esize == 32 || esize == 64); static_assert(esize == 32 || esize == 64);
constexpr u64 msb_mask = esize == 32 ? 0x8000000080000000 : 0x8000000000000000; constexpr u64 msb_mask = esize == 32 ? 0x8000000080000000 : 0x8000000000000000;
@ -72,7 +72,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
code.movaps(xmm0, operand1); code.movaps(xmm0, operand1);
if constexpr (op == Op::Add) { if constexpr (op == VectorSaturationOp::Add) {
ICODE(vpadd)(result, operand1, operand2); ICODE(vpadd)(result, operand1, operand2);
code.vpternlogd(xmm0, result, operand2, 0b00100100); code.vpternlogd(xmm0, result, operand2, 0b00100100);
} else { } else {
@ -102,7 +102,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code); const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
if (code.HasHostFeature(HostFeature::AVX)) { if (code.HasHostFeature(HostFeature::AVX)) {
if constexpr (op == Op::Add) { if constexpr (op == VectorSaturationOp::Add) {
ICODE(vpadd)(result, operand1, operand2); ICODE(vpadd)(result, operand1, operand2);
} else { } else {
ICODE(vpsub)(result, operand1, operand2); ICODE(vpsub)(result, operand1, operand2);
@ -112,7 +112,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
} else { } else {
code.movaps(xmm0, operand1); code.movaps(xmm0, operand1);
code.movaps(tmp, operand1); code.movaps(tmp, operand1);
if constexpr (op == Op::Add) { if constexpr (op == VectorSaturationOp::Add) {
ICODE(padd)(result, operand2); ICODE(padd)(result, operand2);
} else { } else {
ICODE(psub)(result, operand2); ICODE(psub)(result, operand2);
@ -121,7 +121,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
code.pxor(tmp, result); code.pxor(tmp, result);
} }
if constexpr (op == Op::Add) { if constexpr (op == VectorSaturationOp::Add) {
code.pandn(xmm0, tmp); code.pandn(xmm0, tmp);
} else { } else {
code.pand(xmm0, tmp); code.pand(xmm0, tmp);
@ -165,7 +165,7 @@ void EmitVectorSignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* in
} }
} }
template<Op op, size_t esize> template<VectorSaturationOp op, size_t esize>
void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) { void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst* inst) {
static_assert(esize == 32 || esize == 64); static_assert(esize == 32 || esize == 64);
@ -177,7 +177,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code); const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8(); const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8();
if constexpr (op == Op::Add) { if constexpr (op == VectorSaturationOp::Add) {
ICODE(vpadd)(result, operand1, operand2); ICODE(vpadd)(result, operand1, operand2);
ICODE(vpcmpu)(k1, result, operand1, CmpInt::LessThan); ICODE(vpcmpu)(k1, result, operand1, CmpInt::LessThan);
ICODE(vpternlog)(result | k1, result, result, u8(0xFF)); ICODE(vpternlog)(result | k1, result, result, u8(0xFF));
@ -201,7 +201,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8(); const Xbyak::Reg8 overflow = ctx.reg_alloc.ScratchGpr(code).cvt8();
const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code); const Xbyak::Xmm tmp = ctx.reg_alloc.ScratchXmm(code);
if constexpr (op == Op::Add) { if constexpr (op == VectorSaturationOp::Add) {
if (code.HasHostFeature(HostFeature::AVX)) { if (code.HasHostFeature(HostFeature::AVX)) {
code.vpxor(xmm0, operand1, operand2); code.vpxor(xmm0, operand1, operand2);
code.vpand(tmp, operand1, operand2); code.vpand(tmp, operand1, operand2);
@ -250,7 +250,7 @@ void EmitVectorUnsignedSaturated(BlockOfCode& code, EmitContext& ctx, IR::Inst*
code.setnz(overflow); code.setnz(overflow);
code.or_(code.byte[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_qc], overflow); code.or_(code.byte[code.ABI_JIT_PTR + code.GetJitStateInfo().offsetof_fpsr_qc], overflow);
if constexpr (op == Op::Add) { if constexpr (op == VectorSaturationOp::Add) {
code.por(result, tmp); code.por(result, tmp);
ctx.reg_alloc.DefineValue(code, inst, result); ctx.reg_alloc.DefineValue(code, inst, result);
} else { } else {
@ -270,11 +270,11 @@ void EmitX64::EmitVectorSignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst) {
} }
void EmitX64::EmitVectorSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorSignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
EmitVectorSignedSaturated<Op::Add, 32>(code, ctx, inst); EmitVectorSignedSaturated<VectorSaturationOp::Add, 32>(code, ctx, inst);
} }
void EmitX64::EmitVectorSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorSignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
EmitVectorSignedSaturated<Op::Add, 64>(code, ctx, inst); EmitVectorSignedSaturated<VectorSaturationOp::Add, 64>(code, ctx, inst);
} }
void EmitX64::EmitVectorSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorSignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
@ -286,11 +286,11 @@ void EmitX64::EmitVectorSignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst) {
} }
void EmitX64::EmitVectorSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorSignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
EmitVectorSignedSaturated<Op::Sub, 32>(code, ctx, inst); EmitVectorSignedSaturated<VectorSaturationOp::Sub, 32>(code, ctx, inst);
} }
void EmitX64::EmitVectorSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorSignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
EmitVectorSignedSaturated<Op::Sub, 64>(code, ctx, inst); EmitVectorSignedSaturated<VectorSaturationOp::Sub, 64>(code, ctx, inst);
} }
void EmitX64::EmitVectorUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorUnsignedSaturatedAdd8(EmitContext& ctx, IR::Inst* inst) {
@ -302,11 +302,11 @@ void EmitX64::EmitVectorUnsignedSaturatedAdd16(EmitContext& ctx, IR::Inst* inst)
} }
void EmitX64::EmitVectorUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorUnsignedSaturatedAdd32(EmitContext& ctx, IR::Inst* inst) {
EmitVectorUnsignedSaturated<Op::Add, 32>(code, ctx, inst); EmitVectorUnsignedSaturated<VectorSaturationOp::Add, 32>(code, ctx, inst);
} }
void EmitX64::EmitVectorUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorUnsignedSaturatedAdd64(EmitContext& ctx, IR::Inst* inst) {
EmitVectorUnsignedSaturated<Op::Add, 64>(code, ctx, inst); EmitVectorUnsignedSaturated<VectorSaturationOp::Add, 64>(code, ctx, inst);
} }
void EmitX64::EmitVectorUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorUnsignedSaturatedSub8(EmitContext& ctx, IR::Inst* inst) {
@ -318,11 +318,11 @@ void EmitX64::EmitVectorUnsignedSaturatedSub16(EmitContext& ctx, IR::Inst* inst)
} }
void EmitX64::EmitVectorUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorUnsignedSaturatedSub32(EmitContext& ctx, IR::Inst* inst) {
EmitVectorUnsignedSaturated<Op::Sub, 32>(code, ctx, inst); EmitVectorUnsignedSaturated<VectorSaturationOp::Sub, 32>(code, ctx, inst);
} }
void EmitX64::EmitVectorUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) { void EmitX64::EmitVectorUnsignedSaturatedSub64(EmitContext& ctx, IR::Inst* inst) {
EmitVectorUnsignedSaturated<Op::Sub, 64>(code, ctx, inst); EmitVectorUnsignedSaturated<VectorSaturationOp::Sub, 64>(code, ctx, inst);
} }
} // namespace Dynarmic::Backend::X64 } // namespace Dynarmic::Backend::X64

View file

@ -35,7 +35,7 @@ FPUnpacked FusedMulAdd(FPUnpacked addend, FPUnpacked op1, FPUnpacked op2) {
return std::make_tuple(exponent, value); return std::make_tuple(exponent, value);
}(); }();
if (product_value == 0) { if (product_value == u128(0, 0)) {
return addend; return addend;
} }
@ -55,13 +55,13 @@ FPUnpacked FusedMulAdd(FPUnpacked addend, FPUnpacked op1, FPUnpacked op2) {
} }
// addend < product // addend < product
const u128 result = product_value + StickyLogicalShiftRight(addend.mantissa, exp_diff - normalized_point_position); const u128 result = product_value + StickyLogicalShiftRight(u128(addend.mantissa, 0), exp_diff - normalized_point_position);
return ReduceMantissa(product_sign, product_exponent, result); return ReduceMantissa(product_sign, product_exponent, result);
} }
// Subtraction // Subtraction
const u128 addend_long = u128(addend.mantissa) << normalized_point_position; const u128 addend_long = u128(addend.mantissa, 0) << normalized_point_position;
bool result_sign; bool result_sign;
u128 result; u128 result;

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
@ -137,7 +137,7 @@ u128 StickyLogicalShiftRight(u128 operand, int amount) {
} }
if (operand.lower != 0 || operand.upper != 0) { if (operand.lower != 0 || operand.upper != 0) {
return u128(1); return u128(1, 0);
} }
return {}; return {};
} }

View file

@ -22,22 +22,13 @@ struct u128 {
u128(u128&&) = default; u128(u128&&) = default;
u128& operator=(const u128&) = default; u128& operator=(const u128&) = default;
u128& operator=(u128&&) = default; u128& operator=(u128&&) = default;
explicit u128(u64 lower_, u64 upper_) : lower(lower_), upper(upper_) {}
u128(u64 lower_, u64 upper_)
: lower(lower_), upper(upper_) {}
template<typename T>
/* implicit */ u128(T value)
: lower(value), upper(0) {
static_assert(std::is_integral_v<T>);
static_assert(mcl::bitsizeof<T> <= mcl::bitsizeof<u64>);
}
u64 lower = 0; u64 lower = 0;
u64 upper = 0; u64 upper = 0;
template<size_t bit_position> template<size_t bit_position>
bool Bit() const { [[nodiscard]] inline bool Bit() const {
static_assert(bit_position < 128); static_assert(bit_position < 128);
if constexpr (bit_position < 64) { if constexpr (bit_position < 64) {
return mcl::bit::get_bit<bit_position>(lower); return mcl::bit::get_bit<bit_position>(lower);

View file

@ -228,7 +228,7 @@ public:
} }
ResultAndCarry<U32> RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) { ResultAndCarry<U32> RotateRight(const U32& value_in, const U8& shift_amount, const U1& carry_in) {
const auto result = Inst<U32>(Opcode::RotateRight32, value_in, shift_amount, carry_in); const auto result = Inst<U32>(Opcode::BitRotateRight32, value_in, shift_amount, carry_in);
const auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result); const auto carry_out = Inst<U1>(Opcode::GetCarryFromOp, result);
return {result, carry_out}; return {result, carry_out};
} }
@ -265,9 +265,9 @@ public:
U32U64 RotateRight(const U32U64& value_in, const U8& shift_amount) { U32U64 RotateRight(const U32U64& value_in, const U8& shift_amount) {
if (value_in.GetType() == Type::U32) { if (value_in.GetType() == Type::U32) {
return Inst<U32>(Opcode::RotateRight32, value_in, shift_amount, Imm1(0)); return Inst<U32>(Opcode::BitRotateRight32, value_in, shift_amount, Imm1(0));
} else { } else {
return Inst<U64>(Opcode::RotateRight64, value_in, shift_amount); return Inst<U64>(Opcode::BitRotateRight64, value_in, shift_amount);
} }
} }

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project. /* This file is part of the dynarmic project.
@ -45,8 +45,8 @@ constexpr bool IsArithmeticShift(const Opcode op) noexcept {
/// @brief Determines whether or not this instruction performs a logical shift. /// @brief Determines whether or not this instruction performs a logical shift.
constexpr bool IsCircularShift(const Opcode op) noexcept { constexpr bool IsCircularShift(const Opcode op) noexcept {
return op == Opcode::RotateRight32 return op == Opcode::BitRotateRight32
|| op == Opcode::RotateRight64 || op == Opcode::BitRotateRight64
|| op == Opcode::RotateRightExtended; || op == Opcode::RotateRightExtended;
} }

View file

@ -46,8 +46,9 @@ OPCODE(LogicalShiftRight32, U32, U32,
OPCODE(LogicalShiftRight64, U64, U64, U8 ) OPCODE(LogicalShiftRight64, U64, U64, U8 )
OPCODE(ArithmeticShiftRight32, U32, U32, U8, U1 ) OPCODE(ArithmeticShiftRight32, U32, U32, U8, U1 )
OPCODE(ArithmeticShiftRight64, U64, U64, U8 ) OPCODE(ArithmeticShiftRight64, U64, U64, U8 )
OPCODE(RotateRight32, U32, U32, U8, U1 ) // windows.h defines RotateRight64 and RotateRight32
OPCODE(RotateRight64, U64, U64, U8 ) OPCODE(BitRotateRight32, U32, U32, U8, U1 )
OPCODE(BitRotateRight64, U64, U64, U8 )
OPCODE(RotateRightExtended, U32, U32, U1 ) OPCODE(RotateRightExtended, U32, U32, U1 )
OPCODE(LogicalShiftLeftMasked32, U32, U32, U32 ) OPCODE(LogicalShiftLeftMasked32, U32, U32, U32 )
OPCODE(LogicalShiftLeftMasked64, U64, U64, U64 ) OPCODE(LogicalShiftLeftMasked64, U64, U64, U64 )

View file

@ -1072,12 +1072,12 @@ static void ConstantPropagation(IR::Block& block) {
ReplaceUsesWith(inst, false, Safe::ArithmeticShiftRight<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8())); ReplaceUsesWith(inst, false, Safe::ArithmeticShiftRight<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
} }
break; break;
case Op::RotateRight32: case Op::BitRotateRight32:
if (FoldShifts(inst)) { if (FoldShifts(inst)) {
ReplaceUsesWith(inst, true, mcl::bit::rotate_right<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8())); ReplaceUsesWith(inst, true, mcl::bit::rotate_right<u32>(inst.GetArg(0).GetU32(), inst.GetArg(1).GetU8()));
} }
break; break;
case Op::RotateRight64: case Op::BitRotateRight64:
if (FoldShifts(inst)) { if (FoldShifts(inst)) {
ReplaceUsesWith(inst, false, mcl::bit::rotate_right<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8())); ReplaceUsesWith(inst, false, mcl::bit::rotate_right<u64>(inst.GetArg(0).GetU64(), inst.GetArg(1).GetU8()));
} }