CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
583 lines
23 KiB
C++
583 lines
23 KiB
C++
// Copyright (c) 2026 Advanced Micro Devices, Inc.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "dbt_translate.h"
|
|
|
|
#include "rocjitsu/code/amdgpu_code_object.h"
|
|
#include "rocjitsu/code/amdgpu_elf.h"
|
|
#include "rocjitsu/code/code_object_identity.h"
|
|
#include "rocjitsu/code/dbt/binary_translator.h"
|
|
#include "rocjitsu/code/executable.h"
|
|
#include "rocjitsu/isa/decoder.h"
|
|
#include "rocjitsu/isa/instruction.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <exception>
|
|
#include <format>
|
|
#include <iomanip>
|
|
#include <memory>
|
|
#include <span>
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
namespace rocjitsu::tools {
|
|
|
|
namespace {
|
|
|
|
constexpr int kInputError = 2;
|
|
constexpr int kTranslationError = 3;
|
|
constexpr int kValidationError = 5;
|
|
|
|
void add_error(ToolResult<TranslateOutput> &result, int exit_code, std::string message) {
|
|
result.errors.push_back({exit_code, std::move(message)});
|
|
}
|
|
|
|
struct CodeObjectInspection {
|
|
CodeObjectReport report;
|
|
std::string disassembly;
|
|
};
|
|
|
|
void record_decode_failure(CodeSectionReport §ion_report, size_t byte_offset,
|
|
std::string message) {
|
|
++section_report.decode_failure_count;
|
|
if (!section_report.has_first_decode_failure) {
|
|
section_report.has_first_decode_failure = true;
|
|
section_report.first_decode_failure_offset = byte_offset;
|
|
section_report.first_decode_failure_message = std::move(message);
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] CodeObjectInspection inspect_code_object(const AmdGpuCodeObject &obj,
|
|
rj_code_arch_t arch,
|
|
const std::string &label,
|
|
bool include_disassembly) {
|
|
CodeObjectInspection inspection;
|
|
inspection.report.available = true;
|
|
|
|
std::ostringstream os;
|
|
if (include_disassembly)
|
|
os << "--- " << label << " ---\n";
|
|
|
|
auto decoder = Decoder::create(arch);
|
|
inspection.report.decoder_available = decoder != nullptr;
|
|
if (!decoder) {
|
|
if (include_disassembly)
|
|
os << "decoder unavailable\n";
|
|
for (const auto *section : obj.text_sections()) {
|
|
CodeSectionReport section_report;
|
|
section_report.name = section->name();
|
|
section_report.size_bytes = section->size();
|
|
inspection.report.sections.push_back(std::move(section_report));
|
|
}
|
|
inspection.disassembly = os.str();
|
|
return inspection;
|
|
}
|
|
|
|
for (const auto *section : obj.text_sections()) {
|
|
CodeSectionReport section_report;
|
|
section_report.name = section->name();
|
|
section_report.size_bytes = section->size();
|
|
|
|
if (include_disassembly)
|
|
os << "section " << section->name() << " size=" << section->size() << "\n";
|
|
|
|
const auto *words = reinterpret_cast<const uint32_t *>(section->data());
|
|
const size_t word_count = section->size() / sizeof(uint32_t);
|
|
size_t pc = 0;
|
|
|
|
while (pc < word_count) {
|
|
// Linked gfx1250 objects use zero-filled holes between independently
|
|
// aligned function bodies. BasicBlock::build() treats these words as
|
|
// padding rather than instructions, so host validation must do the same.
|
|
if (arch == ROCJITSU_CODE_ARCH_GFX1250 && words[pc] == 0) {
|
|
++pc;
|
|
continue;
|
|
}
|
|
try {
|
|
std::unique_ptr<Instruction> inst(decoder->decode(&words[pc]));
|
|
if (!inst) {
|
|
record_decode_failure(section_report, pc * sizeof(uint32_t), "decode returned null");
|
|
if (include_disassembly) {
|
|
os << " 0x" << std::hex << std::setw(4) << std::setfill('0') << pc * 4
|
|
<< ": <decode returned null>\n"
|
|
<< std::dec << std::setfill(' ');
|
|
}
|
|
++pc;
|
|
continue;
|
|
}
|
|
|
|
const uint32_t inst_words = inst->size() / sizeof(uint32_t);
|
|
++section_report.instruction_count;
|
|
if (include_disassembly) {
|
|
os << " 0x" << std::hex << std::setw(4) << std::setfill('0') << pc * 4 << ": "
|
|
<< std::dec << std::setfill(' ') << inst->disassemble() << " [";
|
|
for (uint32_t i = 0; i < inst_words && pc + i < word_count; ++i) {
|
|
if (i != 0)
|
|
os << ' ';
|
|
os << std::hex << std::setw(8) << std::setfill('0') << words[pc + i];
|
|
}
|
|
os << std::dec << std::setfill(' ') << "]\n";
|
|
}
|
|
pc += inst_words == 0 ? 1 : inst_words;
|
|
} catch (const std::exception &e) {
|
|
record_decode_failure(section_report, pc * sizeof(uint32_t), e.what());
|
|
if (include_disassembly) {
|
|
os << " 0x" << std::hex << std::setw(4) << std::setfill('0') << pc * 4
|
|
<< ": <decode error: " << e.what() << ">\n"
|
|
<< std::dec << std::setfill(' ');
|
|
}
|
|
++pc;
|
|
}
|
|
}
|
|
|
|
inspection.report.sections.push_back(std::move(section_report));
|
|
}
|
|
|
|
inspection.disassembly = os.str();
|
|
return inspection;
|
|
}
|
|
|
|
[[nodiscard]] std::string format_offset(size_t offset) {
|
|
std::ostringstream os;
|
|
os << "0x" << std::hex << offset;
|
|
return os.str();
|
|
}
|
|
|
|
[[nodiscard]] bool validate_host_decode(const CodeObjectReport &report, std::string &error) {
|
|
if (!report.available) {
|
|
error = "translated output was not inspected";
|
|
return false;
|
|
}
|
|
if (!report.decoder_available) {
|
|
error = "host decoder unavailable";
|
|
return false;
|
|
}
|
|
|
|
size_t inst_count = 0;
|
|
size_t failures = 0;
|
|
const CodeSectionReport *first_failed_section = nullptr;
|
|
for (const auto §ion : report.sections) {
|
|
inst_count += section.instruction_count;
|
|
failures += section.decode_failure_count;
|
|
if (first_failed_section == nullptr && section.has_first_decode_failure)
|
|
first_failed_section = §ion;
|
|
}
|
|
|
|
if (inst_count == 0) {
|
|
error = "translated output contains no decodable host instructions";
|
|
return false;
|
|
}
|
|
if (failures != 0) {
|
|
error = std::to_string(failures) + " translated instructions failed host decode";
|
|
if (first_failed_section != nullptr) {
|
|
error += " (first failure in " + first_failed_section->name + " at " +
|
|
format_offset(first_failed_section->first_decode_failure_offset) + ": " +
|
|
first_failed_section->first_decode_failure_message + ")";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
[[nodiscard]] BinaryTranslatorOptions
|
|
make_binary_translator_options(const TranslateOptions &options) {
|
|
BinaryTranslatorOptions translator_options;
|
|
translator_options.debug_min_free_vgpr = options.debug_min_free_vgpr;
|
|
translator_options.debug_continue_after_failure = options.debug_continue_after_failure;
|
|
translator_options.skip_failed_kernels = options.skip_failed_kernels;
|
|
translator_options.input_revision = options.input_revision;
|
|
translator_options.output_revision = options.output_revision;
|
|
return translator_options;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace detail {
|
|
|
|
std::string describe_byte_difference(std::span<const uint8_t> first,
|
|
std::span<const uint8_t> second, std::string_view location) {
|
|
assert(!std::ranges::equal(first, second));
|
|
|
|
const size_t common_size = std::min(first.size(), second.size());
|
|
size_t offset = 0;
|
|
while (offset < common_size && first[offset] == second[offset])
|
|
++offset;
|
|
|
|
if (offset < common_size) {
|
|
const std::string size_change =
|
|
first.size() == second.size()
|
|
? ""
|
|
: std::format("; size {} -> {} bytes", first.size(), second.size());
|
|
return std::format("{} first differs at 0x{:x} (first=0x{:02x}, second=0x{:02x}){}", location,
|
|
offset, static_cast<unsigned>(first[offset]),
|
|
static_cast<unsigned>(second[offset]), size_change);
|
|
}
|
|
|
|
return std::format("{} size changed from {} to {} bytes", location, first.size(), second.size());
|
|
}
|
|
|
|
std::string find_idempotence_difference(std::span<const ExecutableSectionBytes> first_sections,
|
|
std::span<const ExecutableSectionBytes> second_sections,
|
|
std::span<const uint8_t> first_elf,
|
|
std::span<const uint8_t> second_elf) {
|
|
const auto count_named = [](std::span<const ExecutableSectionBytes> sections,
|
|
std::string_view name) {
|
|
return std::ranges::count_if(
|
|
sections, [name](const ExecutableSectionBytes §ion) { return section.name == name; });
|
|
};
|
|
|
|
std::optional<std::string_view> removed;
|
|
for (const ExecutableSectionBytes §ion : first_sections) {
|
|
if (count_named(first_sections, section.name) > count_named(second_sections, section.name)) {
|
|
removed = section.name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
std::optional<std::string_view> added;
|
|
for (const ExecutableSectionBytes §ion : second_sections) {
|
|
if (count_named(second_sections, section.name) > count_named(first_sections, section.name)) {
|
|
added = section.name;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (removed && added) {
|
|
return std::format("executable sections changed: removed '{}', added '{}'", *removed, *added);
|
|
}
|
|
if (removed)
|
|
return std::format("executable section '{}' was removed", *removed);
|
|
if (added)
|
|
return std::format("executable section '{}' was added", *added);
|
|
|
|
for (size_t index = 0; index < first_sections.size(); ++index) {
|
|
if (first_sections[index].name != second_sections[index].name) {
|
|
return std::format("executable sections were reordered at index {}: first='{}', second='{}'",
|
|
index, first_sections[index].name, second_sections[index].name);
|
|
}
|
|
}
|
|
|
|
for (size_t index = 0; index < first_sections.size(); ++index) {
|
|
const ExecutableSectionBytes &first_section = first_sections[index];
|
|
const ExecutableSectionBytes &second_section = second_sections[index];
|
|
if (!std::ranges::equal(first_section.bytes, second_section.bytes)) {
|
|
return describe_byte_difference(first_section.bytes, second_section.bytes,
|
|
std::format("section '{}'", first_section.name));
|
|
}
|
|
}
|
|
|
|
return describe_byte_difference(first_elf, second_elf, "ELF image");
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
namespace {
|
|
|
|
[[nodiscard]] std::vector<detail::ExecutableSectionBytes>
|
|
collect_executable_sections(const AmdGpuCodeObject &object) {
|
|
std::vector<detail::ExecutableSectionBytes> sections;
|
|
for (const std::unique_ptr<Section> §ion : object.all_sections()) {
|
|
if ((section->flags() & SHF_EXECINSTR) == 0)
|
|
continue;
|
|
sections.push_back(
|
|
{section->name(), {reinterpret_cast<const uint8_t *>(section->data()), section->size()}});
|
|
}
|
|
return sections;
|
|
}
|
|
|
|
[[nodiscard]] std::string find_idempotence_difference(const AmdGpuCodeObject &first_obj,
|
|
const AmdGpuCodeObject &second_obj,
|
|
std::span<const uint8_t> first_elf,
|
|
std::span<const uint8_t> second_elf) {
|
|
return detail::find_idempotence_difference(collect_executable_sections(first_obj),
|
|
collect_executable_sections(second_obj), first_elf,
|
|
second_elf);
|
|
}
|
|
|
|
[[nodiscard]] std::string disassemble_source_instruction(const AmdGpuCodeObject &obj,
|
|
uint64_t offset, rj_code_arch_t arch) {
|
|
auto decoder = Decoder::create(arch);
|
|
if (!decoder)
|
|
return "<decoder unavailable>";
|
|
|
|
const auto &text_sections = obj.text_sections();
|
|
if (text_sections.empty())
|
|
return "<source section unavailable>";
|
|
|
|
// BinaryTranslator source offsets are relative to the original .text section.
|
|
// Use the primary .text section so relocated local-cave bytes are not treated
|
|
// as source instructions when a translated object is inspected again.
|
|
const auto *text = text_sections.front();
|
|
if (offset % sizeof(uint32_t) != 0)
|
|
return "<source offset unaligned>";
|
|
if (offset + sizeof(uint32_t) > text->size())
|
|
return "<source offset out of range>";
|
|
|
|
const auto *words = reinterpret_cast<const uint32_t *>(text->data());
|
|
try {
|
|
std::unique_ptr<Instruction> inst(decoder->decode(&words[offset / sizeof(uint32_t)]));
|
|
if (!inst)
|
|
return "<decode returned null>";
|
|
return inst->disassemble();
|
|
} catch (const std::exception &e) {
|
|
return std::string("<decode error: ") + e.what() + ">";
|
|
}
|
|
}
|
|
|
|
[[nodiscard]] std::vector<std::string> disassemble_words(std::span<const uint32_t> words,
|
|
rj_code_arch_t arch) {
|
|
std::vector<std::string> lines;
|
|
auto decoder = Decoder::create(arch);
|
|
if (!decoder) {
|
|
lines.push_back("<decoder unavailable>");
|
|
return lines;
|
|
}
|
|
|
|
size_t pc = 0;
|
|
while (pc < words.size()) {
|
|
try {
|
|
std::unique_ptr<Instruction> inst(decoder->decode(&words[pc]));
|
|
if (!inst) {
|
|
lines.push_back("<decode returned null>");
|
|
++pc;
|
|
continue;
|
|
}
|
|
|
|
lines.push_back(inst->disassemble());
|
|
const uint32_t inst_words = inst->size() / sizeof(uint32_t);
|
|
pc += inst_words == 0 ? 1 : inst_words;
|
|
} catch (const std::exception &e) {
|
|
lines.push_back(std::string("<decode error: ") + e.what() + ">");
|
|
++pc;
|
|
}
|
|
}
|
|
|
|
return lines;
|
|
}
|
|
|
|
[[nodiscard]] InstructionTranslationReport
|
|
build_instruction_report(const TranslationTraceEvent &trace, const AmdGpuCodeObject &source,
|
|
rj_code_arch_t guest_arch, rj_code_arch_t host_arch) {
|
|
InstructionTranslationReport report;
|
|
report.source_offset = trace.source_offset;
|
|
report.source_size = trace.source_size;
|
|
report.source_words.assign(trace.source_words.begin(), trace.source_words.end());
|
|
report.source_instruction =
|
|
disassemble_source_instruction(source, trace.source_offset, guest_arch);
|
|
report.has_legalization = trace.legalization != nullptr;
|
|
report.action = trace.legalization ? trace.legalization->action : Action::Identity;
|
|
report.copied_original = trace.copied_original;
|
|
report.semantic_lowering = trace.semantic_lowering;
|
|
report.changed = trace.changed;
|
|
report.emitted_in_cave = trace.emitted_in_cave;
|
|
report.target_offset = trace.target_offset;
|
|
report.target_words.assign(trace.target_words.begin(), trace.target_words.end());
|
|
report.target_instructions = disassemble_words(report.target_words, host_arch);
|
|
return report;
|
|
}
|
|
|
|
struct SelectedInput {
|
|
std::unique_ptr<Executable> executable;
|
|
std::unique_ptr<AmdGpuCodeObject> direct_code_object;
|
|
const AmdGpuCodeObject *code_object = nullptr;
|
|
};
|
|
|
|
[[nodiscard]] SelectedInput select_input(const TranslateOptions &options, std::string &error) {
|
|
SelectedInput selected;
|
|
|
|
selected.executable = std::make_unique<Executable>(options.input_path);
|
|
if (selected.executable->is_valid()) {
|
|
if (options.input_target == ROCJITSU_CODE_TARGET_INVALID) {
|
|
error = "input target is not selectable from executable inputs: " + options.input_path;
|
|
selected.executable.reset();
|
|
return selected;
|
|
}
|
|
|
|
selected.code_object =
|
|
selected.executable->code_object(options.input_target, options.code_object_index);
|
|
if (selected.code_object != nullptr)
|
|
return selected;
|
|
|
|
error = "failed to select requested code object from executable: " + options.input_path;
|
|
selected.executable.reset();
|
|
return selected;
|
|
}
|
|
|
|
// Try executable inputs first, then standalone AMDGPU code-object ELFs. This
|
|
// keeps the CLI convenient for both HIP objects and already-extracted DBT
|
|
// outputs without exposing a mode flag.
|
|
selected.executable.reset();
|
|
selected.direct_code_object = std::make_unique<AmdGpuCodeObject>(options.input_path);
|
|
if (!selected.direct_code_object->is_valid()) {
|
|
error = "failed to parse input as an AMDGPU code object: " + options.input_path;
|
|
selected.direct_code_object.reset();
|
|
return selected;
|
|
}
|
|
|
|
selected.code_object = selected.direct_code_object.get();
|
|
return selected;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
std::optional<std::string_view> translation_request_error(const TranslateOptions &options) {
|
|
if (options.guest_arch == ROCJITSU_CODE_ARCH_GFX1250 &&
|
|
options.input_revision == ProcessorRevision::Unspecified) {
|
|
return "--input-revision is required when --input-target is gfx1250";
|
|
}
|
|
if (options.guest_arch != ROCJITSU_CODE_ARCH_GFX1250 &&
|
|
options.input_revision != ProcessorRevision::Unspecified) {
|
|
return "--input-revision is only valid when --input-target is gfx1250";
|
|
}
|
|
if (options.host_arch == ROCJITSU_CODE_ARCH_GFX1250 &&
|
|
options.output_revision == ProcessorRevision::Unspecified) {
|
|
return "--output-revision is required when --output-target is gfx1250";
|
|
}
|
|
if (options.host_arch != ROCJITSU_CODE_ARCH_GFX1250 &&
|
|
options.output_revision != ProcessorRevision::Unspecified) {
|
|
return "--output-revision is only valid when --output-target is gfx1250";
|
|
}
|
|
if (options.guest_arch == ROCJITSU_CODE_ARCH_GFX1250 &&
|
|
options.host_arch == ROCJITSU_CODE_ARCH_GFX1250 &&
|
|
options.input_revision == ProcessorRevision::Gfx1250A0 &&
|
|
options.output_revision == ProcessorRevision::Gfx1250B0) {
|
|
return "gfx1250 A0-to-B0 translation is not supported";
|
|
}
|
|
if (options.verify_idempotence && options.guest_arch != options.host_arch)
|
|
return "--verify-idempotence requires matching input and output architectures";
|
|
if (options.verify_idempotence && options.skip_failed_kernels)
|
|
return "--verify-idempotence cannot be combined with --skip-failed-kernels";
|
|
return std::nullopt;
|
|
}
|
|
|
|
ToolResult<TranslateOutput> translate_code_object(const TranslateOptions &options) {
|
|
ToolResult<TranslateOutput> output;
|
|
output.value.host_arch = options.host_arch;
|
|
output.value.target_mach =
|
|
options.target_mach ? options.target_mach : elf_mach_for_arch(options.host_arch);
|
|
output.value.input_revision = options.input_revision;
|
|
output.value.output_revision = options.output_revision;
|
|
|
|
if (const std::optional<std::string_view> request_error = translation_request_error(options)) {
|
|
add_error(output, kInputError, std::string(*request_error));
|
|
return output;
|
|
}
|
|
|
|
if (options.input_path.empty()) {
|
|
add_error(output, kInputError, "input path is required");
|
|
return output;
|
|
}
|
|
|
|
std::string error;
|
|
SelectedInput input = select_input(options, error);
|
|
if (input.code_object == nullptr) {
|
|
add_error(output, kInputError, error.empty() ? "failed to load input" : error);
|
|
return output;
|
|
}
|
|
output.value.source_code_object_id =
|
|
stable_code_object_id(input.code_object->image_data(), input.code_object->image_size());
|
|
|
|
const bool need_report = options.collect_diagnostics;
|
|
const bool need_source_disassembly = options.disassembly == DisassemblyMode::Source ||
|
|
options.disassembly == DisassemblyMode::Both;
|
|
const bool need_translated_disassembly = options.disassembly == DisassemblyMode::Translated ||
|
|
options.disassembly == DisassemblyMode::Both;
|
|
|
|
if (need_report || need_source_disassembly) {
|
|
auto source_inspection = inspect_code_object(*input.code_object, options.guest_arch, "source",
|
|
need_source_disassembly);
|
|
output.value.source_report = std::move(source_inspection.report);
|
|
output.value.disassembly += source_inspection.disassembly;
|
|
}
|
|
|
|
try {
|
|
BinaryTranslator translator(options.guest_arch, options.host_arch, options.target_mach,
|
|
make_binary_translator_options(options));
|
|
if (need_report) {
|
|
output.value.instruction_translations.clear();
|
|
translator.set_trace_callback([&](const TranslationTraceEvent &trace) {
|
|
output.value.instruction_translations.push_back(build_instruction_report(
|
|
trace, *input.code_object, options.guest_arch, options.host_arch));
|
|
});
|
|
}
|
|
auto translated = translator.translate(*input.code_object);
|
|
output.value.elf_bytes = std::move(translated.elf_bytes);
|
|
output.value.host_arch = translated.host_arch;
|
|
output.value.target_mach =
|
|
options.target_mach ? options.target_mach : elf_mach_for_arch(output.value.host_arch);
|
|
output.value.diagnostics = std::move(translated.diagnostics);
|
|
} catch (const std::exception &e) {
|
|
add_error(output, kTranslationError, std::string("translation threw exception: ") + e.what());
|
|
return output;
|
|
}
|
|
|
|
if (has_error_diagnostic(output.value.diagnostics)) {
|
|
add_error(output, kTranslationError, "translation failed");
|
|
return output;
|
|
}
|
|
|
|
if (output.value.elf_bytes.empty()) {
|
|
add_error(output, kTranslationError, "translation produced an empty ELF image");
|
|
return output;
|
|
}
|
|
|
|
AmdGpuCodeObject translated_obj(output.value.elf_bytes.data(), output.value.elf_bytes.size());
|
|
if (!translated_obj.is_valid()) {
|
|
add_error(output, kTranslationError, "translation produced an invalid AMDGPU code object");
|
|
return output;
|
|
}
|
|
|
|
{
|
|
auto translated_inspection = inspect_code_object(translated_obj, options.host_arch,
|
|
"translated", need_translated_disassembly);
|
|
output.value.translated_report = std::move(translated_inspection.report);
|
|
output.value.disassembly += translated_inspection.disassembly;
|
|
}
|
|
|
|
const bool data_only = has_diagnostic_kind(output.value.diagnostics, DiagnosticKind::DataOnly);
|
|
if (!data_only && !validate_host_decode(output.value.translated_report, error)) {
|
|
add_error(output, kValidationError, error);
|
|
return output;
|
|
}
|
|
|
|
if (options.verify_idempotence) {
|
|
output.value.idempotence_checked = true;
|
|
try {
|
|
BinaryTranslator verifier(options.guest_arch, options.host_arch, options.target_mach,
|
|
make_binary_translator_options(options));
|
|
TranslatedCodeObject second = verifier.translate(translated_obj);
|
|
const bool second_ok = second.ok();
|
|
output.value.idempotence_diagnostics = std::move(second.diagnostics);
|
|
if (!second_ok) {
|
|
add_error(output, kValidationError, "idempotence verification second translation failed");
|
|
return output;
|
|
}
|
|
if (second.elf_bytes != output.value.elf_bytes) {
|
|
AmdGpuCodeObject second_obj(second.elf_bytes.data(), second.elf_bytes.size());
|
|
if (!second_obj.is_valid()) {
|
|
add_error(output, kValidationError,
|
|
"idempotence verification produced an invalid AMDGPU code object");
|
|
return output;
|
|
}
|
|
add_error(output, kValidationError,
|
|
"translation output is not byte-idempotent: " +
|
|
find_idempotence_difference(translated_obj, second_obj,
|
|
output.value.elf_bytes, second.elf_bytes));
|
|
return output;
|
|
}
|
|
output.value.idempotence_verified = true;
|
|
} catch (const std::exception &e) {
|
|
add_error(output, kTranslationError,
|
|
std::string("idempotence verification second translation threw exception: ") +
|
|
e.what());
|
|
return output;
|
|
}
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
} // namespace rocjitsu::tools
|