|
|
|
@ -1,3 +1,6 @@ |
|
|
|
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
|
|
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
|
|
@ -29,8 +32,8 @@ enum class AtomSize : u64 { |
|
|
|
S64, |
|
|
|
}; |
|
|
|
|
|
|
|
IR::U32U64 ApplyIntegerAtomOp(IR::IREmitter& ir, const IR::U32U64& offset, const IR::U32U64& op_b, |
|
|
|
AtomOp op, bool is_signed) { |
|
|
|
IR::U32U64 ApplyIntegerAtomOp(IR::IREmitter& ir, const IR::U32U64& offset, const IR::U32U64& op_b, AtomOp op, AtomSize size) { |
|
|
|
bool const is_signed = size == AtomSize::S64 || size == AtomSize::S32; |
|
|
|
switch (op) { |
|
|
|
case AtomOp::ADD: |
|
|
|
return ir.GlobalAtomicIAdd(offset, op_b); |
|
|
|
@ -70,7 +73,7 @@ IR::Value ApplyFpAtomOp(IR::IREmitter& ir, const IR::U64& offset, const IR::Valu |
|
|
|
switch (op) { |
|
|
|
case AtomOp::ADD: |
|
|
|
return size == AtomSize::F32 ? ir.GlobalAtomicF32Add(offset, op_b, f32_control) |
|
|
|
: ir.GlobalAtomicF16x2Add(offset, op_b, f16_control); |
|
|
|
: ir.GlobalAtomicF16x2Add(offset, op_b, f16_control); |
|
|
|
case AtomOp::MIN: |
|
|
|
return ir.GlobalAtomicF16x2Min(offset, op_b, f16_control); |
|
|
|
case AtomOp::MAX: |
|
|
|
@ -90,30 +93,34 @@ IR::U64 AtomOffset(TranslatorVisitor& v, u64 insn) { |
|
|
|
} const mem{insn}; |
|
|
|
|
|
|
|
const IR::U64 address{[&]() -> IR::U64 { |
|
|
|
if (mem.e == 0) { |
|
|
|
if (mem.e == 0) |
|
|
|
return v.ir.UConvert(64, v.X(mem.addr_reg)); |
|
|
|
} |
|
|
|
return v.L(mem.addr_reg); |
|
|
|
}()}; |
|
|
|
const u64 addr_offset{[&]() -> u64 { |
|
|
|
if (mem.addr_reg == IR::Reg::RZ) { |
|
|
|
// When RZ is used, the address is an absolute address
|
|
|
|
return static_cast<u64>(mem.rz_addr_offset.Value()); |
|
|
|
return u64(mem.rz_addr_offset.Value()); |
|
|
|
} else { |
|
|
|
return static_cast<u64>(mem.addr_offset.Value()); |
|
|
|
return u64(mem.addr_offset.Value()); |
|
|
|
} |
|
|
|
}()}; |
|
|
|
return v.ir.IAdd(address, v.ir.Imm64(addr_offset)); |
|
|
|
} |
|
|
|
|
|
|
|
// INC, DEC for U32/S32/U64 does nothing
|
|
|
|
// ADD, INC, DEC for S64 does nothing
|
|
|
|
// Only ADD does something for F32
|
|
|
|
// Only ADD, MIN and MAX does something for F16x2
|
|
|
|
bool AtomOpNotApplicable(AtomSize size, AtomOp op) { |
|
|
|
// TODO: SAFEADD
|
|
|
|
switch (size) { |
|
|
|
case AtomSize::U32: |
|
|
|
case AtomSize::S32: |
|
|
|
case AtomSize::U64: |
|
|
|
return (op == AtomOp::INC || op == AtomOp::DEC); |
|
|
|
case AtomSize::S64: |
|
|
|
return !(op == AtomOp::MIN || op == AtomOp::MAX); |
|
|
|
return (op == AtomOp::ADD || op == AtomOp::INC || op == AtomOp::DEC); |
|
|
|
case AtomSize::F32: |
|
|
|
return op != AtomOp::ADD; |
|
|
|
case AtomSize::F16x2: |
|
|
|
@ -159,15 +166,13 @@ IR::Value ApplyAtomOp(TranslatorVisitor& v, IR::Reg operand_reg, const IR::U64& |
|
|
|
switch (size) { |
|
|
|
case AtomSize::U32: |
|
|
|
case AtomSize::S32: |
|
|
|
return ApplyIntegerAtomOp(v.ir, offset, v.X(operand_reg), op, size == AtomSize::S32); |
|
|
|
case AtomSize::U64: |
|
|
|
case AtomSize::S64: |
|
|
|
return ApplyIntegerAtomOp(v.ir, offset, v.L(operand_reg), op, size == AtomSize::S64); |
|
|
|
return ApplyIntegerAtomOp(v.ir, offset, v.L(operand_reg), op, size); |
|
|
|
case AtomSize::F32: |
|
|
|
return ApplyFpAtomOp(v.ir, offset, v.F(operand_reg), op, size); |
|
|
|
case AtomSize::F16x2: { |
|
|
|
case AtomSize::F16x2: |
|
|
|
return ApplyFpAtomOp(v.ir, offset, v.ir.UnpackFloat2x16(v.X(operand_reg)), op, size); |
|
|
|
} |
|
|
|
default: |
|
|
|
throw NotImplementedException("Atom Size {}", size); |
|
|
|
} |
|
|
|
@ -175,15 +180,11 @@ IR::Value ApplyAtomOp(TranslatorVisitor& v, IR::Reg operand_reg, const IR::U64& |
|
|
|
|
|
|
|
void GlobalAtomic(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, |
|
|
|
const IR::U64& offset, AtomSize size, AtomOp op, bool write_dest) { |
|
|
|
IR::Value result; |
|
|
|
if (AtomOpNotApplicable(size, op)) { |
|
|
|
result = LoadGlobal(v.ir, offset, size); |
|
|
|
} else { |
|
|
|
result = ApplyAtomOp(v, operand_reg, offset, size, op); |
|
|
|
} |
|
|
|
if (write_dest) { |
|
|
|
IR::Value result = AtomOpNotApplicable(size, op) |
|
|
|
? LoadGlobal(v.ir, offset, size) |
|
|
|
: ApplyAtomOp(v, operand_reg, offset, size, op); |
|
|
|
if (write_dest) |
|
|
|
StoreResult(v, dest_reg, result, size); |
|
|
|
} |
|
|
|
} |
|
|
|
} // Anonymous namespace
|
|
|
|
|
|
|
|
|