From 6ab8bec2f0487c83de3c7de5932d0ec2c6416f7f Mon Sep 17 00:00:00 2001 From: Soumil Kushwaha Date: Wed, 17 Jun 2026 10:42:57 -0500 Subject: [PATCH 01/10] [rocjitsu] Flag-Dependent Read/Write Liveness Adds the destination register for SDWA and DPP instructions as a source so that InstDefUse and liveness analysis work soundly. Addresses issue #7033. --- .../lib/python/amdisa/codegen/_generator.py | 17 ++++++ .../rocjitsu/tests/analysis/liveness_test.cpp | 59 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py index 93751cd6e94b..3bd9306d464e 100644 --- a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py +++ b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py @@ -1241,6 +1241,23 @@ def _encoding_implicit_uses_impl( 'static_cast(inst_.saddr), 2});' '}' ) + # SDWA dst_unused:PRESERVE keeps the untouched bytes of the old + # destination, so the instruction reads vdst. DPP with partial + # row or bank mask keeps untouched lanes. Surfacing here so + # liveness and def/use dataflow see the dependency. + if ( + inst_enc.enc_name.upper() in ('ENC_VOP1', 'ENC_VOP2') + and 'vdst' in enc_field_names + ): + return ( + 'bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && ' + 'sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE;' + 'bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && ' + '(dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); ' + 'if (sdwa_preserve || dpp_partial) ' + 'uses.expand(RegisterRef{RegClass::VGPR, ' + 'static_cast(inst_.vdst), 1});' + ) return '' def _enc_field_at_bit(self, enc_name: str, bit_offset: int) -> str | None: diff --git a/emulation/rocjitsu/tests/analysis/liveness_test.cpp b/emulation/rocjitsu/tests/analysis/liveness_test.cpp index 2fd367746913..bbbc039ce1f0 100644 --- a/emulation/rocjitsu/tests/analysis/liveness_test.cpp +++ b/emulation/rocjitsu/tests/analysis/liveness_test.cpp @@ -5,6 +5,7 @@ #include "rocjitsu/analysis/liveness.h" #include "rocjitsu/code/basic_block.h" #include "rocjitsu/code/code_object.h" +#include "rocjitsu/code/rj_code.h" #include "rocjitsu/isa/arch/amdgpu/cdna4/operand.h" #include "rocjitsu/isa/decoder.h" #include "rocjitsu/isa/instruction.h" @@ -519,5 +520,63 @@ TEST(InstDefUse, Predicated) { EXPECT_TRUE(idu.has_predicated_def); } +// --- Generated VOP1 SDWA/DPP destination-preserve reads (real decode) --- +// +// SDWA dst_unused:PRESERVE and a partial DPP row/bank mask both keep the old +// vdst value, so the decoded instruction must report vdst as an implicit use. +// InstDefUse is the per-instruction def/use set LivenessAnalysis consumes (it +// calls Instruction::implicit_uses), so a use surfacing here is exactly what +// reaches liveness -- see ImplicitUseIsLiveBeforeInstruction for that step. +// +// CDNA4 VOP1 word0: encoding[31:25]=0x3F, vdst[24:17], op[16:9]=1 (v_mov_b32), +// src0[8:0]=marker (250=SRC_DPP, 249=SRC_SDWA). +constexpr uint32_t kVop1MovWord0Dpp = (0x3Fu << 25) | (5u << 17) | (1u << 9) | 250u; +constexpr uint32_t kVop1MovWord0Sdwa = (0x3Fu << 25) | (5u << 17) | (1u << 9) | 249u; + +std::unique_ptr decode_cdna4(const std::array &words) { + auto decoder = Decoder::create(ROCJITSU_CODE_ARCH_CDNA4); + return std::unique_ptr(decoder ? decoder->decode(words.data()) : nullptr); +} + +TEST(GeneratedInstDefUse, DppPartialRowMaskReadsDestination) { + // DPP word1: row_mask[31:28]=0x7 (partial), bank_mask[27:24]=0xF, vsrc0[7:0]=2. + auto inst = decode_cdna4({kVop1MovWord0Dpp, (0x7u << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + ASSERT_EQ(std::string_view(inst->mnemonic()).substr(0, 9), "v_mov_b32"); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, DppFullRowMaskDoesNotReadDestination) { + // DPP word1: row_mask=0xF and bank_mask=0xF (full) -> every lane written. + auto inst = decode_cdna4({kVop1MovWord0Dpp, (0xFu << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, SdwaPreserveReadsDestination) { + // SDWA word1: vsrc0[7:0]=2, dst_sel[10:8]=0 (BYTE_0, != DWORD), + // dst_unused[12:11]=2 (UNUSED_PRESERVE). + auto inst = decode_cdna4({kVop1MovWord0Sdwa, (2u << 11) | (0u << 8) | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, SdwaPadDoesNotReadDestination) { + // SDWA word1: dst_sel[10:8]=0, dst_unused[12:11]=0 (UNUSED_PAD) -> no read. + auto inst = decode_cdna4({kVop1MovWord0Sdwa, (0u << 11) | (0u << 8) | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + } // namespace } // namespace rocjitsu From 5e897be726fed50874caf4a4b333864970523c13 Mon Sep 17 00:00:00 2001 From: Allyson Cauble-Chantrenne Date: Fri, 26 Jun 2026 07:16:30 -0500 Subject: [PATCH 02/10] [rocjitsu] regen isa --- .../isa/arch/amdgpu/cdna1/encodings.cpp | 18 ++++++++++++++++++ .../rocjitsu/isa/arch/amdgpu/cdna1/encodings.h | 2 ++ .../isa/arch/amdgpu/cdna2/encodings.cpp | 18 ++++++++++++++++++ .../rocjitsu/isa/arch/amdgpu/cdna2/encodings.h | 2 ++ .../isa/arch/amdgpu/cdna3/encodings.cpp | 18 ++++++++++++++++++ .../rocjitsu/isa/arch/amdgpu/cdna3/encodings.h | 2 ++ .../isa/arch/amdgpu/cdna4/encodings.cpp | 18 ++++++++++++++++++ .../rocjitsu/isa/arch/amdgpu/cdna4/encodings.h | 2 ++ .../isa/arch/amdgpu/gfx1250/encodings.cpp | 18 ++++++++++++++++++ .../isa/arch/amdgpu/gfx1250/encodings.h | 2 ++ .../isa/arch/amdgpu/rdna1/encodings.cpp | 18 ++++++++++++++++++ .../rocjitsu/isa/arch/amdgpu/rdna1/encodings.h | 2 ++ .../isa/arch/amdgpu/rdna2/encodings.cpp | 18 ++++++++++++++++++ .../rocjitsu/isa/arch/amdgpu/rdna2/encodings.h | 2 ++ .../isa/arch/amdgpu/rdna3/encodings.cpp | 18 ++++++++++++++++++ .../rocjitsu/isa/arch/amdgpu/rdna3/encodings.h | 2 ++ .../isa/arch/amdgpu/rdna3_5/encodings.cpp | 18 ++++++++++++++++++ .../isa/arch/amdgpu/rdna3_5/encodings.h | 2 ++ .../isa/arch/amdgpu/rdna4/encodings.cpp | 18 ++++++++++++++++++ .../rocjitsu/isa/arch/amdgpu/rdna4/encodings.h | 2 ++ 20 files changed, 200 insertions(+) diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp index 4491989d12e5..1e07d89e0edf 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp @@ -128,6 +128,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 255 && inst_.src0 != 249; } @@ -168,6 +177,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 255 && inst_.src0 != 249; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.h index 8f7363bc3afd..30518b7dbfda 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.h @@ -77,6 +77,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_dpp(); @@ -131,6 +132,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_dpp(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp index 986f4f852939..d04af9414605 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp @@ -128,6 +128,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 255 && inst_.src0 != 249; } @@ -168,6 +177,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 255 && inst_.src0 != 249; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.h index 977737ad6193..afd2c9ebdd2f 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.h @@ -77,6 +77,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_dpp(); @@ -131,6 +132,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_dpp(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp index 31799f6746b9..93baac5f63e1 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp @@ -128,6 +128,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 255 && inst_.src0 != 249; } @@ -168,6 +177,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 255 && inst_.src0 != 249; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.h index 06d2f0d6fc64..d75732959a6b 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.h @@ -77,6 +77,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_dpp(); @@ -131,6 +132,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_dpp(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp index 00e22edf1a44..7bf33e8620e2 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp @@ -122,6 +122,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 255 && inst_.src0 != 249; } @@ -162,6 +171,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 255 && inst_.src0 != 249; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.h index 6de71e52fb75..7f565cabacc0 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.h @@ -75,6 +75,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_dpp(); @@ -129,6 +130,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_dpp(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp index 17d9e18c6392..c65e1caee8a5 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp @@ -171,6 +171,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return (inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255 && inst_.src0 != 254); @@ -223,6 +232,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return (inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255 && inst_.src0 != 254); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h index 18b6ca970dc0..82b4fc7d8db3 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h @@ -86,6 +86,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_lit64(); @@ -145,6 +146,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool has_lit64(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp index 6ad0a9621bfc..e3cb042d1a44 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp @@ -126,6 +126,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255 && inst_.src0 != 249; @@ -162,6 +171,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255 && inst_.src0 != 249; diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.h index 345ceee39cdd..7992ac59e93f 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.h @@ -77,6 +77,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); using OpEncoding = Vop1MachineInst; @@ -129,6 +130,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool hasImpliedLiteral(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp index cb2a3e44e931..0f6a93cab4fe 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp @@ -126,6 +126,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255 && inst_.src0 != 249; @@ -162,6 +171,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255 && inst_.src0 != 249; diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.h index 464cb18612f1..6f6ecebfcb20 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.h @@ -77,6 +77,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); using OpEncoding = Vop1MachineInst; @@ -129,6 +130,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool hasImpliedLiteral(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp index 9c35ec6b55d2..aca0d1263161 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp @@ -126,6 +126,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255; } @@ -160,6 +169,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h index f960e14f831d..9cb14bd8e739 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h @@ -77,6 +77,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); using OpEncoding = Vop1MachineInst; @@ -130,6 +131,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool hasImpliedLiteral(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp index 3a62ed884ef1..ea347b07017a 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp @@ -130,6 +130,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255; } @@ -164,6 +173,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h index 2b38743efaea..47c7b70299f3 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h @@ -79,6 +79,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); using OpEncoding = Vop1MachineInst; @@ -132,6 +133,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool hasImpliedLiteral(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp index acd8322f5a3b..3f309c740f17 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp @@ -117,6 +117,15 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop1::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop1::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255; } @@ -151,6 +160,15 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe literal_ = reinterpret_cast(inst)[1]; } +void Vop2::implicit_uses(RegisterSet &uses) const { + bool sdwa_preserve = + sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; + bool dpp_partial = + inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + if (sdwa_preserve || dpp_partial) + uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); +} + bool Vop2::default_encoding() { return inst_.src0 != 250 && inst_.src0 != 233 && inst_.src0 != 234 && inst_.src0 != 255; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h index a29e39279888..24c34953d2a1 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h @@ -79,6 +79,7 @@ class Smem : public IsaInstruction { class Vop1 : public IsaInstruction { public: Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); using OpEncoding = Vop1MachineInst; @@ -132,6 +133,7 @@ class Vopc : public IsaInstruction { class Vop2 : public IsaInstruction { public: Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool default_encoding(); bool has_lit(); bool hasImpliedLiteral(); From 87faec0640b176d484f5d74b72d3a9607c5320b5 Mon Sep 17 00:00:00 2001 From: Soumil Kushwaha Date: Wed, 1 Jul 2026 16:36:50 -0500 Subject: [PATCH 03/10] pre-commit fix --- emulation/rocjitsu/tests/analysis/liveness_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emulation/rocjitsu/tests/analysis/liveness_test.cpp b/emulation/rocjitsu/tests/analysis/liveness_test.cpp index 0ba6d36526c9..4f4fbf85660f 100644 --- a/emulation/rocjitsu/tests/analysis/liveness_test.cpp +++ b/emulation/rocjitsu/tests/analysis/liveness_test.cpp @@ -5,8 +5,8 @@ #include "rocjitsu/analysis/liveness.h" #include "rocjitsu/code/basic_block.h" #include "rocjitsu/code/code_object.h" -#include "rocjitsu/code/rj_code.h" #include "rocjitsu/code/patch/instruction_builder.h" +#include "rocjitsu/code/rj_code.h" #include "rocjitsu/isa/arch/amdgpu/cdna4/operand.h" #include "rocjitsu/isa/decoder.h" #include "rocjitsu/isa/instruction.h" From 40717a1d84608a402a677081ae541fc6d2042a95 Mon Sep 17 00:00:00 2001 From: Soumil Kushwaha Date: Wed, 1 Jul 2026 16:47:56 -0500 Subject: [PATCH 04/10] added vop2 tests --- .../rocjitsu/tests/analysis/liveness_test.cpp | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/emulation/rocjitsu/tests/analysis/liveness_test.cpp b/emulation/rocjitsu/tests/analysis/liveness_test.cpp index 4f4fbf85660f..0c2a852e3054 100644 --- a/emulation/rocjitsu/tests/analysis/liveness_test.cpp +++ b/emulation/rocjitsu/tests/analysis/liveness_test.cpp @@ -808,5 +808,59 @@ TEST(GeneratedInstDefUse, SdwaPadDoesNotReadDestination) { EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); } +// --- Generated VOP2 SDWA/DPP destination-preserve reads (real decode) --- +// +// VOP2 shares VOP1's destination-preserve rules: SDWA dst_unused:PRESERVE and a +// partial DPP row/bank mask both keep the old vdst value, so the decoded +// instruction must report vdst as an implicit use (see Vop2::implicit_uses, +// which mirrors Vop1::implicit_uses). These cases mimic the VOP1 tests above but +// exercise the VOP2 encoding path. +// +// CDNA4 VOP2 word0: encoding[31]=0, op[30:25]=1 (v_add_f32), vdst[24:17], +// vsrc1[16:9], src0[8:0]=marker (250=SRC_DPP, 249=SRC_SDWA). The DPP/SDWA word1 +// layouts are identical to VOP1, so the second-word bit fields are reused. +constexpr uint32_t kVop2AddWord0Dpp = (0u << 31) | (1u << 25) | (5u << 17) | (3u << 9) | 250u; +constexpr uint32_t kVop2AddWord0Sdwa = (0u << 31) | (1u << 25) | (5u << 17) | (3u << 9) | 249u; + +TEST(GeneratedInstDefUse, Vop2DppPartialRowMaskReadsDestination) { + // DPP word1: row_mask[31:28]=0x7 (partial), bank_mask[27:24]=0xF, vsrc0[7:0]=2. + auto inst = decode_cdna4({kVop2AddWord0Dpp, (0x7u << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + ASSERT_EQ(std::string_view(inst->mnemonic()).substr(0, 9), "v_add_f32"); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, Vop2DppFullRowMaskDoesNotReadDestination) { + // DPP word1: row_mask=0xF and bank_mask=0xF (full) -> every lane written. + auto inst = decode_cdna4({kVop2AddWord0Dpp, (0xFu << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, Vop2SdwaPreserveReadsDestination) { + // SDWA word1: vsrc0[7:0]=2, dst_sel[10:8]=0 (BYTE_0, != DWORD), + // dst_unused[12:11]=2 (UNUSED_PRESERVE). + auto inst = decode_cdna4({kVop2AddWord0Sdwa, (2u << 11) | (0u << 8) | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, Vop2SdwaPadDoesNotReadDestination) { + // SDWA word1: dst_sel[10:8]=0, dst_unused[12:11]=0 (UNUSED_PAD) -> no read. + auto inst = decode_cdna4({kVop2AddWord0Sdwa, (0u << 11) | (0u << 8) | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + } // namespace } // namespace rocjitsu From 98505bcb881ac08ca7489be0cf2311f4da4c93c3 Mon Sep 17 00:00:00 2001 From: Soumil Kushwaha Date: Tue, 7 Jul 2026 17:53:57 -0500 Subject: [PATCH 05/10] added check for dpp shift out of bounds where bound_ctrl == 0 which causes oob writes to retain the prior vdst values (partial write) --- .../lib/python/amdisa/codegen/_generator.py | 17 +++-- .../isa/arch/amdgpu/cdna1/encodings.cpp | 10 ++- .../isa/arch/amdgpu/cdna2/encodings.cpp | 10 ++- .../isa/arch/amdgpu/cdna3/encodings.cpp | 10 ++- .../isa/arch/amdgpu/cdna4/encodings.cpp | 10 ++- .../isa/arch/amdgpu/gfx1250/encodings.cpp | 10 ++- .../isa/arch/amdgpu/rdna1/encodings.cpp | 10 ++- .../isa/arch/amdgpu/rdna2/encodings.cpp | 10 ++- .../isa/arch/amdgpu/rdna3/encodings.cpp | 10 ++- .../isa/arch/amdgpu/rdna3_5/encodings.cpp | 10 ++- .../isa/arch/amdgpu/rdna4/encodings.cpp | 10 ++- .../isa/arch/amdgpu/shared/dpp_sdwa_ops.h | 18 +++++ .../rocjitsu/tests/analysis/liveness_test.cpp | 74 ++++++++++++++++++- 13 files changed, 162 insertions(+), 47 deletions(-) diff --git a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py index e95d3e1df005..2a99d19222ef 100644 --- a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py +++ b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py @@ -1697,10 +1697,15 @@ def _encoding_implicit_uses_impl( 'static_cast(inst_.saddr), 2});' '}' ) - # SDWA dst_unused:PRESERVE keeps the untouched bytes of the old - # destination, so the instruction reads vdst. DPP with partial - # row or bank mask keeps untouched lanes. Surfacing here so - # liveness and def/use dataflow see the dependency. + # SDWA dst_unused:PRESERVE and DPP that keeps lanes (partial + # row/bank mask, or bound_ctrl == 0 with an edge-crossing dpp_ctrl + # that leaves OOB lanes unwritten) read the old vdst, so surface it + # as a use for liveness/def-use. + # + # Only VOP1/VOP2 need this. VOPC/VOPCX apply the same partial-mask + # preservation to VCC/EXEC, but liveness tracks only SGPR/VGPR/ACC_VGPR + # (see liveness.h) -- VCC/EXEC/SCC are outside the dataflow model, so + # there is no tracked register to surface for those encodings. if ( inst_enc.enc_name.upper() in ('ENC_VOP1', 'ENC_VOP2') and 'vdst' in enc_field_names @@ -1709,7 +1714,9 @@ def _encoding_implicit_uses_impl( 'bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && ' 'sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE;' 'bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && ' - '(dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); ' + '(dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || ' + '(dpp_bound_ctrl_ == 0 && ' + 'amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); ' 'if (sdwa_preserve || dpp_partial) ' 'uses.expand(RegisterRef{RegClass::VGPR, ' 'static_cast(inst_.vdst), 1});' diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp index 1e07d89e0edf..99ba1d94bfef 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp @@ -131,8 +131,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -180,8 +181,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp index d04af9414605..a1320121b22c 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp @@ -131,8 +131,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -180,8 +181,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp index 93baac5f63e1..35ac33ecafdc 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp @@ -131,8 +131,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -180,8 +181,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp index 7bf33e8620e2..655b033d97a0 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp @@ -125,8 +125,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -174,8 +175,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp index c65e1caee8a5..9668d09856ac 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp @@ -174,8 +174,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -235,8 +236,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp index e3cb042d1a44..d7efca629760 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp @@ -129,8 +129,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -174,8 +175,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp index 0f6a93cab4fe..076c71d2f2a8 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp @@ -129,8 +129,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -174,8 +175,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp index aca0d1263161..da1658d226d3 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp @@ -129,8 +129,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -172,8 +173,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp index ea347b07017a..d26fe827e1e1 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp @@ -133,8 +133,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -176,8 +177,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp index 3f309c740f17..4b6b4fefcfda 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp @@ -120,8 +120,9 @@ Vop1::Vop1(std::string_view mnemonic, const Vop1MachineInst *inst, ExecuteFn exe void Vop1::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } @@ -163,8 +164,9 @@ Vop2::Vop2(std::string_view mnemonic, const Vop2MachineInst *inst, ExecuteFn exe void Vop2::implicit_uses(RegisterSet &uses) const { bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; - bool dpp_partial = - inst_.src0 == amdgpu::SRC_DPP && (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF); + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/shared/dpp_sdwa_ops.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/shared/dpp_sdwa_ops.h index 187cc6152e19..20e334d60a27 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/shared/dpp_sdwa_ops.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/shared/dpp_sdwa_ops.h @@ -198,6 +198,24 @@ inline int dpp_permute(uint32_t dpp_ctrl, int lane, int wf_size, bool &out_of_bo return lane; } +/// @brief Return true if a dpp_ctrl can read across a row/wavefront edge. +/// +/// Such controls produce an out-of-bounds source lane, so with BOUND_CTRL=0 +/// they leave some destination lanes unwritten (old value preserved) -- the +/// shifts, wavefront shifts, and row broadcasts. Rotates, mirrors, quad_perm, +/// row_share and row_xmask always map to valid lanes. Derived from dpp_permute +/// over a full 64-lane wavefront so it stays consistent as controls are added; +/// this is an analysis-time query, not on the execution hot path. +inline bool dpp_ctrl_produces_oob(uint32_t dpp_ctrl) { + for (int lane = 0; lane < 64; ++lane) { + bool oob = false; + (void)dpp_permute(dpp_ctrl, lane, /*wf_size=*/64, oob); + if (oob) + return true; + } + return false; +} + /// @brief Check if a lane is disabled by DPP row/bank masks. /// /// @param lane Lane index. diff --git a/emulation/rocjitsu/tests/analysis/liveness_test.cpp b/emulation/rocjitsu/tests/analysis/liveness_test.cpp index 0c2a852e3054..09879865b83b 100644 --- a/emulation/rocjitsu/tests/analysis/liveness_test.cpp +++ b/emulation/rocjitsu/tests/analysis/liveness_test.cpp @@ -768,6 +768,17 @@ std::unique_ptr decode_cdna4(const std::array &words) return std::unique_ptr(decoder ? decoder->decode(words.data()) : nullptr); } +// DPP word1 fields (CDNA4): vsrc0[7:0], dpp_ctrl[16:8], bound_ctrl[19], +// bank_mask[27:24], row_mask[31:28]. With full masks, whether vdst is +// preserved depends on bound_ctrl and whether dpp_ctrl crosses a row/wave +// edge: bound_ctrl=0 + an edge-crossing ctrl leaves OOB lanes unwritten (reads +// vdst); bound_ctrl=1 writes a zero source instead (full write); a ctrl that +// never goes OOB is a full write regardless of bound_ctrl. +constexpr uint32_t kDppFullMasks = (0xFu << 28) | (0xFu << 24); +constexpr uint32_t kDppBoundCtrl = (1u << 19); +constexpr uint32_t kDppCtrlRowShr1 = 0x111u << 8; // row_shr:1 -- crosses the row edge +constexpr uint32_t kDppCtrlRowRor1 = 0x121u << 8; // row_ror:1 -- rotates within the row + TEST(GeneratedInstDefUse, DppPartialRowMaskReadsDestination) { // DPP word1: row_mask[31:28]=0x7 (partial), bank_mask[27:24]=0xF, vsrc0[7:0]=2. auto inst = decode_cdna4({kVop1MovWord0Dpp, (0x7u << 28) | (0xFu << 24) | 2u}); @@ -780,7 +791,8 @@ TEST(GeneratedInstDefUse, DppPartialRowMaskReadsDestination) { } TEST(GeneratedInstDefUse, DppFullRowMaskDoesNotReadDestination) { - // DPP word1: row_mask=0xF and bank_mask=0xF (full) -> every lane written. + // DPP word1: row_mask=0xF, bank_mask=0xF (full), dpp_ctrl=0 (quad_perm, never + // OOB) -> every lane written, no vdst read even with bound_ctrl=0. auto inst = decode_cdna4({kVop1MovWord0Dpp, (0xFu << 28) | (0xFu << 24) | 2u}); ASSERT_NE(inst, nullptr); @@ -808,6 +820,40 @@ TEST(GeneratedInstDefUse, SdwaPadDoesNotReadDestination) { EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); } +TEST(GeneratedInstDefUse, DppBoundCtrlZeroEdgeCrossingReadsDestination) { + // Full masks, bound_ctrl=0, row_shr:1 -> row-edge lanes read OOB and are left + // unwritten, preserving vdst. + auto inst = decode_cdna4({kVop1MovWord0Dpp, kDppFullMasks | kDppCtrlRowShr1 | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, DppBoundCtrlOneEdgeCrossingDoesNotReadDestination) { + // Full masks, row_shr:1 but bound_ctrl=1 -> OOB lanes read a zero source and + // are still written, so every lane is defined and vdst is not read. + auto inst = + decode_cdna4({kVop1MovWord0Dpp, kDppFullMasks | kDppBoundCtrl | kDppCtrlRowShr1 | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, DppBoundCtrlZeroRotateDoesNotReadDestination) { + // Full masks, bound_ctrl=0, row_ror:1 -> a rotate never goes OOB, so every + // lane is written and vdst is not read despite bound_ctrl=0. + auto inst = decode_cdna4({kVop1MovWord0Dpp, kDppFullMasks | kDppCtrlRowRor1 | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + // --- Generated VOP2 SDWA/DPP destination-preserve reads (real decode) --- // // VOP2 shares VOP1's destination-preserve rules: SDWA dst_unused:PRESERVE and a @@ -834,7 +880,8 @@ TEST(GeneratedInstDefUse, Vop2DppPartialRowMaskReadsDestination) { } TEST(GeneratedInstDefUse, Vop2DppFullRowMaskDoesNotReadDestination) { - // DPP word1: row_mask=0xF and bank_mask=0xF (full) -> every lane written. + // DPP word1: row_mask=0xF, bank_mask=0xF (full), dpp_ctrl=0 (quad_perm, never + // OOB) -> every lane written, no vdst read even with bound_ctrl=0. auto inst = decode_cdna4({kVop2AddWord0Dpp, (0xFu << 28) | (0xFu << 24) | 2u}); ASSERT_NE(inst, nullptr); @@ -862,5 +909,28 @@ TEST(GeneratedInstDefUse, Vop2SdwaPadDoesNotReadDestination) { EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); } +TEST(GeneratedInstDefUse, Vop2DppBoundCtrlZeroEdgeCrossingReadsDestination) { + // Full masks, bound_ctrl=0, row_shr:1 -> row-edge lanes read OOB and are left + // unwritten, preserving vdst (mirrors the VOP1 case on the VOP2 path). + auto inst = decode_cdna4({kVop2AddWord0Dpp, kDppFullMasks | kDppCtrlRowShr1 | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, Vop2DppBoundCtrlOneEdgeCrossingDoesNotReadDestination) { + // Full masks, row_shr:1 but bound_ctrl=1 -> OOB lanes read zero and are still + // written, so every lane is defined and vdst is not read. + auto inst = + decode_cdna4({kVop2AddWord0Dpp, kDppFullMasks | kDppBoundCtrl | kDppCtrlRowShr1 | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + } // namespace } // namespace rocjitsu From a47f0adcfee6891296b86397954f3517aab90cde Mon Sep 17 00:00:00 2001 From: Soumil Kushwaha Date: Thu, 9 Jul 2026 15:52:39 -0500 Subject: [PATCH 06/10] added VOP3 DPP coverage --- .../lib/python/amdisa/codegen/_generator.py | 32 ++++++-- .../isa/arch/amdgpu/gfx1250/encodings.cpp | 10 +++ .../isa/arch/amdgpu/gfx1250/encodings.h | 1 + .../isa/arch/amdgpu/rdna3/encodings.cpp | 10 +++ .../isa/arch/amdgpu/rdna3/encodings.h | 1 + .../isa/arch/amdgpu/rdna3_5/encodings.cpp | 10 +++ .../isa/arch/amdgpu/rdna3_5/encodings.h | 1 + .../isa/arch/amdgpu/rdna4/encodings.cpp | 10 +++ .../isa/arch/amdgpu/rdna4/encodings.h | 1 + .../rocjitsu/tests/analysis/liveness_test.cpp | 75 +++++++++++++++++++ 10 files changed, 144 insertions(+), 7 deletions(-) diff --git a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py index 2a99d19222ef..e0d64d103c50 100644 --- a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py +++ b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py @@ -1698,14 +1698,12 @@ def _encoding_implicit_uses_impl( '}' ) # SDWA dst_unused:PRESERVE and DPP that keeps lanes (partial - # row/bank mask, or bound_ctrl == 0 with an edge-crossing dpp_ctrl - # that leaves OOB lanes unwritten) read the old vdst, so surface it - # as a use for liveness/def-use. + # row/bank mask, or bound_ctrl == 0 with an edge-crossing dpp_ctrl) + # leave the old destination in place, so surface it as a use. + # Relevant only for VOP1, VOP2, and VOP3 encodings. # - # Only VOP1/VOP2 need this. VOPC/VOPCX apply the same partial-mask - # preservation to VCC/EXEC, but liveness tracks only SGPR/VGPR/ACC_VGPR - # (see liveness.h) -- VCC/EXEC/SCC are outside the dataflow model, so - # there is no tracked register to surface for those encodings. + # VOP1/VOP2: the modifier forms are always 32-bit VGPR-dest, so emit a + # fixed VGPR ref. (SGPR-dest ops like v_readfirstlane have no SDWA/DPP.) if ( inst_enc.enc_name.upper() in ('ENC_VOP1', 'ENC_VOP2') and 'vdst' in enc_field_names @@ -1721,6 +1719,26 @@ def _encoding_implicit_uses_impl( 'uses.expand(RegisterRef{RegClass::VGPR, ' 'static_cast(inst_.vdst), 1});' ) + # VOP3 gained DPP on gfx11+ (no SDWA). Its vdst can name an SGPR + # (re-encoded compares), so derive the ref from the decoded dst operand + # for the real class/width. Gated on VOP3-DPP support (GCN/CDNA lack the + # dpp_* fields). VOPC/VOPCX stay omitted: they preserve VCC/EXEC, which + # liveness does not track (see liveness.h). + if ( + inst_enc.enc_name.upper() == 'ENC_VOP3' + and 'vdst' in enc_field_names + and self._supports_vop_dpp_encoding('ENC_VOP3') + ): + return ( + 'bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && ' + '(dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || ' + '(dpp_bound_ctrl_ == 0 && ' + 'amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); ' + 'if (dpp_partial) ' + 'if (const auto *dst = dst_operand(0)) ' + 'if (auto ref = dst->to_register_ref()) ' + 'uses.expand(*ref);' + ) return '' def _enc_field_at_bit(self, enc_name: str, bit_offset: int) -> str | None: diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp index 9668d09856ac..4aacac113144 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp @@ -273,6 +273,16 @@ Vop3::Vop3(std::string_view mnemonic, const Vop3MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop3::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3::has_lit_0() { return (((inst_.src0 == 255) && (inst_.src1 != 255)) && (inst_.src2 != 255)); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h index dc91b967af77..b3c340711bb5 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h @@ -182,6 +182,7 @@ class Vop2 : public IsaInstruction { class Vop3 : public IsaInstruction { public: Vop3(std::string_view mnemonic, const Vop3MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_and_has_lit_1(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp index da1658d226d3..5d3eef401636 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp @@ -201,6 +201,16 @@ Vop3::Vop3(std::string_view mnemonic, const Vop3MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop3::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } bool Vop3::has_lit_1() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h index 66460c84e174..9f3a6c046a09 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h @@ -164,6 +164,7 @@ class Vop2 : public IsaInstruction { class Vop3 : public IsaInstruction { public: Vop3(std::string_view mnemonic, const Vop3MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_has_lit_1(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp index d26fe827e1e1..f116c046b644 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp @@ -205,6 +205,16 @@ Vop3::Vop3(std::string_view mnemonic, const Vop3MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop3::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } bool Vop3::has_lit_1() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h index 0dc111039dbb..7779883e4a74 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h @@ -166,6 +166,7 @@ class Vop2 : public IsaInstruction { class Vop3 : public IsaInstruction { public: Vop3(std::string_view mnemonic, const Vop3MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_has_lit_1(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp index 4b6b4fefcfda..47ee3b6fce50 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp @@ -192,6 +192,16 @@ Vop3::Vop3(std::string_view mnemonic, const Vop3MachineInst *inst, ExecuteFn exe size_ += sizeof(MachineInst); } +void Vop3::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } bool Vop3::has_lit_1() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h index 7f30f93ae438..43044e0e4dab 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h @@ -166,6 +166,7 @@ class Vop2 : public IsaInstruction { class Vop3 : public IsaInstruction { public: Vop3(std::string_view mnemonic, const Vop3MachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_has_lit_1(); diff --git a/emulation/rocjitsu/tests/analysis/liveness_test.cpp b/emulation/rocjitsu/tests/analysis/liveness_test.cpp index 09879865b83b..b5c1149ecf37 100644 --- a/emulation/rocjitsu/tests/analysis/liveness_test.cpp +++ b/emulation/rocjitsu/tests/analysis/liveness_test.cpp @@ -932,5 +932,80 @@ TEST(GeneratedInstDefUse, Vop2DppBoundCtrlOneEdgeCrossingDoesNotReadDestination) EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); } +// --- Generated VOP3 DPP destination-preserve reads (real decode) --- +// +// VOP3 gained DPP on gfx11+ (RDNA3/RDNA4/gfx1250) and has no SDWA, so only the +// partial-DPP path applies. Unlike VOP1/VOP2 the VOP3 vdst field can name an +// SGPR: a VOP3-re-encoded compare (v_cmp_*_e64) writes its lane mask to an SGPR +// through vdst. So Vop3::implicit_uses derives the preserved ref from the +// decoded destination operand rather than assuming VGPR -- these cases exercise +// both a VGPR-dest op and an SGPR-dest compare. VOP3 is not in CDNA, so these +// decode for RDNA4. +// +// RDNA4 VOP3 word0: encoding[31:26]=53, op[25:16], clamp[15], opsel[14:11], +// abs[10:8], vdst[7:0]. word1: src0[8:0]=marker (250=SRC_DPP), src1[17:9]. The +// DPP16 word2 layout matches VOP1/VOP2, so its bit fields are reused. +constexpr uint32_t kVop3Enc = 53u << 26; +constexpr uint32_t kVop3AddF32Op = 259u << 16; // v_add_f32_e64 (VGPR vdst) +constexpr uint32_t kVop3CmpLtF32Op = 17u << 16; // v_cmp_lt_f32_e64 (SGPR vdst) +// word1: src0=SRC_DPP, src1=VGPR3. +constexpr uint32_t kVop3DppWord1 = (3u << 9) | 250u; + +std::unique_ptr decode_rdna4(const std::array &words) { + auto decoder = Decoder::create(ROCJITSU_CODE_ARCH_RDNA4); + return std::unique_ptr(decoder ? decoder->decode(words.data()) : nullptr); +} + +TEST(GeneratedInstDefUse, Vop3DppPartialRowMaskReadsVgprDestination) { + // v_add_f32_e64 (VGPR vdst=5), DPP word2: row_mask=0x7 (partial), bank_mask=0xF. + auto inst = decode_rdna4( + {kVop3Enc | kVop3AddF32Op | 5u, kVop3DppWord1, (0x7u << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + ASSERT_EQ(std::string_view(inst->mnemonic()).substr(0, 9), "v_add_f32"); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, Vop3DppFullRowMaskDoesNotReadDestination) { + // Full masks, dpp_ctrl=0 (quad_perm, never OOB) -> every lane written. + auto inst = decode_rdna4( + {kVop3Enc | kVop3AddF32Op | 5u, kVop3DppWord1, (0xFu << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, Vop3DppBoundCtrlZeroEdgeCrossingReadsDestination) { + // Full masks, bound_ctrl=0, row_shr:1 -> row-edge lanes read OOB and are left + // unwritten, preserving the VGPR vdst. + auto inst = decode_rdna4( + {kVop3Enc | kVop3AddF32Op | 5u, kVop3DppWord1, kDppFullMasks | kDppCtrlRowShr1 | 2u}); + ASSERT_NE(inst, nullptr); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 5, 1})); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 5, 1})); +} + +TEST(GeneratedInstDefUse, Vop3CmpDppPartialRowMaskReadsSgprDestinationNotVgpr) { + // v_cmp_lt_f32_e64 writes its lane mask to an SGPR pair through the vdst field + // (here s[8:9]), so a partial DPP mask preserves the SGPR -- not a VGPR. This + // is why Vop3::implicit_uses derives the ref from the dst operand instead of + // hardcoding RegClass::VGPR like VOP1/VOP2. + auto inst = decode_rdna4( + {kVop3Enc | kVop3CmpLtF32Op | 8u, kVop3DppWord1, (0x7u << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + ASSERT_EQ(std::string_view(inst->mnemonic()).substr(0, 9), "v_cmp_lt_"); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::SGPR, 8, 2})); + EXPECT_TRUE(idu.uses.contains({RegClass::SGPR, 8, 2})); + EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 8, 1})); +} + } // namespace } // namespace rocjitsu From 33a0b6f024f45bac9ecdb2757bcc53174d8dc04d Mon Sep 17 00:00:00 2001 From: Soumil Kushwaha Date: Thu, 9 Jul 2026 15:55:30 -0500 Subject: [PATCH 07/10] pre-commit fix --- emulation/rocjitsu/tests/analysis/liveness_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/emulation/rocjitsu/tests/analysis/liveness_test.cpp b/emulation/rocjitsu/tests/analysis/liveness_test.cpp index b5c1149ecf37..28eec61c6882 100644 --- a/emulation/rocjitsu/tests/analysis/liveness_test.cpp +++ b/emulation/rocjitsu/tests/analysis/liveness_test.cpp @@ -946,7 +946,7 @@ TEST(GeneratedInstDefUse, Vop2DppBoundCtrlOneEdgeCrossingDoesNotReadDestination) // abs[10:8], vdst[7:0]. word1: src0[8:0]=marker (250=SRC_DPP), src1[17:9]. The // DPP16 word2 layout matches VOP1/VOP2, so its bit fields are reused. constexpr uint32_t kVop3Enc = 53u << 26; -constexpr uint32_t kVop3AddF32Op = 259u << 16; // v_add_f32_e64 (VGPR vdst) +constexpr uint32_t kVop3AddF32Op = 259u << 16; // v_add_f32_e64 (VGPR vdst) constexpr uint32_t kVop3CmpLtF32Op = 17u << 16; // v_cmp_lt_f32_e64 (SGPR vdst) // word1: src0=SRC_DPP, src1=VGPR3. constexpr uint32_t kVop3DppWord1 = (3u << 9) | 250u; From 915a58f035b8a33f60999c835bba10856cdaae55 Mon Sep 17 00:00:00 2001 From: Soumil Kushwaha Date: Fri, 10 Jul 2026 11:39:43 -0500 Subject: [PATCH 08/10] derive VOP2/VOP2 destination ref from to_register_ref so width = 1 isn't hardcoded, added test case that tests width = 2 instruction --- .../lib/python/amdisa/codegen/_generator.py | 5 +++-- .../rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp | 8 ++++++-- .../rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp | 8 ++++++-- .../rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp | 8 ++++++-- .../rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp | 8 ++++++-- .../isa/arch/amdgpu/gfx1250/encodings.cpp | 8 ++++++-- .../rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp | 8 ++++++-- .../rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp | 8 ++++++-- .../rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp | 8 ++++++-- .../isa/arch/amdgpu/rdna3_5/encodings.cpp | 8 ++++++-- .../rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp | 8 ++++++-- .../rocjitsu/tests/analysis/liveness_test.cpp | 16 ++++++++++++++++ 12 files changed, 79 insertions(+), 22 deletions(-) diff --git a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py index e0d64d103c50..2568c0bd6fbb 100644 --- a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py +++ b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py @@ -1716,8 +1716,9 @@ def _encoding_implicit_uses_impl( '(dpp_bound_ctrl_ == 0 && ' 'amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); ' 'if (sdwa_preserve || dpp_partial) ' - 'uses.expand(RegisterRef{RegClass::VGPR, ' - 'static_cast(inst_.vdst), 1});' + 'if (const auto *dst = dst_operand(0)) ' + 'if (auto ref = dst->to_register_ref()) ' + 'uses.expand(*ref);' ) # VOP3 gained DPP on gfx11+ (no SDWA). Its vdst can name an SGPR # (re-encoded compares), so derive the ref from the decoded dst operand diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp index 99ba1d94bfef..51e1e821028c 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp @@ -135,7 +135,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -185,7 +187,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp index a1320121b22c..9da3e2e7b651 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp @@ -135,7 +135,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -185,7 +187,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp index 35ac33ecafdc..f43e4d8ee7c3 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp @@ -135,7 +135,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -185,7 +187,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp index 655b033d97a0..e690140f4319 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp @@ -129,7 +129,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -179,7 +181,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp index 4aacac113144..48462ec89d48 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp @@ -178,7 +178,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -240,7 +242,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp index d7efca629760..6e6164bb93ad 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp @@ -133,7 +133,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -179,7 +181,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp index 076c71d2f2a8..58df66eab6b3 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp @@ -133,7 +133,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -179,7 +181,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp index 5d3eef401636..193bf0b1dd17 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp @@ -133,7 +133,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -177,7 +179,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp index f116c046b644..ec662d290d46 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp @@ -137,7 +137,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -181,7 +183,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp index 47ee3b6fce50..15dc0a593b40 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp @@ -124,7 +124,9 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -168,7 +170,9 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - uses.expand(RegisterRef{RegClass::VGPR, static_cast(inst_.vdst), 1}); + if (const auto *dst = dst_operand(0)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/tests/analysis/liveness_test.cpp b/emulation/rocjitsu/tests/analysis/liveness_test.cpp index 28eec61c6882..d183637dbd90 100644 --- a/emulation/rocjitsu/tests/analysis/liveness_test.cpp +++ b/emulation/rocjitsu/tests/analysis/liveness_test.cpp @@ -854,6 +854,22 @@ TEST(GeneratedInstDefUse, DppBoundCtrlZeroRotateDoesNotReadDestination) { EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 5, 1})); } +TEST(GeneratedInstDefUse, Vop1DppPartialMaskReadsFullWidthDestination) { + // v_rcp_f64_e32 writes a VGPR pair (v[6:7]). A partial DPP row mask preserves + // the whole 64-bit destination, so the implicit use must match the width-2 + // def -- not just the low dword. + // CDNA4 VOP1 word0: encoding[31:25]=0x3F, vdst[24:17]=6, op[16:9]=37 + // (v_rcp_f64), src0[8:0]=250 (SRC_DPP). + constexpr uint32_t kVop1RcpF64Word0Dpp = (0x3Fu << 25) | (6u << 17) | (37u << 9) | 250u; + auto inst = decode_cdna4({kVop1RcpF64Word0Dpp, (0x7u << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + ASSERT_EQ(std::string_view(inst->mnemonic()).substr(0, 9), "v_rcp_f64"); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 6, 2})); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 6, 2})); +} + // --- Generated VOP2 SDWA/DPP destination-preserve reads (real decode) --- // // VOP2 shares VOP1's destination-preserve rules: SDWA dst_unused:PRESERVE and a From 70160608e0e2f19c55b52c3d8827c1d3b6913a13 Mon Sep 17 00:00:00 2001 From: Soumil Kushwaha Date: Fri, 10 Jul 2026 12:08:43 -0500 Subject: [PATCH 09/10] added VOP3P and VOP33_SDST_ENC handling --- .../lib/python/amdisa/codegen/_generator.py | 74 +++++++++---------- .../isa/arch/amdgpu/cdna1/encodings.cpp | 14 ++-- .../isa/arch/amdgpu/cdna2/encodings.cpp | 14 ++-- .../isa/arch/amdgpu/cdna3/encodings.cpp | 14 ++-- .../isa/arch/amdgpu/cdna4/encodings.cpp | 14 ++-- .../isa/arch/amdgpu/gfx1250/encodings.cpp | 43 ++++++++--- .../isa/arch/amdgpu/gfx1250/encodings.h | 2 + .../isa/arch/amdgpu/rdna1/encodings.cpp | 14 ++-- .../isa/arch/amdgpu/rdna2/encodings.cpp | 14 ++-- .../isa/arch/amdgpu/rdna3/encodings.cpp | 43 ++++++++--- .../isa/arch/amdgpu/rdna3/encodings.h | 2 + .../isa/arch/amdgpu/rdna3_5/encodings.cpp | 43 ++++++++--- .../isa/arch/amdgpu/rdna3_5/encodings.h | 2 + .../isa/arch/amdgpu/rdna4/encodings.cpp | 43 ++++++++--- .../isa/arch/amdgpu/rdna4/encodings.h | 2 + .../rocjitsu/tests/analysis/liveness_test.cpp | 35 +++++++++ 16 files changed, 263 insertions(+), 110 deletions(-) diff --git a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py index 2568c0bd6fbb..b9c09a0c7a8f 100644 --- a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py +++ b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py @@ -1697,46 +1697,44 @@ def _encoding_implicit_uses_impl( 'static_cast(inst_.saddr), 2});' '}' ) - # SDWA dst_unused:PRESERVE and DPP that keeps lanes (partial - # row/bank mask, or bound_ctrl == 0 with an edge-crossing dpp_ctrl) - # leave the old destination in place, so surface it as a use. - # Relevant only for VOP1, VOP2, and VOP3 encodings. + # SDWA dst_unused:PRESERVE and DPP that keeps lanes (partial row/bank + # mask, or bound_ctrl == 0 with an edge-crossing dpp_ctrl) leave the old + # destination in place, so surface every destination operand as a use. + # Deriving the ref from the decoded dst operand(s) yields the real class + # and width and covers multi-dst encodings: VOP3_SDST_ENC writes both a + # VGPR result and an SGPR carry, both preserved under a partial mask. # - # VOP1/VOP2: the modifier forms are always 32-bit VGPR-dest, so emit a - # fixed VGPR ref. (SGPR-dest ops like v_readfirstlane have no SDWA/DPP.) - if ( - inst_enc.enc_name.upper() in ('ENC_VOP1', 'ENC_VOP2') - and 'vdst' in enc_field_names - ): - return ( - 'bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && ' - 'sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE;' - 'bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && ' - '(dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || ' - '(dpp_bound_ctrl_ == 0 && ' - 'amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); ' - 'if (sdwa_preserve || dpp_partial) ' - 'if (const auto *dst = dst_operand(0)) ' - 'if (auto ref = dst->to_register_ref()) ' - 'uses.expand(*ref);' - ) - # VOP3 gained DPP on gfx11+ (no SDWA). Its vdst can name an SGPR - # (re-encoded compares), so derive the ref from the decoded dst operand - # for the real class/width. Gated on VOP3-DPP support (GCN/CDNA lack the - # dpp_* fields). VOPC/VOPCX stay omitted: they preserve VCC/EXEC, which - # liveness does not track (see liveness.h). - if ( - inst_enc.enc_name.upper() == 'ENC_VOP3' - and 'vdst' in enc_field_names - and self._supports_vop_dpp_encoding('ENC_VOP3') - ): + # SDWA exists only on VOP1/VOP2; DPP additionally exists on VOP3/VOP3P/ + # VOP3_SDST_ENC on gfx11+ (gated -- GCN/CDNA lack the dpp_* fields). + # VOPC/VOPCX stay omitted: their result lands in VCC/EXEC, which liveness + # does not track (see liveness.h). + enc = inst_enc.enc_name.upper() + has_sdwa = enc in ('ENC_VOP1', 'ENC_VOP2') + has_dpp = enc in ('ENC_VOP1', 'ENC_VOP2') or ( + enc in ('ENC_VOP3', 'ENC_VOP3P', 'VOP3_SDST_ENC') + and self._supports_vop_dpp_encoding(enc) + ) + if (has_sdwa or has_dpp) and 'vdst' in enc_field_names: + decls = '' + guards = [] + if has_sdwa: + decls += ( + 'bool sdwa_preserve = sdwa_dst_sel_ != amdgpu::sdwa::DWORD && ' + 'sdwa_dst_unused_ == amdgpu::sdwa::UNUSED_PRESERVE; ' + ) + guards.append('sdwa_preserve') + if has_dpp: + decls += ( + 'bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && ' + '(dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || ' + '(dpp_bound_ctrl_ == 0 && ' + 'amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); ' + ) + guards.append('dpp_partial') return ( - 'bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && ' - '(dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || ' - '(dpp_bound_ctrl_ == 0 && ' - 'amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); ' - 'if (dpp_partial) ' - 'if (const auto *dst = dst_operand(0)) ' + decls + f'if ({" || ".join(guards)}) ' + 'for (int i = 0; i < num_dst_operands(); ++i) ' + 'if (const auto *dst = dst_operand(i)) ' 'if (auto ref = dst->to_register_ref()) ' 'uses.expand(*ref);' ) diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp index 51e1e821028c..6ca56751f4a6 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp @@ -135,9 +135,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -187,9 +188,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp index 9da3e2e7b651..589e88144b59 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp @@ -135,9 +135,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -187,9 +188,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp index f43e4d8ee7c3..487c57d3f7f3 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp @@ -135,9 +135,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -187,9 +188,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp index e690140f4319..d061978e181a 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp @@ -129,9 +129,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -181,9 +182,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp index 48462ec89d48..3757c46c0d88 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp @@ -178,9 +178,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -242,9 +243,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { @@ -282,9 +284,10 @@ void Vop3::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop3::has_lit_0() { @@ -341,6 +344,17 @@ Vop3p::Vop3p(std::string_view mnemonic, const Vop3pMachineInst *inst, ExecuteFn size_ += sizeof(MachineInst); } +void Vop3p::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3p::has_lit_0() { return (((inst_.src0 == 255) && (inst_.src1 != 255)) && (inst_.src2 != 255)); } @@ -478,6 +492,17 @@ Vop3SdstEnc::Vop3SdstEnc(std::string_view mnemonic, const Vop3SdstEncMachineInst size_ += sizeof(MachineInst); } +void Vop3SdstEnc::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3SdstEnc::has_lit_0() { return (((inst_.src0 == 255) && (inst_.src1 != 255)) && (inst_.src2 != 255)); } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h index b3c340711bb5..fffa01a1fd65 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.h @@ -207,6 +207,7 @@ class Vop3 : public IsaInstruction { class Vop3p : public IsaInstruction { public: Vop3p(std::string_view mnemonic, const Vop3pMachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_and_has_lit_1(); @@ -277,6 +278,7 @@ class Vglobal : public IsaInstruction { class Vop3SdstEnc : public IsaInstruction { public: Vop3SdstEnc(std::string_view mnemonic, const Vop3SdstEncMachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_and_has_lit_1(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp index 6e6164bb93ad..bbdd58563b38 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp @@ -133,9 +133,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -181,9 +182,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp index 58df66eab6b3..d4fad27f1fde 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp @@ -133,9 +133,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -181,9 +182,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp index 193bf0b1dd17..5f18f10d00bd 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp @@ -133,9 +133,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -179,9 +180,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { @@ -210,9 +212,10 @@ void Vop3::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop3::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } @@ -255,6 +258,17 @@ Vop3p::Vop3p(std::string_view mnemonic, const Vop3pMachineInst *inst, ExecuteFn size_ += sizeof(MachineInst); } +void Vop3p::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3p::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } bool Vop3p::has_lit_1() { @@ -422,6 +436,17 @@ Vop3SdstEnc::Vop3SdstEnc(std::string_view mnemonic, const Vop3SdstEncMachineInst size_ += sizeof(MachineInst); } +void Vop3SdstEnc::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3SdstEnc::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h index 9f3a6c046a09..c1599b8fb1fe 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.h @@ -187,6 +187,7 @@ class Vop3 : public IsaInstruction { class Vop3p : public IsaInstruction { public: Vop3p(std::string_view mnemonic, const Vop3pMachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_has_lit_1(); @@ -272,6 +273,7 @@ class Flat : public IsaInstruction { class Vop3SdstEnc : public IsaInstruction { public: Vop3SdstEnc(std::string_view mnemonic, const Vop3SdstEncMachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_has_lit_1(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp index ec662d290d46..0259dcde070d 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp @@ -137,9 +137,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -183,9 +184,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { @@ -214,9 +216,10 @@ void Vop3::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop3::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } @@ -259,6 +262,17 @@ Vop3p::Vop3p(std::string_view mnemonic, const Vop3pMachineInst *inst, ExecuteFn size_ += sizeof(MachineInst); } +void Vop3p::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3p::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } bool Vop3p::has_lit_1() { @@ -426,6 +440,17 @@ Vop3SdstEnc::Vop3SdstEnc(std::string_view mnemonic, const Vop3SdstEncMachineInst size_ += sizeof(MachineInst); } +void Vop3SdstEnc::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3SdstEnc::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h index 7779883e4a74..7fc2630d9368 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.h @@ -189,6 +189,7 @@ class Vop3 : public IsaInstruction { class Vop3p : public IsaInstruction { public: Vop3p(std::string_view mnemonic, const Vop3pMachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_has_lit_1(); @@ -274,6 +275,7 @@ class Flat : public IsaInstruction { class Vop3SdstEnc : public IsaInstruction { public: Vop3SdstEnc(std::string_view mnemonic, const Vop3SdstEncMachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_has_lit_1(); diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp index 15dc0a593b40..ee08b618cb37 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp @@ -124,9 +124,10 @@ void Vop1::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -170,9 +171,10 @@ void Vop2::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (sdwa_preserve || dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop2::default_encoding() { @@ -201,9 +203,10 @@ void Vop3::implicit_uses(RegisterSet &uses) const { (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); if (dpp_partial) - if (const auto *dst = dst_operand(0)) - if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); } bool Vop3::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } @@ -246,6 +249,17 @@ Vop3p::Vop3p(std::string_view mnemonic, const Vop3pMachineInst *inst, ExecuteFn size_ += sizeof(MachineInst); } +void Vop3p::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3p::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } bool Vop3p::has_lit_1() { @@ -404,6 +418,17 @@ Vop3SdstEnc::Vop3SdstEnc(std::string_view mnemonic, const Vop3SdstEncMachineInst size_ += sizeof(MachineInst); } +void Vop3SdstEnc::implicit_uses(RegisterSet &uses) const { + bool dpp_partial = inst_.src0 == amdgpu::SRC_DPP && + (dpp_row_mask_ != 0xF || dpp_bank_mask_ != 0xF || + (dpp_bound_ctrl_ == 0 && amdgpu::dpp::dpp_ctrl_produces_oob(dpp_ctrl_))); + if (dpp_partial) + for (int i = 0; i < num_dst_operands(); ++i) + if (const auto *dst = dst_operand(i)) + if (auto ref = dst->to_register_ref()) + uses.expand(*ref); +} + bool Vop3SdstEnc::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h index 43044e0e4dab..2e03d451b3fd 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.h @@ -189,6 +189,7 @@ class Vop3 : public IsaInstruction { class Vop3p : public IsaInstruction { public: Vop3p(std::string_view mnemonic, const Vop3pMachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_has_lit_1(); @@ -286,6 +287,7 @@ class Vglobal : public IsaInstruction { class Vop3SdstEnc : public IsaInstruction { public: Vop3SdstEnc(std::string_view mnemonic, const Vop3SdstEncMachineInst *inst, ExecuteFn exec_fn); + void implicit_uses(RegisterSet &uses) const override; bool has_lit_0(); bool has_lit_1(); bool has_lit_0_has_lit_1(); diff --git a/emulation/rocjitsu/tests/analysis/liveness_test.cpp b/emulation/rocjitsu/tests/analysis/liveness_test.cpp index d183637dbd90..f4a3ff248869 100644 --- a/emulation/rocjitsu/tests/analysis/liveness_test.cpp +++ b/emulation/rocjitsu/tests/analysis/liveness_test.cpp @@ -1023,5 +1023,40 @@ TEST(GeneratedInstDefUse, Vop3CmpDppPartialRowMaskReadsSgprDestinationNotVgpr) { EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 8, 1})); } +TEST(GeneratedInstDefUse, Vop3pDppPartialRowMaskReadsDestination) { + // v_pk_add_u16 (VOP3P, VGPR vdst=6). VOP3P gained DPP on gfx11+ and has no + // SDWA, so a partial row mask preserves the packed VGPR dst. + // RDNA4 VOP3P word0: encoding[31:24]=204, op[22:16]=10 (v_pk_add_u16), + // vdst[7:0]=6. word1: src0[8:0]=250 (SRC_DPP), src1[17:9]=3 (VGPR3). + constexpr uint32_t kVop3pAddU16Word0 = (204u << 24) | (10u << 16) | 6u; + constexpr uint32_t kVop3pDppWord1 = (3u << 9) | 250u; + auto inst = decode_rdna4({kVop3pAddU16Word0, kVop3pDppWord1, (0x7u << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + ASSERT_EQ(std::string_view(inst->mnemonic()).substr(0, 12), "v_pk_add_u16"); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 6, 1})); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 6, 1})); +} + +TEST(GeneratedInstDefUse, Vop3SdstEncDppPartialRowMaskReadsBothDestinations) { + // v_add_co_ci_u32_e64 (VOP3_SDST_ENC) writes TWO destinations: a VGPR result + // (v6) and an SGPR carry-out (s[8:9]). A partial DPP mask preserves both, so + // both must surface -- this is why implicit_uses loops over every dst operand. + // RDNA4 VOP3_SDST_ENC word0: encoding[31:26]=53, op[25:16]=288, sdst[14:8]=8, + // vdst[7:0]=6. word1: src0=250 (SRC_DPP), src1[17:9]=3, src2[26:18]=10 (carry). + constexpr uint32_t kVop3SdstWord0 = (53u << 26) | (288u << 16) | (8u << 8) | 6u; + constexpr uint32_t kVop3SdstWord1 = (10u << 18) | (3u << 9) | 250u; + auto inst = decode_rdna4({kVop3SdstWord0, kVop3SdstWord1, (0x7u << 28) | (0xFu << 24) | 2u}); + ASSERT_NE(inst, nullptr); + ASSERT_EQ(std::string_view(inst->mnemonic()).substr(0, 14), "v_add_co_ci_u3"); + + InstDefUse idu(*inst); + EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 6, 1})); + EXPECT_TRUE(idu.defs.contains({RegClass::SGPR, 8, 2})); + EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 6, 1})); + EXPECT_TRUE(idu.uses.contains({RegClass::SGPR, 8, 2})); +} + } // namespace } // namespace rocjitsu From 95037138403e21344349334eba0d443490734a19 Mon Sep 17 00:00:00 2001 From: Soumil Kushwaha Date: Fri, 10 Jul 2026 16:27:21 -0500 Subject: [PATCH 10/10] updated implicit uses for DPP/SDWA to only add VGPRs to match the emulator (doesn't currently restore SGPRs) --- .../lib/python/amdisa/codegen/_generator.py | 12 ++++++---- .../isa/arch/amdgpu/cdna1/encodings.cpp | 6 +++-- .../isa/arch/amdgpu/cdna2/encodings.cpp | 6 +++-- .../isa/arch/amdgpu/cdna3/encodings.cpp | 6 +++-- .../isa/arch/amdgpu/cdna4/encodings.cpp | 6 +++-- .../isa/arch/amdgpu/gfx1250/encodings.cpp | 15 ++++++++----- .../isa/arch/amdgpu/rdna1/encodings.cpp | 6 +++-- .../isa/arch/amdgpu/rdna2/encodings.cpp | 6 +++-- .../isa/arch/amdgpu/rdna3/encodings.cpp | 15 ++++++++----- .../isa/arch/amdgpu/rdna3_5/encodings.cpp | 15 ++++++++----- .../isa/arch/amdgpu/rdna4/encodings.cpp | 15 ++++++++----- .../rocjitsu/tests/analysis/liveness_test.cpp | 22 ++++++++++--------- 12 files changed, 84 insertions(+), 46 deletions(-) diff --git a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py index b9c09a0c7a8f..4184e38ba549 100644 --- a/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py +++ b/emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py @@ -1699,10 +1699,13 @@ def _encoding_implicit_uses_impl( ) # SDWA dst_unused:PRESERVE and DPP that keeps lanes (partial row/bank # mask, or bound_ctrl == 0 with an edge-crossing dpp_ctrl) leave the old - # destination in place, so surface every destination operand as a use. - # Deriving the ref from the decoded dst operand(s) yields the real class - # and width and covers multi-dst encodings: VOP3_SDST_ENC writes both a - # VGPR result and an SGPR carry, both preserved under a partial mask. + # destination in place, so surface the preserved destination as a use. + # This matches what the executor actually restores: the non-VOPC SDWA/DPP + # merge only rewrites old vdst bytes/lanes via write_vgpr, so only a + # VGPR-class destination is preserved. SGPR destinations are fully + # written and never restored -- a VOP3_SDST_ENC carry (v_add_co_ci) and a + # VOP3 compare's SGPR mask alike -- so filter to RegClass::VGPR. The ref + # still comes from the decoded dst operand to get the real width. # # SDWA exists only on VOP1/VOP2; DPP additionally exists on VOP3/VOP3P/ # VOP3_SDST_ENC on gfx11+ (gated -- GCN/CDNA lack the dpp_* fields). @@ -1736,6 +1739,7 @@ def _encoding_implicit_uses_impl( 'for (int i = 0; i < num_dst_operands(); ++i) ' 'if (const auto *dst = dst_operand(i)) ' 'if (auto ref = dst->to_register_ref()) ' + 'if (ref->cls == RegClass::VGPR) ' 'uses.expand(*ref);' ) return '' diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp index 6ca56751f4a6..22c03d966d4c 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna1/encodings.cpp @@ -138,7 +138,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -191,7 +192,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp index 589e88144b59..7b4582183c80 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna2/encodings.cpp @@ -138,7 +138,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -191,7 +192,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp index 487c57d3f7f3..0c607640c908 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna3/encodings.cpp @@ -138,7 +138,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -191,7 +192,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp index d061978e181a..d3f3960881a8 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/cdna4/encodings.cpp @@ -132,7 +132,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -185,7 +186,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp index 3757c46c0d88..328d12893818 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/gfx1250/encodings.cpp @@ -181,7 +181,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -246,7 +247,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { @@ -287,7 +289,8 @@ void Vop3::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3::has_lit_0() { @@ -352,7 +355,8 @@ void Vop3p::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3p::has_lit_0() { @@ -500,7 +504,8 @@ void Vop3SdstEnc::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3SdstEnc::has_lit_0() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp index bbdd58563b38..ad9595e93233 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna1/encodings.cpp @@ -136,7 +136,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -185,7 +186,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp index d4fad27f1fde..4e3b1af07d72 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna2/encodings.cpp @@ -136,7 +136,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -185,7 +186,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp index 5f18f10d00bd..85e849431bc3 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3/encodings.cpp @@ -136,7 +136,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -183,7 +184,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { @@ -215,7 +217,8 @@ void Vop3::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } @@ -266,7 +269,8 @@ void Vop3p::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3p::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } @@ -444,7 +448,8 @@ void Vop3SdstEnc::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3SdstEnc::has_lit_0() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp index 0259dcde070d..99713b1f336a 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna3_5/encodings.cpp @@ -140,7 +140,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -187,7 +188,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { @@ -219,7 +221,8 @@ void Vop3::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } @@ -270,7 +273,8 @@ void Vop3p::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3p::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } @@ -448,7 +452,8 @@ void Vop3SdstEnc::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3SdstEnc::has_lit_0() { diff --git a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp index ee08b618cb37..828c9c4cfbd2 100644 --- a/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp +++ b/emulation/rocjitsu/lib/rocjitsu/src/rocjitsu/isa/arch/amdgpu/rdna4/encodings.cpp @@ -127,7 +127,8 @@ void Vop1::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop1::default_encoding() { @@ -174,7 +175,8 @@ void Vop2::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop2::default_encoding() { @@ -206,7 +208,8 @@ void Vop3::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } @@ -257,7 +260,8 @@ void Vop3p::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3p::has_lit_0() { return inst_.src0 == 255 && inst_.src1 != 255 && inst_.src2 != 255; } @@ -426,7 +430,8 @@ void Vop3SdstEnc::implicit_uses(RegisterSet &uses) const { for (int i = 0; i < num_dst_operands(); ++i) if (const auto *dst = dst_operand(i)) if (auto ref = dst->to_register_ref()) - uses.expand(*ref); + if (ref->cls == RegClass::VGPR) + uses.expand(*ref); } bool Vop3SdstEnc::has_lit_0() { diff --git a/emulation/rocjitsu/tests/analysis/liveness_test.cpp b/emulation/rocjitsu/tests/analysis/liveness_test.cpp index f4a3ff248869..7e06fdee77ad 100644 --- a/emulation/rocjitsu/tests/analysis/liveness_test.cpp +++ b/emulation/rocjitsu/tests/analysis/liveness_test.cpp @@ -1007,11 +1007,12 @@ TEST(GeneratedInstDefUse, Vop3DppBoundCtrlZeroEdgeCrossingReadsDestination) { EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 5, 1})); } -TEST(GeneratedInstDefUse, Vop3CmpDppPartialRowMaskReadsSgprDestinationNotVgpr) { - // v_cmp_lt_f32_e64 writes its lane mask to an SGPR pair through the vdst field - // (here s[8:9]), so a partial DPP mask preserves the SGPR -- not a VGPR. This - // is why Vop3::implicit_uses derives the ref from the dst operand instead of - // hardcoding RegClass::VGPR like VOP1/VOP2. +TEST(GeneratedInstDefUse, Vop3CmpDppPartialRowMaskDoesNotReadDestination) { + // v_cmp_lt_f32_e64 writes its lane mask to an SGPR pair via the vdst field + // (s[8:9]). The executor's non-VOPC DPP restore only touches the VGPR file at + // inst_.vdst -- a no-op that writes back the saved value -- and does NOT + // preserve the SGPR mask, which is fully written. So a partial mask reads + // neither the SGPR nor a VGPR, matching implicit_uses filtering to VGPR. auto inst = decode_rdna4( {kVop3Enc | kVop3CmpLtF32Op | 8u, kVop3DppWord1, (0x7u << 28) | (0xFu << 24) | 2u}); ASSERT_NE(inst, nullptr); @@ -1019,7 +1020,7 @@ TEST(GeneratedInstDefUse, Vop3CmpDppPartialRowMaskReadsSgprDestinationNotVgpr) { InstDefUse idu(*inst); EXPECT_TRUE(idu.defs.contains({RegClass::SGPR, 8, 2})); - EXPECT_TRUE(idu.uses.contains({RegClass::SGPR, 8, 2})); + EXPECT_FALSE(idu.uses.contains({RegClass::SGPR, 8, 2})); EXPECT_FALSE(idu.uses.contains({RegClass::VGPR, 8, 1})); } @@ -1039,10 +1040,11 @@ TEST(GeneratedInstDefUse, Vop3pDppPartialRowMaskReadsDestination) { EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 6, 1})); } -TEST(GeneratedInstDefUse, Vop3SdstEncDppPartialRowMaskReadsBothDestinations) { +TEST(GeneratedInstDefUse, Vop3SdstEncDppPartialRowMaskReadsOnlyVgprResult) { // v_add_co_ci_u32_e64 (VOP3_SDST_ENC) writes TWO destinations: a VGPR result - // (v6) and an SGPR carry-out (s[8:9]). A partial DPP mask preserves both, so - // both must surface -- this is why implicit_uses loops over every dst operand. + // (v6) and an SGPR carry-out (s[8:9]). The executor's DPP restore preserves + // only the VGPR result (write_vgpr); the SGPR carry is fully written, so only + // the VGPR surfaces as a use -- implicit_uses filters to RegClass::VGPR. // RDNA4 VOP3_SDST_ENC word0: encoding[31:26]=53, op[25:16]=288, sdst[14:8]=8, // vdst[7:0]=6. word1: src0=250 (SRC_DPP), src1[17:9]=3, src2[26:18]=10 (carry). constexpr uint32_t kVop3SdstWord0 = (53u << 26) | (288u << 16) | (8u << 8) | 6u; @@ -1055,7 +1057,7 @@ TEST(GeneratedInstDefUse, Vop3SdstEncDppPartialRowMaskReadsBothDestinations) { EXPECT_TRUE(idu.defs.contains({RegClass::VGPR, 6, 1})); EXPECT_TRUE(idu.defs.contains({RegClass::SGPR, 8, 2})); EXPECT_TRUE(idu.uses.contains({RegClass::VGPR, 6, 1})); - EXPECT_TRUE(idu.uses.contains({RegClass::SGPR, 8, 2})); + EXPECT_FALSE(idu.uses.contains({RegClass::SGPR, 8, 2})); } } // namespace