Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
471 changes: 288 additions & 183 deletions emulation/rocjitsu/lib/python/amdisa/codegen/_generator.py

Large diffs are not rendered by default.

180 changes: 0 additions & 180 deletions emulation/rocjitsu/lib/python/amdisa/tests/test_exec_mask_flags.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3141,42 +3141,21 @@ def test_gfx1250_buffer_u64_atomic_payload_width_uses_two_dwords():


def test_ev124_125_arch_gating_in_generated_operand(amdgpu_generated_root: Path):
# M0 is encoded as 125 on RDNA3+ (and gfx1250) and as 124 on the
# older RDNA1/2 and all CDNA arches. Verify the generated operand.cpp
# carries the correct kM0EncodingValue constant for each ISA.
expected_m0_encoding = {
'cdna1': 124,
'cdna2': 124,
'cdna3': 124,
'cdna4': 124,
'rdna1': 124,
'rdna2': 124,
'rdna3': 125,
'rdna3_5': 125,
'rdna4': 125,
'gfx1250': 125,
}
for arch, encoding in expected_m0_encoding.items():
# The kM0EncodingValue constant lives with the scalar-resolve code, which
# is emitted into operand.cpp for unsplit arches and into operand_exec.cpp
# for arches that split model/execution sources (e.g. gfx1250).
op = (amdgpu_generated_root / arch / 'operand.cpp').read_text()
exec_path = amdgpu_generated_root / arch / 'operand_exec.cpp'
if exec_path.exists():
op += exec_path.read_text()
assert (
f'constexpr int kM0EncodingValue = {encoding};' in op
), f'{arch}: expected kM0EncodingValue = {encoding}'

# The M0 resolution logic is shared (parameterized by m0_ev) in
# scalar_operand_resolve.h rather than emitted per-arch, so verify the gating
# there: encoding value 124 is the NULL slot when M0 is 125, and the operand
# matching the arch's M0 encoding reads M0.
shared_resolve = (
amdgpu_generated_root / 'shared' / 'scalar_operand_resolve.h'
).read_text()
assert 'if (m0_ev == 125 && ev == 124)\n return 0u; // NULL' in shared_resolve
assert 'if (ev == m0_ev)\n return wf.m0();' in shared_resolve
rdna4_op = (amdgpu_generated_root / 'rdna4' / 'operand.cpp').read_text()
assert 'if (ev == 124)\n return 0u; // NULL' in rdna4_op
assert 'if (ev == 125)\n return wf.m0()' in rdna4_op

rdna3_op = (amdgpu_generated_root / 'rdna3' / 'operand.cpp').read_text()
assert 'if (ev == 124)\n return 0u; // NULL' in rdna3_op
assert 'if (ev == 125)\n return wf.m0()' in rdna3_op

rdna3_5_op = (amdgpu_generated_root / 'rdna3_5' / 'operand.cpp').read_text()
assert 'if (ev == 124)\n return 0u; // NULL' in rdna3_5_op
assert 'if (ev == 125)\n return wf.m0()' in rdna3_5_op

cdna4_op = (amdgpu_generated_root / 'cdna4' / 'operand.cpp').read_text()
assert 'if (ev == 124)\n return wf.m0()' in cdna4_op
assert 'ev == 125' not in cdna4_op.split('can_resolve_src_scalar')[1].split('}')[0]


def test_cdna4_mfma_f8f6f4_accepts_standalone_and_prefixed_encodings(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,39 @@ def test_isa_properties_codegen_uses_profile_values(tmp_path):
) in output


@pytest.mark.parametrize(
('arch', 'profile', 'raw_exec'),
[
('cdna3', CdnaProfile(), False),
('rdna4', Rdna4Profile(), True),
],
)
def test_operand_exec_register_access_is_wave32_gated(
tmp_path, arch, profile, raw_exec
):
generator = CodeGenerator(
SimpleNamespace(
arch_name=arch,
opnd_selectors=[],
operand_types=['OPR_SIMM16', 'OPR_SIMM32', 'OPR_VGPR'],
profile=profile,
),
str(tmp_path),
)

generator.gen_operand()
operand_cpp = (tmp_path / arch / 'operand.cpp').read_text()

assert 'return static_cast<uint32_t>(wf.exec());' in operand_cpp
assert 'wf.set_exec((wf.exec() & 0xFFFFFFFF00000000ULL) | val);' in operand_cpp
if raw_exec:
assert 'return static_cast<uint32_t>(wf.exec_raw() >> 32);' in operand_cpp
assert 'wf.set_exec_raw((wf.exec_raw() & 0x00000000FFFFFFFFULL)' in operand_cpp
else:
assert 'exec_raw' not in operand_cpp
assert 'set_exec_raw' not in operand_cpp


def test_gfx1250_operand_execution_backend_uses_separate_source(tmp_path):
generator = CodeGenerator(
SimpleNamespace(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

rj_add_object_library(rocjitsu_analysis
def_use_chain.cpp
exec_state.cpp
gfx1250_vgpr_msb.cpp
indirect_branch_discovery.cpp
liveness.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ namespace rocjitsu {

namespace {

// Checks if a RegisterRef class is a vector (VGPR/AccVGPR).
[[nodiscard]] bool is_vector_def(RegisterRef ref) {
[[nodiscard]] bool is_exec_masked_def(RegisterRef ref) {
return ref.cls == RegClass::VGPR || ref.cls == RegClass::ACC_VGPR;
}

Expand Down Expand Up @@ -56,33 +55,39 @@ void expand_operand_register(RegisterSet &set, const Instruction &inst, const Op
}

// Must-write with an unknown bank: expanding to all four tuples would falsely
// kill three tuples the instruction does not write (an over-kill, which is
// unsound for liveness), so record NOTHING in the def set instead. An
// unrecorded def can only cause an under-kill, which merely keeps a value live
// longer than necessary and is always a sound (conservative) approximation.
// Known-bank defs above record the precise physical tuple and kill as usual,
// subject to the EXEC-masking rules in kill_defs().
// kill three the instruction does not write, so record NOTHING in the def set.
// This is only sound because such a def never contributes a liveness kill:
// control reaches here only for a VGPR ref (see the early return above), every
// VGPR def is exec-masked (is_exec_masked_def), and kill_defs() drops all VGPR
// kills once has_exec_masked_vector_def is set. If any of those change (a
// non-exec-masked VGPR def, or kill_defs no longer suppressing VGPR kills), an
// unknown-bank def would start over-killing and this must record the precise
// physical tuple instead. (An assert(is_exec_masked_def(ref)) here would be
// tautological — ref is already known to be a VGPR — so the invariant is
// documented rather than checked.)
}

void add_def(InstDefUse &du, const Instruction &inst, const Operand &operand, RegisterRef ref,
const Gfx1250VgprMsbAnalysis *vgpr_msb, UnknownVgprDefPolicy unknown_vgpr_defs) {
expand_operand_register(du.defs, inst, operand, ref, vgpr_msb, OperandExpansionKind::Def,
unknown_vgpr_defs);
if (is_exec_masked_def(ref))
du.has_exec_masked_vector_def = true;
}

} // namespace

InstDefUse::InstDefUse(const Instruction &inst, const Gfx1250VgprMsbAnalysis *vgpr_msb,
UnknownVgprDefPolicy unknown_vgpr_defs) {
has_predicated_def = inst.flags() & PREDICATED_DEF;
const bool ignores_exec = inst.flags() & IGNORES_EXEC;
bool has_vector_def = false;

for (int i = 0; i < inst.num_dst_operands(); ++i) {
const auto *op = inst.dst_operand(i);
if (op == nullptr)
continue;
if (auto ref = op->to_register_ref()) {
expand_operand_register(defs, inst, *op, *ref, vgpr_msb, OperandExpansionKind::Def,
unknown_vgpr_defs);
has_vector_def |= is_vector_def(*ref);
}
if (auto ref = op->to_register_ref())
add_def(*this, inst, *op, *ref, vgpr_msb, unknown_vgpr_defs);
}
has_exec_masked_vector_def = has_vector_def && !ignores_exec;
// No generated instruction currently reports an implicit VGPR def. If one is
// added for gfx1250, it must expose an operand with a VGPR-MSB role so global
// usage can resolve the physical bank instead of recording only the raw low
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ class InstDefUse {
InstDefUse(const Instruction &inst, const Gfx1250VgprMsbAnalysis *vgpr_msb = nullptr,
UnknownVgprDefPolicy unknown_vgpr_defs = UnknownVgprDefPolicy::Omit);

RegisterSet defs; ///< Registers overwritten by the instruction.
RegisterSet uses; ///< Registers read before the instruction writes defs.
bool has_exec_masked_vector_def =
false; ///< True if the instruction doesn't ignore EXEC and has a vector def.
bool has_predicated_def = false; ///< True if defs preserve old values on some paths.
RegisterSet defs; ///< Registers overwritten by the instruction.
RegisterSet uses; ///< Registers read before the instruction writes defs.
bool has_exec_masked_vector_def = false; ///< True if any vector def is predicated by EXEC.
bool has_predicated_def = false; ///< True if defs preserve old values on some paths.
};

} // namespace rocjitsu
Loading
Loading