Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
aab77fb
[rocjitsu] Return scalar builder failures as results
IanWood1 Oct 29, 2026
1b33498
[rocjitsu] Merge develop and check new scalar builder consumers
IanWood1 Oct 29, 2026
9ec6ce7
[rocjitsu] Check scalar builder results in migrated tests
IanWood1 Oct 29, 2026
43b4648
[rocjitsu] Merge develop and reconcile checked builder tests
IanWood1 Oct 29, 2026
6d1cba5
[rocjitsu] Check newly merged boundary drain test encodings
IanWood1 Oct 29, 2026
bb9f2f5
[rocjitsu] Merge develop for new probe contract tests
IanWood1 Oct 29, 2026
3af8a7d
[rocjitsu] Check scalar encodings in new wave-size tests
IanWood1 Oct 29, 2026
21dbe6b
[rocjitsu] Address scalar builder review feedback
IanWood1 Oct 29, 2026
c3ddd13
[rocjitsu] Merge develop and adapt new scalar builder callers
IanWood1 Oct 30, 2026
cfba0f6
Forward scalar builder diagnostics through patching boundaries
IanWood1 Oct 30, 2026
675503c
Unwrap reused trampoline test NOP words once
IanWood1 Oct 30, 2026
99b2ad5
[rocjitsu] Preserve scalar encoding and diagnostic boundaries
IanWood1 Nov 2, 2026
9cd08a5
[rocjitsu] Merge develop into scalar builder errors
IanWood1 Nov 2, 2026
1c8ca86
Preserve scalar builder diagnostics in relocation sequences
IanWood1 Nov 2, 2026
494bc89
[rocjitsu] Preserve branch relocation diagnostic context
IanWood1 Nov 3, 2026
1c3c0cb
Preserve scalar builder diagnostics through layout growth
IanWood1 Nov 3, 2026
9a30b93
Clarify scalar builder diagnostics and test exact errors
IanWood1 Nov 3, 2026
e7b7978
Merge develop and adapt the new CFG test to scalar results
IanWood1 Nov 3, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ enum class ScalarSop2Op {
auto opcode = scalar_sop2_opcode(arch, ScalarSop2Op::AddU32);
if (!opcode)
return std::nullopt;
return build_sop2_encoding(arch, *opcode, sdst, ssrc0, ssrc1);
util::FailureOr<uint32_t> word = build_sop2_encoding(arch, *opcode, sdst, ssrc0, ssrc1);
if (word.failed())
return std::nullopt;
return word.value();
}

/// @brief Build an encoded scalar `s_sub_u32` instruction.
Expand All @@ -94,7 +97,10 @@ enum class ScalarSop2Op {
auto opcode = scalar_sop2_opcode(arch, ScalarSop2Op::SubU32);
if (!opcode)
return std::nullopt;
return build_sop2_encoding(arch, *opcode, sdst, ssrc0, ssrc1);
util::FailureOr<uint32_t> word = build_sop2_encoding(arch, *opcode, sdst, ssrc0, ssrc1);
if (word.failed())
return std::nullopt;
return word.value();
}

/// @brief Build an encoded scalar `s_addc_u32` instruction.
Expand All @@ -104,7 +110,10 @@ enum class ScalarSop2Op {
auto opcode = scalar_sop2_opcode(arch, ScalarSop2Op::AddcU32);
if (!opcode)
return std::nullopt;
return build_sop2_encoding(arch, *opcode, sdst, ssrc0, ssrc1);
util::FailureOr<uint32_t> word = build_sop2_encoding(arch, *opcode, sdst, ssrc0, ssrc1);
if (word.failed())
return std::nullopt;
return word.value();
}

/// @brief Build an encoded scalar `s_subb_u32` instruction.
Expand All @@ -114,7 +123,10 @@ enum class ScalarSop2Op {
auto opcode = scalar_sop2_opcode(arch, ScalarSop2Op::SubbU32);
if (!opcode)
return std::nullopt;
return build_sop2_encoding(arch, *opcode, sdst, ssrc0, ssrc1);
util::FailureOr<uint32_t> word = build_sop2_encoding(arch, *opcode, sdst, ssrc0, ssrc1);
if (word.failed())
return std::nullopt;
return word.value();
}

} // namespace
Expand Down Expand Up @@ -159,8 +171,11 @@ bool append_pc_delta_builder(std::vector<uint32_t> &words, rj_code_arch_t arch,
auto opcode = scalar_sop2_opcode(arch, ScalarSop2Op::AddNcU64);
if (!opcode)
return false;
words.push_back(build_sop2_encoding(arch, *opcode, pc_sreg, pc_sreg,
use_literal32 ? kLiteralOperand : kLiteral64Operand));
util::FailureOr<uint32_t> word = build_sop2_encoding(
arch, *opcode, pc_sreg, pc_sreg, use_literal32 ? kLiteralOperand : kLiteral64Operand);
if (word.failed())
return false;
words.push_back(word.value());
words.push_back(static_cast<uint32_t>(raw_delta));
if (!use_literal32)
words.push_back(static_cast<uint32_t>(raw_delta >> 32));
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ LegalizationLookupFn select_legalization(rj_code_arch_t guest, rj_code_arch_t ho
uint64_t call_offset, rj_code_arch_t arch) {
if (recovery_end > call_offset || (call_offset - recovery_end) % sizeof(uint32_t) != 0)
return false;
const uint32_t nop = build_s_nop(0, arch);
util::FailureOr<uint32_t> maybe_nop = build_s_nop(0, arch);
if (maybe_nop.failed())
return false;
const uint32_t nop = maybe_nop.value();
for (uint64_t offset = recovery_end; offset < call_offset; offset += sizeof(uint32_t)) {
if (text_word_at(text, offset) != nop)
return false;
Expand Down Expand Up @@ -390,9 +393,15 @@ generated_branch_island_pool_offsets(std::span<const uint8_t> text, rj_code_arch
return offsets;
std::vector<uint32_t> slot_words(lookahead_words, 0);

const uint32_t marker = build_s_nop(kBranchIslandPoolMarkerNopImmediate, arch);
const uint32_t skip_pool =
util::FailureOr<uint32_t> maybe_marker = build_s_nop(kBranchIslandPoolMarkerNopImmediate, arch);
if (maybe_marker.failed())
return offsets;
const uint32_t marker = maybe_marker.value();
util::FailureOr<uint32_t> maybe_skip =
build_s_branch(static_cast<int16_t>(kDirectBranchIslandPoolSlots), arch);
if (maybe_skip.failed())
return offsets;
const uint32_t skip_pool = maybe_skip.value();
constexpr uint64_t kPoolBytes = (kGeneratedIslandPoolHeaderWords + kDirectBranchIslandPoolSlots) *
static_cast<uint64_t>(sizeof(uint32_t));
for (uint64_t offset = 0;
Expand Down Expand Up @@ -1483,8 +1492,16 @@ adopted_root_return_offsets(const BlockOffsetIndex &block_index,
bool require_every_text_symbol_mapped,
const std::unordered_map<uint64_t, uint64_t> &canonical_code_pointer_placement,
std::vector<TranslationDiagnostic> &diagnostics) {
if (translated_text.size() < original_text_size)
append_nop_padding(translated_text, original_text_size - translated_text.size(), host_arch);
auto record_builder_error = [&](std::string_view message) {
append_error(diagnostics, DiagnosticKind::Legalization, std::string(message));
};
const util::DiagnosticEmitter builder_error(record_builder_error);
if (translated_text.size() < original_text_size &&
append_nop_padding(translated_text, original_text_size - translated_text.size(), host_arch,
&builder_error)
.failed()) {
return std::nullopt;
}

std::unordered_set<uint64_t> applied_descriptors;
for (const KdTranslation &translation : translations) {
Expand Down Expand Up @@ -2340,6 +2357,26 @@ TranslatedCodeObject BinaryTranslator::translate_impl(const AmdGpuCodeObject &ob
return leave_unchanged();
}

auto record_builder_error = [&](std::string_view message) {
append_error(result.diagnostics, DiagnosticKind::Legalization, std::string(message));
};
const util::DiagnosticEmitter builder_error(record_builder_error);
util::FailureOr<uint32_t> guest_pool_marker =
Comment thread
IanWood1 marked this conversation as resolved.
Resolved
build_s_nop(kBranchIslandPoolMarkerNopImmediate, guest_arch_, &builder_error);
if (guest_pool_marker.failed())
return leave_unchanged();
util::FailureOr<uint32_t> guest_pool_skip = build_s_branch(
static_cast<int16_t>(kDirectBranchIslandPoolSlots), guest_arch_, &builder_error);
if (guest_pool_skip.failed())
return leave_unchanged();
util::FailureOr<uint32_t> guest_long_marker =
build_s_nop(kLongDirectBranchMarkerNopImmediate, guest_arch_, &builder_error);
if (guest_long_marker.failed())
return leave_unchanged();
util::FailureOr<uint32_t> host_endpgm = build_s_endpgm(host_arch_, &builder_error);
if (host_endpgm.failed())
return leave_unchanged();

CodeObjectPatcher patcher(obj);

// Reject relocation-section metadata ROCr cannot materialize before examining
Expand Down Expand Up @@ -3433,20 +3470,16 @@ TranslatedCodeObject BinaryTranslator::translate_impl(const AmdGpuCodeObject &ob
const Instruction *marker = marker_it->second;
const Instruction *skip = skip_it->second;
const auto skip_delta = skip->branch_offset_bytes();
reachable_header =
marker->size() == static_cast<int>(sizeof(uint32_t)) &&
marker->raw_encoding() != nullptr &&
marker->raw_encoding()[0] ==
build_s_nop(kBranchIslandPoolMarkerNopImmediate, guest_arch_) &&
marker->next_instruction() == skip &&
skip->size() == static_cast<int>(sizeof(uint32_t)) &&
skip->raw_encoding() != nullptr &&
skip->raw_encoding()[0] ==
build_s_branch(static_cast<int16_t>(kDirectBranchIslandPoolSlots), guest_arch_) &&
skip_delta &&
pool_offset + kGeneratedIslandPoolHeaderWords * sizeof(uint32_t) +
static_cast<uint64_t>(*skip_delta) ==
pool_offset + kGeneratedIslandPoolBytes;
reachable_header = marker->size() == static_cast<int>(sizeof(uint32_t)) &&
marker->raw_encoding() != nullptr &&
marker->raw_encoding()[0] == guest_pool_marker.value() &&
marker->next_instruction() == skip &&
skip->size() == static_cast<int>(sizeof(uint32_t)) &&
skip->raw_encoding() != nullptr &&
skip->raw_encoding()[0] == guest_pool_skip.value() && skip_delta &&
pool_offset + kGeneratedIslandPoolHeaderWords * sizeof(uint32_t) +
static_cast<uint64_t>(*skip_delta) ==
pool_offset + kGeneratedIslandPoolBytes;
}

bool reachable_slot = false;
Expand Down Expand Up @@ -3694,10 +3727,9 @@ TranslatedCodeObject BinaryTranslator::translate_impl(const AmdGpuCodeObject &ob
marker->size() == static_cast<int>(sizeof(uint32_t)) &&
marker->src_loc() + sizeof(uint32_t) == first.source_getpc_offset &&
marker->raw_encoding() != nullptr &&
marker->raw_encoding()[0] ==
build_s_nop(kLongDirectBranchMarkerNopImmediate, guest_arch_) &&
call != nullptr && call->size() == static_cast<int>(sizeof(uint32_t)) &&
!has_interior_block_entry && !has_external_call_entry;
marker->raw_encoding()[0] == guest_long_marker.value() && call != nullptr &&
call->size() == static_cast<int>(sizeof(uint32_t)) && !has_interior_block_entry &&
!has_external_call_entry;
if (canonical_marked_window) {
consumer.preserve_marked_long_transfer = true;
consumer.marked_window_begin = marker->src_loc();
Expand Down Expand Up @@ -3937,7 +3969,9 @@ TranslatedCodeObject BinaryTranslator::translate_impl(const AmdGpuCodeObject &ob
.return_sreg = source_fixup.source_return_sreg,
.is_call = source_fixup.source_is_call,
.preserve_marked_long_transfer = true});
append_nop_padding(kernel_text, window_bytes, host_arch_);
if (append_nop_padding(kernel_text, window_bytes, host_arch_, &builder_error).failed()) {
return leave_unchanged();
}
active_marked_long_transfer = {
.source_end = consumer.marked_window_end,
.source_begin = consumer.marked_window_begin,
Expand Down Expand Up @@ -4099,7 +4133,11 @@ TranslatedCodeObject BinaryTranslator::translate_impl(const AmdGpuCodeObject &ob
.target_inst_offset = target_offset,
.target_window_bytes = branch_window_bytes,
.translated_words = target_words});
append_nop_padding(kernel_text, branch_window_bytes - inst.size(), host_arch_);
if (append_nop_padding(kernel_text, branch_window_bytes - inst.size(), host_arch_,
&builder_error)
.failed()) {
return leave_unchanged();
}
queue_trace(pending_traces, inst, offset, branch_leg, copied_original, false, changed,
target_offset, std::move(target_words));
continue;
Expand All @@ -4115,7 +4153,10 @@ TranslatedCodeObject BinaryTranslator::translate_impl(const AmdGpuCodeObject &ob
.return_sreg = source_fixup.source_return_sreg,
.is_call = source_fixup.source_is_call,
.keep_dynamic_when_long = recovered_it->second.builder_is_contiguous});
append_nop_padding(kernel_text, sizeof(uint32_t), host_arch_);
if (append_nop_padding(kernel_text, sizeof(uint32_t), host_arch_, &builder_error)
.failed()) {
return leave_unchanged();
}
continue;
}

Expand Down Expand Up @@ -4349,7 +4390,7 @@ TranslatedCodeObject BinaryTranslator::translate_impl(const AmdGpuCodeObject &ob
// any other target-side expansion, the terminator belongs in relocated
// function extents. Without an architectural terminator, ordinary text
// materialization can turn this unreachable stub into a fallthrough.
const uint32_t endpgm = build_s_endpgm(host_arch_);
const uint32_t endpgm = host_endpgm.value();
append_words(kernel_text, std::span<const uint32_t>(&endpgm, 1));
// The boundary is inferred from what follows the block, not from anything the block itself
// says, so a body that really did run on past here is cut short instead of translated.
Expand Down Expand Up @@ -4389,7 +4430,10 @@ TranslatedCodeObject BinaryTranslator::translate_impl(const AmdGpuCodeObject &ob
target_offset_by_source_offset.emplace(block->end_offset(), placement.target_end);
if (!can_use_long_direct_branches && !preserve_generated_branch_island_pools &&
block != scope.blocks.back() && kernel_text.size() >= next_branch_island_pool_offset) {
append_direct_branch_island_pool(kernel_text, layout, host_arch_);
if (append_direct_branch_island_pool(kernel_text, layout, host_arch_, &builder_error)
.failed()) {
return leave_unchanged();
}
next_branch_island_pool_offset = next_direct_branch_island_pool_offset(kernel_text.size());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,11 @@ descriptor_vgpr_granularity_for_wavefront(rj_code_arch_t arch, uint32_t wavefron
// Kernel-entry prologue construction.
// -----------------------------------------------------------------------------

void append_rdna4_salu_write(std::vector<uint32_t> &words, uint32_t word) {
words.push_back(word);
void append_rdna4_salu_write(std::vector<uint32_t> &words, util::FailureOr<uint32_t> word) {
// Only the RDNA4 prologue calls this helper. Its SGPRs follow the descriptor's
// six-bit user-SGPR count, and its TTMP/inline sources are fixed valid codes.
assert(word.succeeded());
words.push_back(word.value());
// The prologue feeds the original kernel entry, whose first few instructions
// may immediately consume these SGPRs. GFX12 needs an explicit ALU delay for
// scalar producer/consumer dependencies; entry prologues bypass the normal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,8 @@ reserve_cdna3_virtual_lds_base_sgpr_pair(TranslationContext &context, KernelBloc
translation.prologue_words.push_back(build_cdna3_s_add_u32(temp, temp, product));
};

// The admission checks above bound every move operand to the scalar fields;
// CDNA3 has the required encoding, so these builder results are guaranteed.
auto append_restore_guest_user_sgprs = [&]() {
if (translation.source_has_kernarg_segment_ptr) {
// The wrapper pointer is DBT-owned. Restore the source kernarg pointer
Expand All @@ -717,10 +719,11 @@ reserve_cdna3_virtual_lds_base_sgpr_pair(TranslationContext &context, KernelBloc
append_smem_load_dwordx2(temp, state_sbase,
translation.kernarg_wrapper_original_pointer_offset);
translation.prologue_words.push_back(
build_s_mov_b32(pointer_base_sgpr, temp, ROCJITSU_CODE_ARCH_CDNA3));
build_s_mov_b32(pointer_base_sgpr, temp, ROCJITSU_CODE_ARCH_CDNA3).value());
translation.prologue_words.push_back(
build_s_mov_b32(static_cast<uint16_t>(pointer_base_sgpr + 1),
static_cast<uint16_t>(temp + 1), ROCJITSU_CODE_ARCH_CDNA3));
static_cast<uint16_t>(temp + 1), ROCJITSU_CODE_ARCH_CDNA3)
.value());
}

// If DBT inserted a target-only kernarg segment pointer, CP shifted every
Expand All @@ -729,7 +732,8 @@ reserve_cdna3_virtual_lds_base_sgpr_pair(TranslationContext &context, KernelBloc
for (uint16_t i = 0; i < translation.user_sgpr_repair_count; ++i) {
const uint16_t dst = static_cast<uint16_t>(translation.user_sgpr_repair_start + i);
const uint16_t src = static_cast<uint16_t>(dst + 2);
translation.prologue_words.push_back(build_s_mov_b32(dst, src, ROCJITSU_CODE_ARCH_CDNA3));
translation.prologue_words.push_back(
build_s_mov_b32(dst, src, ROCJITSU_CODE_ARCH_CDNA3).value());
}
};

Expand All @@ -738,7 +742,7 @@ reserve_cdna3_virtual_lds_base_sgpr_pair(TranslationContext &context, KernelBloc
// stride fields:
// offset = wg_x * stride_x + wg_y * stride_y + wg_z * stride_z.
translation.prologue_words.push_back(
build_s_mov_b32(temp, scalar_positive_inline_u32(0), ROCJITSU_CODE_ARCH_CDNA3));
build_s_mov_b32(temp, scalar_positive_inline_u32(0), ROCJITSU_CODE_ARCH_CDNA3).value());
append_stride_term(translation.lds_overflow_workgroup_id_sgpr_x, kVirtualLdsStateStrideXOffset);
append_stride_term(translation.lds_overflow_workgroup_id_sgpr_y, kVirtualLdsStateStrideYOffset);
append_stride_term(translation.lds_overflow_workgroup_id_sgpr_z, kVirtualLdsStateStrideZOffset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,11 @@ ExpandResult lower_accvgpr_read(const Instruction &inst) {
std::to_string(src_acc));
const uint16_t src_unified = src_acc - 512;

if (dst_vgpr == src_unified)
return ExpandResult::success({build_s_nop()});
if (dst_vgpr == src_unified) {
constexpr util::FailureOr<uint32_t> nop = build_s_nop();
static_assert(nop.succeeded());
return ExpandResult::success({nop.value()});
}

return ExpandResult::success(
{build_v_mov_b32(static_cast<uint8_t>(dst_vgpr), static_cast<uint16_t>(256 + src_unified))});
Expand Down Expand Up @@ -283,7 +286,11 @@ ExpandResult expand_accvgpr_write(const Instruction &, uint32_t, uint64_t, std::
const LaneLayout *, const LaneLayout *) {
// AccVGPR writes are already represented by the unified VGPR mapping that
// descriptor translation reserves for RDNA targets.
return ExpandResult::success({build_s_nop()});
{
constexpr util::FailureOr<uint32_t> nop = build_s_nop();
static_assert(nop.succeeded());
return ExpandResult::success({nop.value()});
}
}

ExpandResult expand_mfma_f32_16x16x16_f16(const Instruction &inst, uint32_t, uint64_t,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@ std::vector<uint32_t> encode_waitcnt_gfx12(const WaitcntValues &vals) {
if (need_expcnt)
words.push_back(make_gfx12_sopp(rdna4::kSWaitExpcnt, vals.expcnt));

if (words.empty())
words.push_back(build_s_nop());
if (words.empty()) {
constexpr util::FailureOr<uint32_t> nop = build_s_nop();
static_assert(nop.succeeded());
words.push_back(nop.value());
}

return words;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: MIT

#include "rocjitsu/code/patch/entry_prologue.h"
#include "rocjitsu/code/patch/error_report.h"

#include "rocjitsu/code/analysis/free_registers.h"
#include "rocjitsu/code/builders/instruction_builder.h"
Expand Down Expand Up @@ -158,10 +159,7 @@ std::optional<DbiEntryPrologue> build_dbi_entry_prologue(const KD &desc, rj_code
prologue.original_kernarg_pointer_offset = layout->original_kernarg_pointer_offset;

// TODO: Accept a DiagnosticEmitter in the prologue APIs and remove this error_out adapter.
auto record_error = [error_out](std::string_view message) {
if (error_out != nullptr)
error_out->assign(message);
};
OptionalStringDiagnosticSink record_error(error_out);
const util::DiagnosticEmitter error(record_error);
// Delegated failures use the adapter to populate error_out before returning failure.
const auto offset_max = max_smem_byte_offset(arch, &error);
Expand Down Expand Up @@ -195,10 +193,17 @@ std::optional<DbiEntryPrologue> build_dbi_entry_prologue(const KD &desc, rj_code
return std::nullopt;
prologue.words.push_back(wait.value());

prologue.words.push_back(build_s_mov_b32(*kernarg_base, storage.entry_temp_base, arch));
prologue.words.push_back(build_s_mov_b32(static_cast<uint16_t>(*kernarg_base + 1),
static_cast<uint16_t>(storage.entry_temp_base + 1),
arch));
util::FailureOr<uint32_t> restore_lo =
build_s_mov_b32(*kernarg_base, storage.entry_temp_base, arch, &error);
if (restore_lo.failed())
return std::nullopt;
util::FailureOr<uint32_t> restore_hi =
build_s_mov_b32(static_cast<uint16_t>(*kernarg_base + 1),
static_cast<uint16_t>(storage.entry_temp_base + 1), arch, &error);
if (restore_hi.failed())
return std::nullopt;
prologue.words.push_back(restore_lo.value());
prologue.words.push_back(restore_hi.value());
return prologue;
}

Expand Down
Loading
Loading