blob: e2c3399f3af31df9c53b844fb6028a1b8bccc266 [file] [edit]
/*
* Copyright © 2026 Intel Corporation
* SPDX-License-Identifier: MIT
*/
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "util/ralloc.h"
#include "gen.h"
#include "gen_private.h"
namespace {
static void PRINTFLIKE(1, 2) NORETURN
failf(const char *fmt, ...)
{
fflush(stdout);
fprintf(stderr, "ERROR: ");
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fputc('\n', stderr);
exit(1);
}
static void
print_top_help(FILE *fp)
{
fprintf(fp,
"Usage: gentool COMMAND [OPTIONS] [INPUT]\n"
"\n"
"Commands:\n"
" asm Assemble Gen instructions into binary output\n"
" disasm Disassemble Gen instruction binaries\n"
" region Show register-region diagrams for a single instruction\n"
" help Show help for a command or help topic\n"
"\n"
"Help topics:\n"
" asm, disasm, region, platforms, syntax, lsc\n"
"\n"
"Supports Gfx9+ platforms.\n"
"\n"
"Use 'gentool help TOPIC' for help topics, or 'gentool COMMAND --help'\n"
"for command-specific help.\n");
}
static void
print_asm_help(FILE *fp)
{
fprintf(fp,
"Usage: gentool asm [OPTIONS] [INPUT]\n"
"\n"
"Assemble Gen instructions.\n"
"\n"
"Options:\n"
" -h, --help Show this help\n"
" -p, --platform=PLATFORM 3-letter platform name (required)\n"
" -i, --input=PATH Read assembly from PATH, or '-' for stdin\n"
" -o, --output=PATH Write output to PATH, or '-' for stdout\n"
"\n"
"If INPUT is provided positionally, it is used as the input path.\n"
"\n"
"Example:\n"
" gentool asm -p tgl shader.asm\n");
}
static void
print_disasm_help(FILE *fp)
{
fprintf(fp,
"Usage: gentool disasm [OPTIONS] [INPUT]\n"
"\n"
"Disassemble Gen binaries.\n"
"\n"
"Options:\n"
" -h, --help Show this help\n"
" -p, --platform=PLATFORM 3-letter platform name (required)\n"
" -i, --input=PATH Read binary input from PATH, or '-' for stdin\n"
" -o, --output=PATH Write assembly to PATH, or '-' for stdout\n"
" -v, --verbose Print explicit regions, types, and other details\n"
" --translated-sends Print LSC sends as their translated mnemonic instead of\n"
" a raw SEND instruction\n"
" --hex Prefix each instruction with a hex dump of its encoded bytes\n"
"\n"
"If INPUT is provided positionally, it is used as the input path.\n"
"\n"
"Example:\n"
" gentool disasm -p tgl shader.bin\n");
}
static void
print_region_help(FILE *fp)
{
fprintf(fp,
"Usage: gentool region [OPTIONS]\n"
"\n"
"Parse a single Gen instruction and show the register regions it touches.\n"
"\n"
"Options:\n"
" -h, --help Show this help\n"
" -p, --platform=PLATFORM 3-letter platform name (required)\n"
"\n"
"Reads from stdin, writes to stdout.\n"
"\n"
"The input must parse to exactly one instruction.\n"
"\n"
"Each register row is drawn as one byte per character. For regular\n"
"regions, the bytes of logical element 0..v are marked with 0-9a-v.\n"
"A '*' marks overlapping logical elements. SEND payloads are shown one\n"
"message register at a time. Immediate operands are reported separately.\n"
"\n"
"Currently this command requires Align1 instructions for explicit\n"
"source/destination region diagrams; SEND payloads are supported too.\n"
"\n"
"Example:\n"
" printf 'add (8) r5<1>:ud r6<8;8,1>:ud r7<8;8,1>:ud\n' | gentool region -p tgl\n");
}
static void
print_platforms_help(FILE *fp)
{
fprintf(fp, "Supported platforms (Gfx9+):\n\n");
for (unsigned i = 0; ; i++) {
const char *platform = intel_platform_name_by_index(i);
if (!platform)
break;
intel_device_info devinfo = {};
const int pci_id = intel_device_name_to_pci_device_id(platform);
if (pci_id < 0 || !intel_get_device_info_for_build(pci_id, &devinfo))
continue;
if (devinfo.ver < 9)
continue;
if (devinfo.verx10 % 10 == 0) {
fprintf(fp, " %-5s gfx%d\n", platform, devinfo.verx10 / 10);
} else {
fprintf(fp, " %-5s gfx%d.%d\n", platform,
devinfo.verx10 / 10, devinfo.verx10 % 10);
}
}
}
static void
print_syntax_help(FILE *fp)
{
fprintf(fp,
"Gen syntax quick reference\n"
"\n"
" add (8) r5 r6 0x0000002a\n"
" opcode, exec size, dst, src0, src1\n"
" defaults: sequential region/stride, :ud, subnr 0, chan offset 0\n"
"\n"
" mov (8|M0) r10.0<1>:f r11.0<8;8,1>:f\n"
" explicit dst stride, src region, and type suffixes\n"
" '<v;w,h>' is a source region; '<v>' is shorthand for '<v;1,0>'\n"
" the destination uses a stride like '<1>'\n"
"\n"
" cmp (16) (ge)f3.1 r5 r6 r7\n"
" conditional modifier writes a flag register\n"
"\n"
" (W&~f0.0) add (8|M0) r1.0<1>:f r2.0<8;8,1>:f 0x3f800000:f\n"
" 'W' enables write masking; '~f0.0' is the inverted predicate\n"
"\n"
" load.slm.d32x4.a32 (16) r10 r8\n"
" translated LSC load syntax\n"
" see 'gentool help lsc' for more on LSC mnemonics\n"
"\n"
" send.ugm (1|M0) r6 r5:1 null:0 a0.0 0x2229e500\n"
" raw SEND syntax; ':1' and ':0' are source lengths\n"
"\n"
" if (16) jip:L10 uip:L20\n"
" add (16) r20 r20 1:d\n"
" else (16) jip:L20 uip:L20\n"
" L10:\n"
" add (16) r21 r21 -1:d\n"
" L20:\n"
" labels may be used as branch targets in structured control flow\n"
"\n");
}
static void
print_lsc_help(FILE *fp)
{
fprintf(fp,
"LSC syntax quick reference\n"
"\n"
" load.slm.d32x4.a32 (16) r10 r8\n"
" load.<sfid>.<data_shape>.<addr_size>\n"
" dst is the response GRF, src is the address payload\n"
"\n"
" load.ugm.d32x32t.a32.ca.cc.bss[a0.0] (1) r6 r5\n"
" ugm/tgm/slm select the LSC message target\n"
" ca.cc are the L1/L3 cache controls; bss[a0.0] is the surface syntax\n"
"\n"
" load_cmask.tgm.d32.xy.a32.ca.ca.bti[3] (8) r20 r12\n"
" typed loads with load_cmask; 'xy' is the channel mask\n"
"\n"
" atomic_add.ugm.d32.a32.wt.wb.bss[a0.0] (8) r40 r35 r8\n"
" atomics take an address payload plus source data payload(s)\n"
"\n"
" send.ugm (1|M0) r6 r5:1 null:0 a0.0 0x2229e500\n"
" raw SEND form of the translated UGM load above\n"
"\n"
"Surface syntaxes:\n"
" flat flat addressing (can be omitted)\n"
" slm shared local memory\n"
" bss[a0.0] bindless surface from an address register\n"
" ss[a0.0] surface state from an address register\n"
" bti[3] surface state by BTI index\n"
"\n"
"Cache control suffixes are written as <L1>.<L3>:\n"
" uc = uncached\n"
" ca = cached\n"
" cc = cached as constant (Xe2+ loads)\n"
" st = streaming\n"
" ri = invalidate-after-read\n"
" wt = write-through (stores)\n"
" wb = write-back (stores)\n");
}
static char
element_char(unsigned idx)
{
static const char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
return idx < ARRAY_SIZE(digits) - 1 ? digits[idx] : '#';
}
static const char *
send_desc_label(bool ex_desc)
{
return ex_desc ? "ex_desc" : "desc";
}
static std::string
register_name(gen_file file, unsigned nr)
{
if (file == GEN_GRF)
return "r" + std::to_string(nr);
if (file != GEN_ARF)
return "reg" + std::to_string(nr);
const unsigned arf = nr & 0xf0;
const unsigned idx = nr & 0x0f;
switch (arf) {
case GEN_ARF_NULL:
return "null";
case GEN_ARF_ADDRESS:
return "a" + std::to_string(idx);
case GEN_ARF_ACCUMULATOR:
return "acc" + std::to_string(idx);
case GEN_ARF_FLAG:
return "f" + std::to_string(idx);
case GEN_ARF_MASK:
return "mask" + std::to_string(idx);
case GEN_ARF_SCALAR:
return "s" + std::to_string(idx);
case GEN_ARF_STATE:
return "sr" + std::to_string(idx);
case GEN_ARF_CONTROL:
return "cr" + std::to_string(idx);
case GEN_ARF_NOTIFICATION_COUNT:
return "n" + std::to_string(idx);
case GEN_ARF_IP:
return "ip";
case GEN_ARF_TDR:
return "tdr" + std::to_string(idx);
case GEN_ARF_TIMESTAMP:
return "tm" + std::to_string(idx);
default:
return "arf" + std::to_string(nr);
}
}
struct region_diagram {
gen_file file = GEN_BAD_FILE;
unsigned row_size = 0;
int first_row = 0;
std::vector<std::string> rows;
};
static void
region_diagram_reserve_row(region_diagram *diag, int row)
{
if (diag->rows.empty()) {
diag->first_row = row;
diag->rows.emplace_back(diag->row_size, '.');
return;
}
if (row < diag->first_row) {
diag->rows.insert(diag->rows.begin(), diag->first_row - row,
std::string(diag->row_size, '.'));
diag->first_row = row;
} else if ((size_t)(row - diag->first_row) >= diag->rows.size()) {
diag->rows.resize(row - diag->first_row + 1,
std::string(diag->row_size, '.'));
}
}
static void
region_diagram_mark_byte(region_diagram *diag, int row, unsigned col, char value)
{
assert(col < diag->row_size);
region_diagram_reserve_row(diag, row);
char &slot = diag->rows[row - diag->first_row][col];
if (slot == '.' || slot == value) {
slot = value;
} else {
slot = '*';
}
}
static void
region_diagram_mark_span(region_diagram *diag,
const gen_operand &op,
unsigned start,
unsigned size,
char value)
{
unsigned offset = op.subnr + start;
for (unsigned i = 0; i < size; i++, offset++) {
const int row = op.nr + offset / diag->row_size;
const unsigned col = offset % diag->row_size;
region_diagram_mark_byte(diag, row, col, value);
}
}
static void
print_region_row(FILE *fp, const std::string &row)
{
for (size_t i = 0; i < row.size(); i++) {
const size_t rev_i = row.size() - 1 - i;
if (i > 0) {
if (i % 16 == 0) {
fputs(" ", fp);
} else if (i % 4 == 0) {
fputc(' ', fp);
}
}
fputc(row[rev_i], fp);
}
}
static void
print_region_diagram(FILE *fp, const region_diagram &diag)
{
if (diag.rows.empty()) {
fputs("| (no register bytes) |\n", fp);
return;
}
for (size_t i = 0; i < diag.rows.size(); i++) {
const int row = diag.first_row + i;
const std::string reg_name = register_name(diag.file, row);
fputs("| ", fp);
print_region_row(fp, diag.rows[i]);
fprintf(fp, " | %s\n", reg_name.c_str());
}
}
static bool
build_align1_dst_diagram(const intel_device_info *devinfo,
const gen_inst *inst,
region_diagram *diag)
{
if (inst->align16)
return false;
const gen_operand &dst = inst->dst;
if (dst.indirect || dst.file == GEN_IMM || is_null(dst))
return false;
diag->file = dst.file;
diag->row_size = devinfo->grf_size;
const unsigned type_size = MAX2(gen_type_size_bytes(dst.type), 1u);
const unsigned hstride = dst.region.hstride;
for (unsigned i = 0; i < inst->exec_size; i++) {
const unsigned start = i * hstride * type_size;
region_diagram_mark_span(diag, dst, start, type_size, element_char(i));
}
return true;
}
static bool
build_align1_src_diagram(const intel_device_info *devinfo,
const gen_inst *inst,
const gen_operand &src,
region_diagram *diag)
{
if (inst->align16)
return false;
if (src.indirect || src.file == GEN_IMM)
return false;
diag->file = src.file;
diag->row_size = devinfo->grf_size;
const unsigned type_size = MAX2(gen_type_size_bytes(src.type), 1u);
if (src.region.vstride == GEN_VSTRIDE_ONE_DIMENSIONAL) {
for (unsigned i = 0; i < inst->exec_size; i++) {
const unsigned start = i * src.region.hstride * type_size;
region_diagram_mark_span(diag, src, start, type_size, element_char(i));
}
return true;
}
const unsigned width = MAX2(src.region.width, 1u);
const unsigned rows = DIV_ROUND_UP(inst->exec_size, width);
unsigned elem = 0;
unsigned rowbase = 0;
for (unsigned y = 0; y < rows && elem < inst->exec_size; y++) {
unsigned offset = rowbase;
for (unsigned x = 0; x < width && elem < inst->exec_size; x++, elem++) {
region_diagram_mark_span(diag, src, offset, type_size, element_char(elem));
offset += src.region.hstride * type_size;
}
rowbase += src.region.vstride * type_size;
}
return true;
}
static bool
build_send_payload_diagram(const intel_device_info *devinfo,
const gen_operand &op,
unsigned reg_count,
region_diagram *diag)
{
if (op.indirect || op.file == GEN_IMM || is_null(op) || reg_count == 0)
return false;
diag->file = op.file;
diag->row_size = devinfo->grf_size;
for (unsigned r = 0; r < reg_count; r++) {
const unsigned start = r * diag->row_size;
region_diagram_mark_span(diag, op, start, diag->row_size, element_char(r));
}
return true;
}
static bool
build_send_desc_diagram(const intel_device_info *devinfo,
const gen_operand &op,
region_diagram *diag)
{
if (op.indirect || op.file == GEN_IMM || is_null(op))
return false;
diag->file = op.file;
diag->row_size = devinfo->grf_size;
region_diagram_mark_span(diag, op, 0, 4, 'd');
return true;
}
static int
send_dst_len(const gen_inst *inst)
{
if (inst->send.desc_is_reg)
return -1;
return (inst->send.desc_imm >> 20) & 0x1F;
}
static void
print_regular_region_output(const intel_device_info *devinfo,
FILE *fp,
const gen_inst *inst)
{
const gen_format format = gen_inst_format(inst->opcode);
const bool has_dst = gen_inst_has_dst(format, inst->opcode);
const unsigned num_sources = gen_inst_num_sources(devinfo, inst);
if (inst->align16) {
fputs("regions\n| (Align16 region diagrams are not supported yet) |\n\n", fp);
return;
}
if (has_dst) {
fputs("dst\n", fp);
if (is_null(inst->dst)) {
fputs("| (null destination) |\n", fp);
} else if (inst->dst.indirect) {
fputs("| (indirect destination is not supported) |\n", fp);
} else {
region_diagram diag = {};
if (build_align1_dst_diagram(devinfo, inst, &diag))
print_region_diagram(fp, diag);
else
fputs("| (destination has no direct register bytes) |\n", fp);
}
fputc('\n', fp);
}
for (unsigned i = 0; i < num_sources; i++) {
char label[16];
snprintf(label, sizeof(label), "src%u", i);
const gen_operand &src = inst->src[i];
fprintf(fp, "%s\n", label);
if (src.file == GEN_IMM) {
fputs("| (immediate operand) |\n", fp);
} else if (src.indirect) {
fputs("| (indirect source is not supported) |\n", fp);
} else {
region_diagram diag = {};
if (build_align1_src_diagram(devinfo, inst, src, &diag))
print_region_diagram(fp, diag);
else
fputs("| (source has no direct register bytes) |\n", fp);
}
fputc('\n', fp);
}
}
static void
print_send_region_output(const intel_device_info *devinfo,
FILE *fp,
const gen_inst *inst)
{
const int dst_len = send_dst_len(inst);
const int src0_len = gen_inst_send_src0_len(inst);
const int src1_len = gen_inst_is_split_send(devinfo, inst) ?
gen_inst_send_src1_len(devinfo, inst) : 0;
fputs("dst\n", fp);
if (is_null(inst->dst)) {
fputs("| (null destination) |\n", fp);
} else if (dst_len < 0) {
fputs("| (response length comes from a descriptor register) |\n", fp);
} else {
region_diagram diag = {};
if (build_send_payload_diagram(devinfo, inst->dst, dst_len, &diag))
print_region_diagram(fp, diag);
else
fputs("| (destination payload has no direct register bytes) |\n", fp);
}
fputc('\n', fp);
fputs("src0\n", fp);
if (src0_len < 0) {
fputs("| (payload length comes from a descriptor register) |\n", fp);
} else {
region_diagram diag = {};
if (build_send_payload_diagram(devinfo, inst->src[0], src0_len, &diag))
print_region_diagram(fp, diag);
else
fputs("| (source payload has no direct register bytes) |\n", fp);
}
fputc('\n', fp);
if (gen_inst_is_split_send(devinfo, inst)) {
fputs("src1\n", fp);
if (is_null(inst->src[1])) {
fputs("| (null payload) |\n", fp);
} else if (src1_len < 0) {
fputs("| (extended payload length comes from an ex_desc register) |\n", fp);
} else {
region_diagram diag = {};
if (build_send_payload_diagram(devinfo, inst->src[1], src1_len, &diag))
print_region_diagram(fp, diag);
else
fputs("| (extended payload has no direct register bytes) |\n", fp);
}
fputc('\n', fp);
}
for (unsigned i = 0; i < 2; i++) {
const bool ex_desc = i != 0;
const bool is_reg = ex_desc ? inst->send.ex_desc_is_reg : inst->send.desc_is_reg;
if (!is_reg)
continue;
gen_operand op = {};
op.file = GEN_ARF;
op.nr = GEN_ARF_ADDRESS;
op.type = GEN_TYPE_UD;
op.subnr = ex_desc ? inst->send.ex_desc_subnr : 0;
fprintf(fp, "%s\n", send_desc_label(ex_desc));
region_diagram diag = {};
if (build_send_desc_diagram(devinfo, op, &diag))
print_region_diagram(fp, diag);
else
fputs("| (descriptor register is not a direct register operand) |\n", fp);
fputc('\n', fp);
}
}
static void
print_region_output(const intel_device_info *devinfo,
FILE *fp,
const gen_inst *inst)
{
switch (gen_inst_format(inst->opcode)) {
case GEN_FORMAT_SEND:
print_send_region_output(devinfo, fp, inst);
break;
case GEN_FORMAT_BASIC_ONE_SRC:
case GEN_FORMAT_BASIC_TWO_SRC:
case GEN_FORMAT_BASIC_THREE_SRC:
case GEN_FORMAT_DPAS_THREE_SRC:
print_regular_region_output(devinfo, fp, inst);
break;
case GEN_FORMAT_BRANCH_ONE_SRC:
case GEN_FORMAT_BRANCH_TWO_SRC:
case GEN_FORMAT_NOP:
case GEN_FORMAT_ILLEGAL:
fputs("regions\n| (instruction has no explicit register regions) |\n\n", fp);
break;
}
}
static void
init_devinfo(const char *platform, intel_device_info *devinfo)
{
const int pci_id = intel_device_name_to_pci_device_id(platform);
if (pci_id < 0)
failf("can't parse platform '%s', expected 3 letter platform name", platform);
if (!intel_get_device_info_from_pci_id(pci_id, devinfo))
failf("can't find device information for '%s'", platform);
if (devinfo->ver < 9)
failf("device has gfx version %d, but gentool currently requires gfx9+",
devinfo->ver);
}
static FILE *
open_output(const std::string &path, bool binary)
{
if (path == "-" || path.empty())
return stdout;
FILE *fp = fopen(path.c_str(), binary ? "wb" : "w");
if (!fp)
failf("failed to open output '%s': %s", path.c_str(), strerror(errno));
return fp;
}
static void
read_input(const std::string &path, std::string *out)
{
std::ifstream file;
if (path != "-" && !path.empty()) {
file.open(path, std::ios::binary);
if (!file)
failf("failed to open input '%s': %s", path.c_str(), strerror(errno));
}
std::istream &stream = file.is_open() ? file : std::cin;
std::ostringstream ss;
ss << stream.rdbuf();
*out = ss.str();
if (stream.bad())
failf("failed to read input '%s' (stream error)",
path == "-" ? "<stdin>" : path.c_str());
}
static void
write_output_bytes(const std::string &path, const void *data, size_t size)
{
FILE *output = open_output(path, true);
if (size > 0 && fwrite(data, 1, size, output) != size)
failf("failed to write binary output: %s", strerror(errno));
if (output == stdout) {
if (fflush(output) != 0)
failf("failed to flush output: %s", strerror(errno));
} else if (fclose(output) != 0) {
failf("failed to close output '%s': %s", path.c_str(), strerror(errno));
}
}
static void
print_parse_errors(const std::string &input_name,
const gen_error *errors,
int num_errors)
{
for (int i = 0; i < num_errors; i++) {
fprintf(stderr, "%s:%u: %s\n", input_name.c_str(),
errors[i].index, errors[i].msg);
}
}
static int
cmd_help(int argc, char **argv)
{
if (argc <= 1) {
print_top_help(stdout);
return 0;
}
if (argc > 2) {
fprintf(stderr, "gentool help: unexpected extra argument '%s'\n", argv[2]);
return 1;
}
const std::string sub = argv[1];
if (sub == "asm") {
print_asm_help(stdout);
return 0;
}
if (sub == "disasm") {
print_disasm_help(stdout);
return 0;
}
if (sub == "region") {
print_region_help(stdout);
return 0;
}
if (sub == "platforms") {
print_platforms_help(stdout);
return 0;
}
if (sub == "syntax") {
print_syntax_help(stdout);
return 0;
}
if (sub == "lsc") {
print_lsc_help(stdout);
return 0;
}
fprintf(stderr, "gentool: unknown help topic '%s'\n", argv[1]);
fprintf(stderr, "Available help topics: asm, disasm, region, platforms, syntax, lsc\n");
return 1;
}
static int
cmd_asm(int argc, char **argv)
{
std::string input_path = "-";
std::string output_path = "-";
intel_device_info devinfo = {};
bool have_devinfo = false;
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"platform", required_argument, NULL, 'p'},
{"gen", required_argument, NULL, 'p'},
{"input", required_argument, NULL, 'i'},
{"output", required_argument, NULL, 'o'},
{},
};
optind = 1;
opterr = 0;
while (true) {
const int c = getopt_long(argc, argv, "hp:i:o:", long_options, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
print_asm_help(stdout);
return 0;
case 'p': {
init_devinfo(optarg, &devinfo);
have_devinfo = true;
break;
}
case 'i':
input_path = optarg;
break;
case 'o':
output_path = optarg;
break;
case '?':
default:
failf("unknown option '%s'", argv[optind - 1]);
}
}
if (optind < argc) {
if (input_path != "-")
failf("input specified both by --input and positionally");
input_path = argv[optind++];
}
if (optind != argc)
failf("unexpected extra argument '%s'", argv[optind]);
if (!have_devinfo)
failf("missing required --platform option");
std::string text;
const std::string input_name = input_path == "-" ? "<stdin>" : input_path;
read_input(input_path, &text);
if (text.size() > INT_MAX)
failf("input is too large");
void *mem_ctx = ralloc_context(NULL);
gen_parse_params parse_params = {};
parse_params.devinfo = &devinfo;
parse_params.text = text.c_str();
parse_params.text_size = (int)text.size();
parse_params.mem_ctx = mem_ctx;
if (!gen_parse(&parse_params)) {
fprintf(stderr, "gentool asm: parse failed\n");
print_parse_errors(input_name, parse_params.errors, parse_params.num_errors);
ralloc_free(mem_ctx);
return 1;
}
if (parse_params.num_insts == 0) {
write_output_bytes(output_path, NULL, 0);
ralloc_free(mem_ctx);
return 0;
}
const int raw_bytes_size = parse_params.num_insts * (int)sizeof(gen_raw_inst);
gen_encode_params encode_params = {};
encode_params.devinfo = &devinfo;
encode_params.insts = parse_params.insts;
encode_params.num_insts = parse_params.num_insts;
encode_params.mem_ctx = mem_ctx;
encode_params.raw_bytes = rzalloc_size(mem_ctx, raw_bytes_size);
encode_params.raw_bytes_size = raw_bytes_size;
if (!gen_encode(&encode_params)) {
fprintf(stderr, "gentool asm: validation failed\n");
gen_print_params print_params = {};
print_params.devinfo = &devinfo;
print_params.fp = stderr;
print_params.flags = GEN_PRINT_VERBOSE;
print_params.insts = parse_params.insts;
print_params.num_insts = parse_params.num_insts;
print_params.errors = encode_params.errors;
print_params.num_errors = encode_params.num_errors;
gen_print(&print_params);
ralloc_free(mem_ctx);
return 1;
}
write_output_bytes(output_path, encode_params.raw_bytes,
encode_params.raw_bytes_size);
ralloc_free(mem_ctx);
return 0;
}
static int
cmd_disasm(int argc, char **argv)
{
std::string input_path = "-";
std::string output_path = "-";
intel_device_info devinfo = {};
bool have_devinfo = false;
gen_print_flags print_flags = GEN_PRINT_NONE;
enum { OPT_TRANSLATED_SENDS = 1000, OPT_HEX };
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"platform", required_argument, NULL, 'p'},
{"gen", required_argument, NULL, 'p'},
{"input", required_argument, NULL, 'i'},
{"output", required_argument, NULL, 'o'},
{"verbose", no_argument, NULL, 'v'},
{"translated-sends", no_argument, NULL, OPT_TRANSLATED_SENDS},
{"hex", no_argument, NULL, OPT_HEX},
{},
};
optind = 1;
opterr = 0;
while (true) {
const int c = getopt_long(argc, argv, "hp:i:o:v", long_options, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
print_disasm_help(stdout);
return 0;
case 'p': {
init_devinfo(optarg, &devinfo);
have_devinfo = true;
break;
}
case 'i':
input_path = optarg;
break;
case 'o':
output_path = optarg;
break;
case 'v':
print_flags = (gen_print_flags)(print_flags | GEN_PRINT_VERBOSE);
break;
case OPT_TRANSLATED_SENDS:
print_flags = (gen_print_flags)(print_flags | GEN_PRINT_TRANSLATED_SENDS);
break;
case OPT_HEX:
print_flags = (gen_print_flags)(print_flags | GEN_PRINT_HEX);
break;
case '?':
default:
failf("unknown option '%s'", argv[optind - 1]);
}
}
if (optind < argc) {
if (input_path != "-")
failf("input specified both by --input and positionally");
input_path = argv[optind++];
}
if (optind != argc)
failf("unexpected extra argument '%s'", argv[optind]);
if (!have_devinfo)
failf("missing required --platform option");
std::string raw_bytes;
read_input(input_path, &raw_bytes);
if (raw_bytes.size() > INT_MAX)
failf("input is too large");
if (raw_bytes.size() % 8 != 0)
failf("input size must be a multiple of 8 bytes");
if (raw_bytes.empty()) {
FILE *output = open_output(output_path, false);
if (fflush(output) != 0)
failf("failed to flush output: %s", strerror(errno));
if (output != stdout && fclose(output) != 0)
failf("failed to close output '%s': %s", output_path.c_str(), strerror(errno));
return 0;
}
void *mem_ctx = ralloc_context(NULL);
gen_decode_params decode_params = {};
decode_params.devinfo = &devinfo;
decode_params.raw_bytes = raw_bytes.data();
decode_params.raw_bytes_size = (int)raw_bytes.size();
decode_params.mem_ctx = mem_ctx;
if (!gen_decode(&decode_params)) {
ralloc_free(mem_ctx);
failf("decode failed");
}
gen_validate_params validate_params = {};
validate_params.devinfo = &devinfo;
validate_params.insts = decode_params.insts;
validate_params.num_insts = decode_params.num_insts;
validate_params.mem_ctx = mem_ctx;
const bool valid = gen_validate(&validate_params);
FILE *output = open_output(output_path, false);
gen_print_params print_params = {};
print_params.devinfo = &devinfo;
print_params.fp = output;
print_params.flags = print_flags;
print_params.insts = decode_params.insts;
print_params.num_insts = decode_params.num_insts;
print_params.errors = validate_params.errors;
print_params.num_errors = validate_params.num_errors;
print_params.raw_bytes = raw_bytes.data();
print_params.raw_bytes_size = (int)raw_bytes.size();
if (!gen_print(&print_params)) {
ralloc_free(mem_ctx);
failf("print failed");
}
if (fflush(output) != 0) {
ralloc_free(mem_ctx);
failf("failed to flush output: %s", strerror(errno));
}
if (output != stdout && fclose(output) != 0) {
ralloc_free(mem_ctx);
failf("failed to close output '%s': %s", output_path.c_str(), strerror(errno));
}
ralloc_free(mem_ctx);
return valid ? 0 : 1;
}
static int
cmd_region(int argc, char **argv)
{
intel_device_info devinfo = {};
bool have_devinfo = false;
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"platform", required_argument, NULL, 'p'},
{"gen", required_argument, NULL, 'p'},
{},
};
optind = 1;
opterr = 0;
while (true) {
const int c = getopt_long(argc, argv, "hp:", long_options, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
print_region_help(stdout);
return 0;
case 'p': {
init_devinfo(optarg, &devinfo);
have_devinfo = true;
break;
}
case '?':
default:
failf("unknown option '%s'", argv[optind - 1]);
}
}
if (optind != argc)
failf("unexpected extra argument '%s'", argv[optind]);
if (!have_devinfo)
failf("missing required --platform option");
std::string text;
read_input("-", &text);
if (text.size() > INT_MAX)
failf("input is too large");
void *mem_ctx = ralloc_context(NULL);
gen_parse_params parse_params = {};
parse_params.devinfo = &devinfo;
parse_params.text = text.c_str();
parse_params.text_size = (int)text.size();
parse_params.mem_ctx = mem_ctx;
if (!gen_parse(&parse_params)) {
fprintf(stderr, "gentool region: parse failed\n");
print_parse_errors("<stdin>", parse_params.errors, parse_params.num_errors);
ralloc_free(mem_ctx);
return 1;
}
if (parse_params.num_insts != 1) {
fprintf(stderr,
"gentool region: expected exactly one instruction, got %d\n",
parse_params.num_insts);
ralloc_free(mem_ctx);
return 1;
}
print_region_output(&devinfo, stdout, &parse_params.insts[0]);
ralloc_free(mem_ctx);
return 0;
}
} /* namespace */
int
main(int argc, char **argv)
{
struct command {
const char *name;
int (*func)(int argc, char **argv);
};
static const command cmds[] = {
{ "asm", cmd_asm },
{ "disasm", cmd_disasm },
{ "region", cmd_region },
{ "help", cmd_help },
};
if (argc <= 1) {
print_top_help(stderr);
return 1;
}
if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) {
print_top_help(stdout);
return 0;
}
const command *cmd = NULL;
for (const command *c = cmds; c < cmds + ARRAY_SIZE(cmds); c++) {
if (!strcmp(c->name, argv[1])) {
cmd = c;
break;
}
}
if (cmd)
return cmd->func(argc - 1, argv + 1);
fprintf(stderr, "gentool: unknown command '%s'\n", argv[1]);
print_top_help(stderr);
return 1;
}