Skip to content
Merged
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
15 changes: 9 additions & 6 deletions .github/workflows/rocjitsu-corpus-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ on:
required: true
corpus_ref:
description: "Branch, tag, or SHA used by HIP, race, and DBT corpus tests"
# Last updated on 2026/10/16.
default: "e191fc247b8c04f984f41cf2b2a83ff89d6a0854"
# Last updated on 2026/10/21.
default: "ebb4004d7633293e94539b3577fb126397b8c6a4"
required: true
vulkan_corpus_ref:
description: "Branch, tag, or SHA used by the Vulkan compute/buffer CTS tests"
Expand All @@ -57,7 +57,7 @@ env:
ROCJITSU_DBT_VENV: ${{ github.workspace }}/gfx1250-dbt-venv-${{ github.run_id }}-${{ github.run_attempt }}
ROCJITSU_HSACO_CORPUS: ${{ github.workspace }}/gfx1250-packaged-hsacos-${{ github.run_id }}-${{ github.run_attempt }}
# Pins HIP, race, and DBT together; keep this equal to the corpus_ref default.
ROCJITSU_DBT_CORPUS_REF: e191fc247b8c04f984f41cf2b2a83ff89d6a0854
ROCJITSU_DBT_CORPUS_REF: ebb4004d7633293e94539b3577fb126397b8c6a4
ROCJITSU_DBT_SHA_FRAGMENTS: ${{ github.workspace }}/gfx1250-dbt-sha-pairs-${{ github.run_id }}-${{ github.run_attempt }}/fragments
ROCJITSU_DBT_SHA_MANIFEST: ${{ github.workspace }}/gfx1250-dbt-sha-pairs-${{ github.run_id }}-${{ github.run_attempt }}/gfx1250-b0-a0-sha-pairs.json
ROCM_SDK_VERSION: 10.1.0a20260812
Expand Down Expand Up @@ -181,8 +181,8 @@ jobs:
steps:
- name: Install base dependencies
run: |
sudo -n apt-get update
sudo -n DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
sudo -n apt-get -o Acquire::Retries=5 update
sudo -n DEBIAN_FRONTEND=noninteractive apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
build-essential \
ca-certificates \
curl \
Expand Down Expand Up @@ -592,6 +592,9 @@ jobs:
if [[ "${{ matrix.run_race_corpus }}" == "true" ]]; then
corpus_test_flags+=(--race-tests)
fi
if [[ "${{ matrix.enable_ubsan }}" == "1" ]]; then
corpus_test_flags+=(--ubsan)
fi
bash "${ROCJITSU_SOURCE_DIR}/tests/corpus/run-corpus-tests.sh" "${corpus_test_flags[@]}" || corpus_test_status=$?
exit "${corpus_test_status}"

Expand Down Expand Up @@ -815,7 +818,7 @@ jobs:
if: ${{ !cancelled() && matrix.name == 'release' && steps.build_rocjitsu.outcome == 'success' }}
shell: bash
run: |
sudo -n DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
sudo -n DEBIAN_FRONTEND=noninteractive apt-get -o Acquire::Retries=5 install -y --no-install-recommends \
mesa-vulkan-drivers \
libvulkan1 \
libdrm-amdgpu1 \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,8 @@ def _effectful_inline_binary_op(name: str, template: str) -> InlineBinaryOp:
' return (ab == c) ? (std::signbit(ab) ? c : ab) : (ab > c ? ab : c); }}()',
'maxmin_f': 'std::fmin(std::fmax({0}, {1}), {2})',
'minmax_f': 'std::fmax(std::fmin({0}, {1}), {2})',
# Integer results reach registers as raw bits. A floating result would make
# a negative value's conversion to unsigned register storage undefined.
'maxmin_i': 'std::min(std::max({0}, {1}), {2})',
'minmax_i': 'std::max(std::min({0}, {1}), {2})',
'maxmin_num': 'std::fmin(std::fmax({0}, {1}), {2})',
Expand Down
6 changes: 5 additions & 1 deletion emulation/rocjitsu/lib/python/amdisa/isa_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,11 @@ def supports_dpp_opcode(
*,
has_modifier_encoding: bool,
) -> bool:
"""Qualify DPP16/DPP8 availability when the XML omits a valid form."""
"""Qualify DPP16/DPP8 encoding availability.

has_modifier_encoding supplies the XML default. Profiles may override
it when the XML omits a valid form; dpp_opcode_rule defines ISA legality.
"""
del enc_name, inst_name
return has_modifier_encoding

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,27 +84,6 @@ def test_rdna4_dpp_predicate_filters_opcodes_without_alternates():
assert 'inst_.op >=' in helper


def test_rdna4_floating_dot_dpp_availability():
spec = Parser(str(_mrisa_dir() / 'amdgpu_isa_rdna4.xml'), Rdna4Profile()).parse()
generator = CodeGenerator(spec, '')
for name in (
'V_DOT2_F32_F16',
'V_DOT2_F32_BF16',
'V_DOT4_F32_FP8_BF8',
'V_DOT4_F32_BF8_FP8',
'V_DOT4_F32_FP8_FP8',
'V_DOT4_F32_BF8_BF8',
):
inst = _find_instruction(spec, 'ENC_VOP3P', name)
assert generator._instruction_supports_dpp(inst, 'ENC_VOP3P'), name
assert generator._instruction_supports_dpp8(inst, 'ENC_VOP3P'), name

for name in ('V_PK_ADD_F16', 'V_DOT4_I32_IU8', 'V_DOT8_U32_U4'):
inst = _find_instruction(spec, 'ENC_VOP3P', name)
assert not generator._instruction_supports_dpp(inst, 'ENC_VOP3P'), name
assert not generator._instruction_supports_dpp8(inst, 'ENC_VOP3P'), name


def test_opcode_set_condition_omits_unsigned_domain_boundaries():
assert CodeGenerator._opcode_set_condition([0, 1, 5]) == (
'inst_.op <= 1 || inst_.op == 5'
Expand Down Expand Up @@ -242,3 +221,43 @@ def test_dpp8_movrel_rejects_wide_semantic_source(monkeypatch):
AssertionError, match='DPP8 requires a source no wider than 32 bits'
):
generator._gen_execute_body(inst, sem, 'ENC_VOP1')


@pytest.mark.parametrize(
'name',
[
'V_DOT2_F32_F16',
'V_DOT2_F32_BF16',
'V_DOT4_F32_FP8_FP8',
'V_DOT4_F32_FP8_BF8',
'V_DOT4_F32_BF8_FP8',
'V_DOT4_F32_BF8_BF8',
],
)
def test_rdna4_floating_dot_dpp_availability(name):
spec = Parser(str(_mrisa_dir() / 'amdgpu_isa_rdna4.xml'), Rdna4Profile()).parse()
inst = _find_instruction(spec, 'ENC_VOP3P', name)
generator = CodeGenerator(spec, '')
if name.startswith('V_DOT4_'):
assert 'VOP3P_VOP_DPP16' not in inst.available_encodings
assert 'VOP3P_VOP_DPP8' not in inst.available_encodings
assert generator._instruction_supports_dpp(inst, 'ENC_VOP3P')
assert generator._instruction_supports_dpp8(inst, 'ENC_VOP3P')


@pytest.mark.parametrize(
'name',
[
'V_DOT4_I32_IU8',
'V_DOT4_U32_U8',
'V_DOT8_U32_U4',
'V_PK_ADD_F16',
'V_PK_ADD_U16',
],
)
def test_rdna4_unsupported_packed_dpp_remains_forbidden(name):
spec = Parser(str(_mrisa_dir() / 'amdgpu_isa_rdna4.xml'), Rdna4Profile()).parse()
inst = _find_instruction(spec, 'ENC_VOP3P', name)
generator = CodeGenerator(spec, '')
assert not generator._instruction_supports_dpp(inst, 'ENC_VOP3P')
assert not generator._instruction_supports_dpp8(inst, 'ENC_VOP3P')
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,20 @@ def test_rdna4_fp8_dot4_uses_the_gfx12_dot_model():
assert 'fp8_e4m3_to_f32' not in cpp


def test_fp8_dot4_rejects_missing_or_invalid_formats():
import pytest

for operation in (None, '', 'fp8', 'fp8_fp8_fp8', 'f16_fp8', 'fp8_unknown'):
with pytest.raises(ValueError, match='unhandled FP8 dot4 operation'):
gen_dot4(
['vdst'],
['src0', 'src1', 'src2'],
'dot4_f32_fp8',
operation=operation,
dot_accumulation=FloatDotAccumulation.GFX12,
)


def test_fp8_dot4_rejects_other_accumulation_models():
import pytest

Expand Down
17 changes: 12 additions & 5 deletions emulation/rocjitsu/lib/python/amdisa/tests/test_sema_lower.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,31 @@ def test_non_scc_inline_operation_has_no_scc_effect():
('callee', 'ty', 'expected', 'unexpected'),
[
('maxmin', SemaType.I32, 'std::min(std::max(', 'std::fmin'),
('minmax', SemaType.I32, 'std::max(std::min(', 'std::fmax'),
('maxmin', SemaType.U32, 'std::min(std::max(', 'std::fmin'),
('minmax', SemaType.U32, 'std::max(std::min(', 'std::fmax'),
('maxmin', SemaType.F32, 'std::fmin(std::fmax(', 'std::min'),
('minmax', SemaType.F32, 'std::fmax(std::fmin(', 'std::max'),
],
)
def test_minmax_ternary_lowering_uses_result_type(callee, ty, expected, unexpected):
@pytest.mark.parametrize('full_block', [False, True])
def test_minmax_ternary_lowering_uses_result_type(
callee, ty, expected, unexpected, full_block
):
node = SemaNode(
SemaNodeKind.CALL,
call_name=callee,
ty=ty,
children=(
SemaNode(SemaNodeKind.ID, id_name=callee),
SemaNode(SemaNodeKind.LIT, lit_value='a', ty=ty),
SemaNode(SemaNodeKind.LIT, lit_value='b', ty=ty),
SemaNode(SemaNodeKind.LIT, lit_value='c', ty=ty),
*(_cast(_src(i), ty) for i in range(3)),
),
)
result = _lower_call(node, LoweringContext(exec_model=ExecModel.VECTOR))
if full_block:
body = SemaNode(SemaNodeKind.ASSIGN, children=(_cast(_dst(0), ty), node))
result = lower_sema_block(SemaBlock('V_TEST', ExecModel.VECTOR, body))
else:
result = _lower_call(node, LoweringContext(exec_model=ExecModel.VECTOR))
assert expected in result
assert unexpected not in result

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ inline uint16_t gfx12_dot2_packed16(uint16_t src0_lo, uint16_t src1_lo, uint16_t

/// @brief GFX12 V_DOT4_F32_{FP8,BF8}_{FP8,BF8}: four OCP FP8 (E4M3) or BF8 (E5M2)
/// products from packed bytes plus an F32 accumulator.
/// @details Uses the grouped rounding, host/MODE independence and NaN precedence of
/// gfx12_dot8_f32, with one group of four products.
/// @tparam Src0Fp8 Selects FP8 for src0, and BF8 otherwise; likewise Src1Fp8.
template <bool Src0Fp8, bool Src1Fp8>
inline uint32_t gfx12_dot4_f32_fp8(uint32_t src0, uint32_t src1, uint32_t acc) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,27 +244,31 @@ std::array<uint32_t, 4> unpack_format(const Format &f, uint32_t selectors,
// channel count. General DST_SEL routing is not an inverse of load swizzling.
// CDNA1-4, RDNA1/2 and RDNA3.5 use this policy by extrapolation, without native
// measurements. Rejection marks unsupported emulation, not a hardware fault.
bool qualified_store_selectors(const Format &f, uint32_t selectors) {
util::FailureOr<uint32_t> resolve_store_selectors(const Format &f, uint32_t selectors) {
constexpr uint32_t identity = 4 | (5 << 3) | (6 << 6) | (7 << 9);
constexpr uint32_t reversed = 7 | (6 << 3) | (5 << 6) | (4 << 9);
const uint32_t channels =
std::count_if(f.widths.begin(), f.widths.end(), [](uint32_t width) { return width != 0; });
if (channels == 4) {
constexpr uint32_t rgb = 4 | (5 << 3) | (6 << 6);
constexpr uint32_t bgra = 6 | (5 << 3) | (4 << 6) | (7 << 9);
return selectors == identity || selectors == reversed || selectors == bgra ||
selectors == rgb || selectors == (rgb | (1 << 9));
if (selectors == identity || selectors == reversed || selectors == bgra || selectors == rgb ||
selectors == (rgb | (1 << 9)))
return selectors;
return util::Result::failure();
}
if (channels == 1 && f.widths[0] == 32 && f.number == Number::Float && selectors == 0)
Comment thread
newling marked this conversation as resolved.
Resolved
return 4u << 9; // Native gfx1100/gfx1201 resolve this view to A8 (W to R).
if (channels == 1 && (selectors == identity || selectors == reversed || selectors == (4 << 9)))
return true; // Single-channel identity, WZYX, and A8 views were measured.
return selectors; // Single-channel identity, WZYX, and A8 views were measured.
if (!channels)
return false;
return util::Result::failure();
for (uint32_t channel = 0; channel < 4; ++channel) {
const uint32_t selector = (selectors >> (channel * 3)) & 7;
if (channel < channels ? selector != channel + 4 : selector > 1)
return false;
return util::Result::failure();
}
return true;
return selectors;
}

void pack_format(const Format &f, uint32_t selectors, std::span<const uint32_t> components,
Expand Down Expand Up @@ -318,11 +322,13 @@ util::Result pack_buffer_format(uint32_t format, uint32_t selectors,
std::span<const uint32_t> components, std::span<uint8_t> bytes,
BufferFormatEncoding encoding, BufferStoreMissingChannel missing) {
const auto decoded = decode(format, encoding);
if (decoded.failed() || components.empty() ||
!qualified_store_selectors(decoded.value(), selectors))
if (decoded.failed() || components.empty())
return util::Result::failure();
const auto resolved = resolve_store_selectors(decoded.value(), selectors);
if (resolved.failed())
return util::Result::failure();
const fp_mode::detail::ScopedFenv environment(0);
pack_format(decoded.value(), selectors, components, bytes, missing, encoding);
pack_format(decoded.value(), resolved.value(), components, bytes, missing, encoding);
return util::Result::success();
}

Expand Down Expand Up @@ -388,9 +394,12 @@ util::FailureOr<bool> prepare_buffer_format(Wavefront &wf, VectorMemState &d, ui
d.buffer_selectors = resolved;
}
}
if (!d.is_load && format < 0 &&
!qualified_store_selectors(d.decoded_buffer_format, d.buffer_selectors))
return util::Result::failure();
if (!d.is_load && format < 0) {
const auto resolved = resolve_store_selectors(d.decoded_buffer_format, d.buffer_selectors);
if (resolved.failed())
return util::Result::failure();
d.buffer_selectors = resolved.value();
}
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ buffer_format_bytes(uint32_t format, BufferFormatEncoding encoding = BufferForma
util::FailureOr<std::array<uint32_t, 4>>
unpack_buffer_format(uint32_t format, uint32_t selectors, std::span<const uint8_t> bytes,
BufferFormatEncoding encoding = BufferFormatEncoding::Gfx11);
/// Packs an untyped descriptor-selected store, including DST_SEL qualification.
/// @brief Pack an untyped descriptor-selected store, including DST_SEL qualification.
/// @details Native gfx1100/gfx1201 single-channel FLOAT32 buffer stores resolve all-zero
/// DST_SEL to A8: W supplies R when present; otherwise @p missing supplies R.
/// This routing is extrapolated to the other supported targets and does not
/// apply to image stores.
util::Result
pack_buffer_format(uint32_t format, uint32_t selectors, std::span<const uint32_t> components,
std::span<uint8_t> bytes,
Expand Down
Loading
Loading