|  | // Copyright 2023 The IREE Authors | 
|  | // | 
|  | // Licensed under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  |  | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include "iree/base/api.h" | 
|  | #include "iree/io/file_contents.h" | 
|  | #include "iree/schemas/instruments/dispatch.h" | 
|  |  | 
|  | // NOTE: include order matters: | 
|  | #include "iree/base/internal/flatcc/parsing.h" | 
|  | #include "iree/schemas/instruments/dispatch_def_reader.h" | 
|  |  | 
|  | typedef struct { | 
|  | iree_instruments_DispatchFunctionDef_vec_t functions_def; | 
|  | iree_instruments_DispatchSiteDef_vec_t dispatch_sites_def; | 
|  | } iree_dispatch_metadata_t; | 
|  |  | 
|  | static iree_status_t iree_tooling_dump_dispatch_metadata( | 
|  | const uint8_t* flatbuffer_ptr, iree_host_size_t flatbuffer_size, | 
|  | iree_dispatch_metadata_t* out_metadata, FILE* stream) { | 
|  | memset(out_metadata, 0, sizeof(*out_metadata)); | 
|  |  | 
|  | iree_instruments_DispatchInstrumentDef_table_t instr_def = | 
|  | iree_instruments_DispatchInstrumentDef_as_root(flatbuffer_ptr); | 
|  |  | 
|  | iree_instruments_DispatchFunctionDef_vec_t functions_def = | 
|  | iree_instruments_DispatchInstrumentDef_functions(instr_def); | 
|  | out_metadata->functions_def = functions_def; | 
|  | for (iree_host_size_t i = 0; | 
|  | i < iree_instruments_DispatchFunctionDef_vec_len(functions_def); ++i) { | 
|  | fprintf(stream, "\n"); | 
|  | iree_instruments_DispatchFunctionDef_table_t function_def = | 
|  | iree_instruments_DispatchFunctionDef_vec_at(functions_def, i); | 
|  | flatbuffers_string_t name = | 
|  | iree_instruments_DispatchFunctionDef_name(function_def); | 
|  | fprintf(stream, | 
|  | "//" | 
|  | "===---------------------------------------------------------------" | 
|  | "-------===//\n"); | 
|  | fprintf(stream, "// export[%" PRIhsz "]: %s\n", i, name); | 
|  | fprintf(stream, | 
|  | "//" | 
|  | "===---------------------------------------------------------------" | 
|  | "-------===//\n"); | 
|  | flatbuffers_string_t target = | 
|  | iree_instruments_DispatchFunctionDef_target(function_def); | 
|  | if (target) fprintf(stream, "//  target: %s\n", target); | 
|  | flatbuffers_string_t layout = | 
|  | iree_instruments_DispatchFunctionDef_layout(function_def); | 
|  | if (layout) fprintf(stream, "//  layout: %s\n", layout); | 
|  | flatbuffers_string_t source = | 
|  | iree_instruments_DispatchFunctionDef_source(function_def); | 
|  | if (source) fprintf(stream, "%s\n", source); | 
|  | fprintf(stream, "\n"); | 
|  | } | 
|  |  | 
|  | fprintf(stream, | 
|  | "//" | 
|  | "===---------------------------------------------------------------" | 
|  | "-------===//\n"); | 
|  | iree_instruments_DispatchSiteDef_vec_t dispatch_sites_def = | 
|  | iree_instruments_DispatchInstrumentDef_sites(instr_def); | 
|  | out_metadata->dispatch_sites_def = dispatch_sites_def; | 
|  | for (iree_host_size_t i = 0; | 
|  | i < iree_instruments_DispatchSiteDef_vec_len(dispatch_sites_def); ++i) { | 
|  | iree_instruments_DispatchSiteDef_table_t dispatch_site_def = | 
|  | iree_instruments_DispatchSiteDef_vec_at(dispatch_sites_def, i); | 
|  | iree_instruments_DispatchFunctionDef_table_t function_def = | 
|  | iree_instruments_DispatchFunctionDef_vec_at( | 
|  | functions_def, | 
|  | iree_instruments_DispatchSiteDef_function(dispatch_site_def)); | 
|  | flatbuffers_string_t name = | 
|  | iree_instruments_DispatchFunctionDef_name(function_def); | 
|  | fprintf(stream, "// dispatch site %" PRIhsz ": %s\n", i, name); | 
|  | } | 
|  | fprintf(stream, | 
|  | "//" | 
|  | "===---------------------------------------------------------------" | 
|  | "-------===//\n\n"); | 
|  |  | 
|  | return iree_ok_status(); | 
|  | } | 
|  |  | 
|  | static void iree_tooling_dump_print_value( | 
|  | iree_instrument_dispatch_value_type_t type, uint64_t raw_value, | 
|  | FILE* stream) { | 
|  | union { | 
|  | int8_t i8; | 
|  | int16_t i16; | 
|  | int32_t i32; | 
|  | int64_t i64; | 
|  | float f32; | 
|  | double f64; | 
|  | uint8_t value_storage[sizeof(uint64_t)]; | 
|  | } value = {.i64 = raw_value}; | 
|  | switch (type) { | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_SINT_8: | 
|  | fprintf(stream, "%" PRId8, value.i8); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_UINT_8: | 
|  | fprintf(stream, "%" PRIu8, value.i8); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_SINT_16: | 
|  | fprintf(stream, "%" PRId16, value.i16); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_UINT_16: | 
|  | fprintf(stream, "%" PRIu16, value.i16); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_SINT_32: | 
|  | fprintf(stream, "%" PRId32, value.i32); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_UINT_32: | 
|  | fprintf(stream, "%" PRIu32, value.i32); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_SINT_64: | 
|  | fprintf(stream, "%" PRId64, value.i64); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_UINT_64: | 
|  | fprintf(stream, "%" PRIu64, value.i64); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_POINTER: | 
|  | fprintf(stream, "%16" PRIX64, value.i64); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_FLOAT_16: | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_BFLOAT_16: | 
|  | fprintf(stream, "%4" PRIX16, value.i16); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_FLOAT_32: | 
|  | fprintf(stream, "%e %f", value.f32, value.f32); | 
|  | break; | 
|  | case IREE_INSTRUMENT_DISPATCH_VALUE_TYPE_FLOAT_64: | 
|  | fprintf(stream, "%e %f", value.f64, value.f64); | 
|  | break; | 
|  | default: | 
|  | fprintf(stream, "<<unknown type: %02X>>", (uint32_t)type); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static iree_status_t iree_tooling_dump_dispatch_ringbuffer( | 
|  | const uint8_t* data_ptr, iree_host_size_t data_size, | 
|  | const iree_dispatch_metadata_t* metadata, FILE* stream) { | 
|  | const uint64_t ring_size = data_size - IREE_INSTRUMENT_DISPATCH_PADDING; | 
|  | const uint8_t* ring_data = data_ptr; | 
|  | const uint64_t ring_head = *(const uint64_t*)(ring_data + data_size - 8); | 
|  | const uint64_t ring_range = iree_min(ring_head, ring_size); | 
|  |  | 
|  | for (iree_host_size_t i = 0; i < ring_range;) { | 
|  | const iree_instrument_dispatch_header_t* header = | 
|  | (const iree_instrument_dispatch_header_t*)(ring_data + i); | 
|  | switch (header->tag) { | 
|  | case IREE_INSTRUMENT_DISPATCH_TYPE_WORKGROUP: { | 
|  | const iree_instrument_dispatch_workgroup_t* workgroup = | 
|  | (const iree_instrument_dispatch_workgroup_t*)header; | 
|  | iree_instruments_DispatchSiteDef_table_t dispatch_site_def = | 
|  | iree_instruments_DispatchSiteDef_vec_at( | 
|  | metadata->dispatch_sites_def, workgroup->dispatch_id); | 
|  | iree_instruments_DispatchFunctionDef_table_t function_def = | 
|  | iree_instruments_DispatchFunctionDef_vec_at( | 
|  | metadata->functions_def, | 
|  | iree_instruments_DispatchSiteDef_function(dispatch_site_def)); | 
|  | flatbuffers_string_t name_def = | 
|  | iree_instruments_DispatchFunctionDef_name(function_def); | 
|  | fprintf(stream, | 
|  | "%016" PRIX64 | 
|  | " | WORKGROUP dispatch(%u %s %ux%ux%u) %u,%u,%u pid:%u\n", | 
|  | (uint64_t)i, workgroup->dispatch_id, name_def, | 
|  | workgroup->workgroup_count_x, workgroup->workgroup_count_y, | 
|  | workgroup->workgroup_count_z, workgroup->workgroup_id_x, | 
|  | workgroup->workgroup_id_y, workgroup->workgroup_id_z, | 
|  | workgroup->processor_id); | 
|  | i += sizeof(*workgroup); | 
|  | break; | 
|  | } | 
|  | case IREE_INSTRUMENT_DISPATCH_TYPE_PRINT: { | 
|  | const iree_instrument_dispatch_print_t* print = | 
|  | (const iree_instrument_dispatch_print_t*)header; | 
|  | fprintf(stream, "%016" PRIX64 " | PRINT %.*s\n", | 
|  | (uint64_t)print->workgroup_offset, (int)print->length, | 
|  | print->data); | 
|  | i += iree_host_align(sizeof(*print) + print->length, 16); | 
|  | break; | 
|  | } | 
|  | case IREE_INSTRUMENT_DISPATCH_TYPE_VALUE: { | 
|  | const iree_instrument_dispatch_value_t* value = | 
|  | (const iree_instrument_dispatch_value_t*)header; | 
|  | fprintf(stream, "%016" PRIX64 " | VALUE %04u = ", | 
|  | (uint64_t)value->workgroup_offset, (uint32_t)value->ordinal); | 
|  | iree_tooling_dump_print_value(value->type, value->bits, stream); | 
|  | fputc('\n', stream); | 
|  | i += sizeof(*value); | 
|  | break; | 
|  | } | 
|  | case IREE_INSTRUMENT_DISPATCH_TYPE_MEMORY_LOAD: { | 
|  | const iree_instrument_dispatch_memory_op_t* op = | 
|  | (const iree_instrument_dispatch_memory_op_t*)header; | 
|  | fprintf(stream, "%016" PRIX64 " | LOAD  %016" PRIX64 " %u\n", | 
|  | (uint64_t)op->workgroup_offset, op->address, (int)op->length); | 
|  | i += sizeof(*op); | 
|  | break; | 
|  | } | 
|  | case IREE_INSTRUMENT_DISPATCH_TYPE_MEMORY_STORE: { | 
|  | const iree_instrument_dispatch_memory_op_t* op = | 
|  | (const iree_instrument_dispatch_memory_op_t*)header; | 
|  | fprintf(stream, "%016" PRIX64 " | STORE %016" PRIX64 " %u\n", | 
|  | (uint64_t)op->workgroup_offset, op->address, (int)op->length); | 
|  | i += sizeof(*op); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | return iree_make_status(IREE_STATUS_UNIMPLEMENTED, | 
|  | "unimplemented dispatch instr type: %u", | 
|  | (uint32_t)header->tag); | 
|  | } | 
|  | } | 
|  |  | 
|  | return iree_ok_status(); | 
|  | } | 
|  |  | 
|  | static iree_status_t iree_tooling_dump_instrument_file( | 
|  | iree_const_byte_span_t file_contents, FILE* stream) { | 
|  | const uint8_t* file_ptr = file_contents.data; | 
|  | iree_host_size_t file_size = file_contents.data_length; | 
|  |  | 
|  | iree_dispatch_metadata_t dispatch_metadata = {0}; | 
|  | for (iree_host_size_t file_offset = 0; file_offset < file_size;) { | 
|  | const iree_idbts_chunk_header_t* header = | 
|  | (const iree_idbts_chunk_header_t*)(file_ptr + file_offset); | 
|  | const uint8_t* payload = file_ptr + file_offset + sizeof(*header); | 
|  | switch (header->type) { | 
|  | case IREE_IDBTS_CHUNK_TYPE_DISPATCH_METADATA: { | 
|  | IREE_RETURN_IF_ERROR(iree_tooling_dump_dispatch_metadata( | 
|  | payload, header->content_length, &dispatch_metadata, stream)); | 
|  | break; | 
|  | } | 
|  | case IREE_IDBTS_CHUNK_TYPE_DISPATCH_RINGBUFFER: { | 
|  | IREE_RETURN_IF_ERROR(iree_tooling_dump_dispatch_ringbuffer( | 
|  | payload, header->content_length, &dispatch_metadata, stream)); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | return iree_make_status(IREE_STATUS_UNIMPLEMENTED, | 
|  | "unimplemented chunk type: %u", | 
|  | (uint32_t)header->type); | 
|  | } | 
|  | file_offset += | 
|  | sizeof(*header) + iree_host_align(header->content_length, 16); | 
|  | } | 
|  |  | 
|  | return iree_ok_status(); | 
|  | } | 
|  |  | 
|  | int main(int argc, char** argv) { | 
|  | IREE_TRACE_APP_ENTER(); | 
|  |  | 
|  | if (argc < 2) { | 
|  | fprintf(stderr, | 
|  | "Syntax: iree-dump-instruments instruments.bin > instruments.txt\n" | 
|  | "Example usage:\n" | 
|  | "  $ iree-compile \\n" | 
|  | "        --iree-hal-target-device=local \\n" | 
|  | "        --iree-hal-local-target-device-backends=llvm-cpu \\n" | 
|  | "        --iree-hal-instrument-dispatches=16mib \\n" | 
|  | "        --iree-llvmcpu-instrument-memory-accesses=false \\n" | 
|  | "        runtime/src/iree/runtime/demo/simple_mul.mlir \\n" | 
|  | "        -o=simple_mul_instr.vmfb\n" | 
|  | "  $ iree-run-module \\n" | 
|  | "        --device=local-sync \\n" | 
|  | "        --module=simple_mul_instr.vmfb \\n" | 
|  | "        --function=simple_mul \\n" | 
|  | "        --input=4xf32=2 \\n" | 
|  | "        --input=4xf32=4 \\n" | 
|  | "        --instrument_file=instrument.bin\n" | 
|  | "  $ iree-dump-instruments instrument.bin\n" | 
|  | "\n"); | 
|  | IREE_TRACE_APP_EXIT(EXIT_FAILURE); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  |  | 
|  | iree_io_file_contents_t* file_contents = NULL; | 
|  | iree_status_t status = iree_io_file_contents_map( | 
|  | iree_make_cstring_view(argv[1]), IREE_IO_FILE_ACCESS_READ, | 
|  | iree_allocator_system(), &file_contents); | 
|  | if (iree_status_is_ok(status)) { | 
|  | status = | 
|  | iree_tooling_dump_instrument_file(file_contents->const_buffer, stdout); | 
|  | } | 
|  | iree_io_file_contents_free(file_contents); | 
|  |  | 
|  | if (!iree_status_is_ok(status)) { | 
|  | iree_status_fprint(stderr, status); | 
|  | iree_status_free(status); | 
|  | IREE_TRACE_APP_EXIT(EXIT_FAILURE); | 
|  | return EXIT_FAILURE; | 
|  | } | 
|  | IREE_TRACE_APP_EXIT(EXIT_SUCCESS); | 
|  | return EXIT_SUCCESS; | 
|  | } |