/*
* Mesa 3-D graphics library
*
* Copyright (C) 2014 LunarG, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors:
* Chia-I Wu <olv@lunarg.com>
*/
#include <stdio.h>
#include "genhw/genhw.h"
#include "toy_compiler.h"
#define DISASM_PRINTER_BUFFER_SIZE 256
#define DISASM_PRINTER_COLUMN_WIDTH 16
struct disasm_printer {
char buf[DISASM_PRINTER_BUFFER_SIZE];
int len;
};
struct disasm_operand {
unsigned file:2;
unsigned type:4;
unsigned addr_mode:1;
unsigned reg:8;
unsigned subreg:5;
unsigned addr_subreg:3;
unsigned addr_imm:10;
};
struct disasm_dst_operand {
struct disasm_operand base;
unsigned horz_stride:2;
unsigned writemask:4;
};
struct disasm_src_operand {
struct disasm_operand base;
unsigned vert_stride:4;
unsigned width:3;
unsigned horz_stride:2;
unsigned swizzle_x:2;
unsigned swizzle_y:2;
unsigned swizzle_z:2;
unsigned swizzle_w:2;
unsigned negate:1;
unsigned absolute:1;
};
struct disasm_inst {
const struct ilo_dev *dev;
unsigned has_jip:1;
unsigned has_uip:1;
unsigned opcode:7;
unsigned access_mode:1;
unsigned mask_ctrl:1;
unsigned dep_ctrl:2;
unsigned qtr_ctrl:2;
unsigned thread_ctrl:2;
unsigned pred_ctrl:4;
unsigned pred_inv:1;
unsigned exec_size:3;
unsigned cond_modifier:4;
unsigned sfid:4;
unsigned fc:4;
unsigned acc_wr_ctrl:1;
unsigned branch_ctrl:1;
unsigned cmpt_ctrl:1;
unsigned debug_ctrl:1;
unsigned saturate:1;
unsigned nib_ctrl:1;
unsigned flag_reg:1;
unsigned flag_subreg:1;
struct disasm_dst_operand dst;
struct disasm_src_operand src0;
struct disasm_src_operand src1;
union {
struct disasm_src_operand src2;
uint64_t imm64;
uint32_t ud;
int32_t d;
uint16_t uw;
int16_t w;
float f;
struct {
int16_t jip;
int16_t uip;
} ip16;
struct {
int32_t jip;
int32_t uip;
} ip32;
} u;
};
static const struct {
const char *name;
int src_count;
} disasm_opcode_table[128] = {
[GEN6_OPCODE_ILLEGAL] = { "illegal", 0 },
[GEN6_OPCODE_MOV] = { "mov", 1 },
[GEN6_OPCODE_SEL] = { "sel", 2 },
[GEN6_OPCODE_MOVI] = { "movi", 1 },
[GEN6_OPCODE_NOT] = { "not", 1 },
[GEN6_OPCODE_AND] = { "and", 2 },
[GEN6_OPCODE_OR] = { "or", 2 },
[GEN6_OPCODE_XOR] = { "xor", 2 },
[GEN6_OPCODE_SHR] = { "shr", 2 },
[GEN6_OPCODE_SHL] = { "shl", 2 },
[GEN6_OPCODE_DIM] = { "dim", 1 },
[GEN6_OPCODE_ASR] = { "asr", 2 },
[GEN6_OPCODE_CMP] = { "cmp", 2 },
[GEN6_OPCODE_CMPN] = { "cmpn", 2 },
[GEN7_OPCODE_CSEL] = { "csel", 3 },
[GEN7_OPCODE_F32TO16] = { "f32to16", 1 },
[GEN7_OPCODE_F16TO32] = { "f16to32", 1 },
[GEN7_OPCODE_BFREV] = { "bfrev", 1 },
[GEN7_OPCODE_BFE] = { "bfe", 3 },
[GEN7_OPCODE_BFI1] = { "bfi1", 2 },
[GEN7_OPCODE_BFI2] = { "bfi2", 3 },
[GEN6_OPCODE_JMPI] = { "jmpi", 1 },
[GEN7_OPCODE_BRD] = { "brd", 1 },
[GEN6_OPCODE_IF] = { "if", 2 },
[GEN7_OPCODE_BRC] = { "brc", 1 },
[GEN6_OPCODE_ELSE] = { "else", 1 },
[GEN6_OPCODE_ENDIF] = { "endif", 0 },
[GEN6_OPCODE_CASE] = { "case", 2 },
[GEN6_OPCODE_WHILE] = { "while", 1 },
[GEN6_OPCODE_BREAK] = { "break", 1 },
[GEN6_OPCODE_CONT] = { "cont", 1 },
[GEN6_OPCODE_HALT] = { "halt", 1 },
[GEN75_OPCODE_CALLA] = { "calla", 1 },
[GEN6_OPCODE_CALL] = { "call", 1 },
[GEN6_OPCODE_RETURN] = { "return", 1 },
[GEN8_OPCODE_GOTO] = { "goto", 1 },
[GEN6_OPCODE_WAIT] = { "wait", 1 },
[GEN6_OPCODE_SEND] = { "send", 1 },
[GEN6_OPCODE_SENDC] = { "sendc", 1 },
[GEN6_OPCODE_MATH] = { "math", 2 },
[GEN6_OPCODE_ADD] = { "add", 2 },
[GEN6_OPCODE_MUL] = { "mul", 2 },
[GEN6_OPCODE_AVG] = { "avg", 2 },
[GEN6_OPCODE_FRC] = { "frc", 1 },
[GEN6_OPCODE_RNDU] = { "rndu", 1 },
[GEN6_OPCODE_RNDD] = { "rndd", 1 },
[GEN6_OPCODE_RNDE] = { "rnde", 1 },
[GEN6_OPCODE_RNDZ] = { "rndz", 1 },
[GEN6_OPCODE_MAC] = { "mac", 2 },
[GEN6_OPCODE_MACH] = { "mach", 2 },
[GEN6_OPCODE_LZD] = { "lzd", 1 },
[GEN7_OPCODE_FBH] = { "fbh", 1 },
[GEN7_OPCODE_FBL] = { "fbl", 1 },
[GEN7_OPCODE_CBIT] = { "cbit", 1 },
[GEN7_OPCODE_ADDC] = { "addc", 2 },
[GEN7_OPCODE_SUBB] = { "subb", 2 },
[GEN6_OPCODE_SAD2] = { "sad2", 2 },
[GEN6_OPCODE_SADA2] = { "sada2", 2 },
[GEN6_OPCODE_DP4] = { "dp4", 2 },
[GEN6_OPCODE_DPH] = { "dph", 2 },
[GEN6_OPCODE_DP3] = { "dp3", 2 },
[GEN6_OPCODE_DP2] = { "dp2", 2 },
[GEN6_OPCODE_LINE] = { "line", 2 },
[GEN6_OPCODE_PLN] = { "pln", 2 },
[GEN6_OPCODE_MAD] = { "mad", 3 },
[GEN6_OPCODE_LRP] = { "lrp", 3 },
[GEN6_OPCODE_NOP] = { "nop", 0 },
};
static void
disasm_inst_decode_dw0_opcode_gen6(struct disasm_inst *inst, uint32_t dw0)
{
ILO_DEV_ASSERT(inst->dev, 6, 8);
inst->opcode = GEN_EXTRACT(dw0, GEN6_INST_OPCODE);
switch (inst->opcode) {
case GEN6_OPCODE_IF:
inst->has_jip = true;
inst->has_uip = (ilo_dev_gen(inst->dev) >= ILO_GEN(7));
break;
case GEN6_OPCODE_ELSE:
inst->has_jip = true;
inst->has_uip = (ilo_dev_gen(inst->dev) >= ILO_GEN(8));
break;
case GEN6_OPCODE_BREAK:
case GEN6_OPCODE_CONT:
case GEN6_OPCODE_HALT:
inst->has_uip = true;
/* fall through */
case GEN6_OPCODE_JMPI:
case GEN7_OPCODE_BRD:
case GEN7_OPCODE_BRC:
case GEN6_OPCODE_ENDIF:
case GEN6_OPCODE_CASE:
case GEN6_OPCODE_WHILE:
case GEN75_OPCODE_CALLA:
case GEN6_OPCODE_CALL:
case GEN6_OPCODE_RETURN:
inst->has_jip = true;
break;
default:
break;
}
}
static void
disasm_inst_decode_dw0_gen6(struct disasm_inst *inst, uint32_t dw0)
{
ILO_DEV_ASSERT(inst->dev, 6, 8);
disasm_inst_decode_dw0_opcode_gen6(inst, dw0);
inst->access_mode = GEN_EXTRACT(dw0, GEN6_INST_ACCESSMODE);
if (ilo_dev_gen(inst->dev) >= ILO_GEN(8)) {
inst->dep_ctrl = GEN_EXTRACT(dw0, GEN8_INST_DEPCTRL);
inst->nib_ctrl = (bool) (dw0 & GEN8_INST_NIBCTRL);
} else {
inst->mask_ctrl = GEN_EXTRACT(dw0, GEN6_INST_MASKCTRL);
inst->dep_ctrl = GEN_EXTRACT(dw0, GEN6_INST_DEPCTRL);
}
inst->qtr_ctrl = GEN_EXTRACT(dw0, GEN6_INST_QTRCTRL);
inst->thread_ctrl = GEN_EXTRACT(dw0, GEN6_INST_THREADCTRL);
inst->pred_ctrl = GEN_EXTRACT(dw0, GEN6_INST_PREDCTRL);
inst->pred_inv = (bool) (dw0 & GEN6_INST_PREDINV);
inst->exec_size = GEN_EXTRACT(dw0, GEN6_INST_EXECSIZE);
switch (inst->opcode) {
case GEN6_OPCODE_SEND:
case GEN6_OPCODE_SENDC:
inst->sfid = GEN_EXTRACT(dw0, GEN6_INST_SFID);
break;
case GEN6_OPCODE_MATH:
inst->fc = GEN_EXTRACT(dw0, GEN6_INST_FC);
break;
default:
inst->cond_modifier = GEN_EXTRACT(dw0, GEN6_INST_CONDMODIFIER);
break;
}
switch (inst->opcode) {
case GEN6_OPCODE_IF:
case GEN6_OPCODE_ELSE:
case GEN8_OPCODE_GOTO:
if (ilo_dev_gen(inst->dev) >= ILO_GEN(8)) {
inst->branch_ctrl = (bool) (dw0 & GEN8_INST_BRANCHCTRL);
break;
}
default:
inst->acc_wr_ctrl = (bool) (dw0 & GEN6_INST_ACCWRCTRL);
break;
}
inst->cmpt_ctrl = (bool) (dw0 & GEN6_INST_CMPTCTRL);
inst->debug_ctrl = (bool) (dw0 & GEN6_INST_DEBUGCTRL);
inst->saturate = (bool) (dw0 & GEN6_INST_SATURATE);
}
static void
disasm_inst_decode_dw1_low_gen6(struct disasm_inst *inst, uint32_t dw1)
{
ILO_DEV_ASSERT(inst->dev, 6, 7.5);
inst->dst.base.file = GEN_EXTRACT(dw1, GEN6_INST_DST_FILE);
inst->dst.base.type = GEN_EXTRACT(dw1, GEN6_INST_DST_TYPE);
inst->src0.base.file = GEN_EXTRACT(dw1, GEN6_INST_SRC0_FILE);
inst->src0.base.type = GEN_EXTRACT(dw1, GEN6_INST_SRC0_TYPE);
inst->src1.base.file = GEN_EXTRACT(dw1, GEN6_INST_SRC1_FILE);
inst->src1.base.type = GEN_EXTRACT(dw1, GEN6_INST_SRC1_TYPE);
if (ilo_dev_gen(inst->dev) >= ILO_GEN(7))
inst->nib_ctrl = (bool) (dw1 & GEN7_INST_NIBCTRL);
}
static void
disasm_inst_decode_dw1_low_gen8(struct disasm_inst *inst, uint32_t dw1)
{
ILO_DEV_ASSERT(inst->dev, 8, 8);
inst->flag_subreg = GEN_EXTRACT(dw1, GEN8_INST_FLAG_SUBREG);
inst->flag_reg = GEN_EXTRACT(dw1, GEN8_INST_FLAG_REG);
inst->mask_ctrl = GEN_EXTRACT(dw1, GEN8_INST_MASKCTRL);
inst->dst.base.file = GEN_EXTRACT(dw1, GEN8_INST_DST_FILE);
inst->dst.base.type = GEN_EXTRACT(dw1, GEN8_INST_DST_TYPE);
inst->src0.base.file = GEN_EXTRACT(dw1, GEN8_INST_SRC0_FILE);
inst->src0.base.type = GEN_EXTRACT(dw1, GEN8_INST_SRC0_TYPE);
inst->dst.base.addr_imm = GEN_EXTRACT(dw1, GEN8_INST_DST_ADDR_IMM_BIT9) <<
GEN8_INST_DST_ADDR_IMM_BIT9__SHR;
}
static void
disasm_inst_decode_dw1_high_gen6(struct disasm_inst *inst, uint32_t dw1)
{
ILO_DEV_ASSERT(inst->dev, 6, 8);
inst->dst.base.addr_mode = GEN_EXTRACT(dw1, GEN6_INST_DST_ADDRMODE);
if (inst->dst.base.addr_mode == GEN6_ADDRMODE_DIRECT) {
inst->dst.base.reg = GEN_EXTRACT(dw1, GEN6_INST_DST_REG);
if (inst->access_mode == GEN6_ALIGN_1) {
inst->dst.base.subreg = GEN_EXTRACT(dw1, GEN6_INST_DST_SUBREG);
} else {
inst->dst.base.subreg =
GEN_EXTRACT(dw1, GEN6_INST_DST_SUBREG_ALIGN16) <<
GEN6_INST_DST_SUBREG_ALIGN16__SHR;
}
} else {
if (ilo_dev_gen(inst->dev) >= ILO_GEN(8)) {
inst->dst.base.addr_subreg =
GEN_EXTRACT(dw1, GEN8_INST_DST_ADDR_SUBREG);
/* bit 9 is already set in disasm_inst_decode_dw1_low_gen8() */
if (inst->access_mode == GEN6_ALIGN_1) {
inst->dst.base.addr_imm |=
GEN_EXTRACT(dw1, GEN8_INST_DST_ADDR_IMM);
} else {
inst->dst.base.addr_imm |=
GEN_EXTRACT(dw1, GEN8_INST_DST_ADDR_IMM_ALIGN16) <<
GEN8_INST_DST_ADDR_IMM_ALIGN16__SHR;
}
} else {
inst->dst.base.addr_subreg =
GEN_EXTRACT(dw1, GEN6_INST_DST_ADDR_SUBREG);
if (inst->access_mode == GEN6_ALIGN_1) {
inst->dst.base.addr_imm =
GEN_EXTRACT(dw1, GEN6_INST_DST_ADDR_IMM);
} else {
inst->dst.base.addr_imm =
GEN_EXTRACT(dw1, GEN6_INST_DST_ADDR_IMM_ALIGN16) <<
GEN6_INST_DST_ADDR_IMM_ALIGN16__SHR;
}
}
}
inst->dst.horz_stride = GEN_EXTRACT(dw1, GEN6_INST_DST_HORZSTRIDE);
if (inst->access_mode == GEN6_ALIGN_1)
inst->dst.writemask = 0xf;
else
inst->dst.writemask = GEN_EXTRACT(dw1, GEN6_INST_DST_WRITEMASK);
}
static void
disasm_inst_decode_dw1_gen6(struct disasm_inst *inst, uint32_t dw1)
{
ILO_DEV_ASSERT(inst->dev, 6, 8);
if (ilo_dev_gen(inst->dev) >= ILO_GEN(8))
disasm_inst_decode_dw1_low_gen8(inst, dw1);
else
disasm_inst_decode_dw1_low_gen6(inst, dw1);
if (ilo_dev_gen(inst->dev) == ILO_GEN(6) &&
inst->has_jip && !inst->has_uip)
inst->u.imm64 = dw1 >> 16;
else
disasm_inst_decode_dw1_high_gen6(inst, dw1);
}
static void
disasm_inst_decode_dw2_dw3_gen6(struct disasm_inst *inst,
uint32_t dw2, uint32_t dw3)
{
int imm_bits = 0, count, i;
ILO_DEV_ASSERT(inst->dev, 6, 8);
if (ilo_dev_gen(inst->dev) >= ILO_GEN(8)) {
/* how about real 64-bit immediates? */
if (inst->has_uip) {
imm_bits = 64;
inst->src1.base.file = GEN6_FILE_IMM;
inst->src1.base.type = GEN6_TYPE_D;
} else {
inst->src1.base.file = GEN_EXTRACT(dw2, GEN8_INST_SRC1_FILE);
inst->src1.base.type = GEN_EXTRACT(dw2, GEN8_INST_SRC1_TYPE);
if (inst->src0.base.file == GEN6_FILE_IMM ||
inst->src1.base.file == GEN6_FILE_IMM)
imm_bits = 32;
}
} else {
if (ilo_dev_gen(inst->dev) >= ILO_GEN(7))
inst->flag_reg = GEN_EXTRACT(dw2, GEN7_INST_FLAG_REG);
inst->flag_subreg = GEN_EXTRACT(dw2, GEN6_INST_FLAG_SUBREG);
if (inst->src0.base.file == GEN6_FILE_IMM ||
inst->src1.base.file == GEN6_FILE_IMM)
imm_bits = 32;
}
switch (imm_bits) {
case 32:
inst->u.imm64 = dw3;
count = 1;
break;
case 64:
inst->u.imm64 = (uint64_t) dw2 << 32 | dw3;
count = 0;
break;
default:
count = 2;
break;
}
for (i = 0; i < count; i++) {
struct disasm_src_operand *src = (i == 0) ? &inst->src0 : &inst->src1;
const uint32_t dw = (i == 0) ? dw2 : dw3;
src->base.addr_mode = GEN_EXTRACT(dw, GEN6_INST_SRC_ADDRMODE);
if (src->base.addr_mode == GEN6_ADDRMODE_DIRECT) {
src->base.reg = GEN_EXTRACT(dw, GEN6_INST_SRC_REG);
if (inst->access_mode == GEN6_ALIGN_1) {
src->base.subreg = GEN_EXTRACT(dw, GEN6_INST_SRC_SUBREG);
} else {
src->base.subreg = GEN_EXTRACT(dw, GEN6_INST_SRC_SUBREG_ALIGN16) <<
GEN6_INST_SRC_SUBREG_ALIGN16__SHR;
}
} else {
if (ilo_dev_gen(inst->dev) >= ILO_GEN(8)) {
src->base.addr_subreg =
GEN_EXTRACT(dw, GEN8_INST_SRC_ADDR_SUBREG);
if (inst->access_mode == GEN6_ALIGN_1) {
src->base.addr_imm = GEN_EXTRACT(dw, GEN8_INST_SRC_ADDR_IMM);
} else {
src->base.addr_imm =
GEN_EXTRACT(dw, GEN8_INST_SRC_ADDR_IMM_ALIGN16) <<
GEN8_INST_SRC_ADDR_IMM_ALIGN16__SHR;
}
if (i == 0) {
inst->dst.base.addr_imm |= GEN_EXTRACT(dw,
GEN8_INST_SRC0_ADDR_IMM_BIT9) <<
GEN8_INST_SRC0_ADDR_IMM_BIT9__SHR;
} else {
inst->dst.base.addr_imm |= GEN_EXTRACT(dw,
GEN8_INST_SRC1_ADDR_IMM_BIT9) <<
GEN8_INST_SRC1_ADDR_IMM_BIT9__SHR;
}
} else {
src->base.addr_subreg =
GEN_EXTRACT(dw, GEN6_INST_SRC_ADDR_SUBREG);
if (inst->access_mode == GEN6_ALIGN_1) {
src->base.addr_imm = GEN_EXTRACT(dw, GEN6_INST_SRC_ADDR_IMM);
} else {
src->base.addr_imm =
GEN_EXTRACT(dw, GEN6_INST_SRC_ADDR_IMM_ALIGN16) <<
GEN6_INST_SRC_ADDR_IMM_ALIGN16__SHR;
}
}
}
src->vert_stride = GEN_EXTRACT(dw, GEN6_INST_SRC_VERTSTRIDE);
if (inst->access_mode == GEN6_ALIGN_1) {
src->width = GEN_EXTRACT(dw, GEN6_INST_SRC_WIDTH);
src->horz_stride = GEN_EXTRACT(dw, GEN6_INST_SRC_HORZSTRIDE);
src->swizzle_x = GEN6_SWIZZLE_X;
src->swizzle_y = GEN6_SWIZZLE_Y;
src->swizzle_z = GEN6_SWIZZLE_Z;
src->swizzle_w = GEN6_SWIZZLE_W;
} else {
src->width = GEN6_WIDTH_4;
src->horz_stride = GEN6_HORZSTRIDE_1;
src->swizzle_x = GEN_EXTRACT(dw, GEN6_INST_SRC_SWIZZLE_X);
src->swizzle_y = GEN_EXTRACT(dw, GEN6_INST_SRC_SWIZZLE_Y);
src->swizzle_z = GEN_EXTRACT(dw, GEN6_INST_SRC_SWIZZLE_Z);
src->swizzle_w = GEN_EXTRACT(dw, GEN6_INST_SRC_SWIZZLE_W);
}
src->negate = (bool) (dw & GEN6_INST_SRC_NEGATE);
src->absolute = (bool) (dw & GEN6_INST_SRC_ABSOLUTE);
}
}
static void
disasm_inst_decode_3src_dw1_gen6(struct disasm_inst *inst, uint32_t dw1)
{
static const unsigned type_mapping[4] = {
[GEN7_TYPE_F_3SRC] = GEN6_TYPE_F,
[GEN7_TYPE_D_3SRC] = GEN6_TYPE_D,
[GEN7_TYPE_UD_3SRC] = GEN6_TYPE_UD,
[GEN7_TYPE_DF_3SRC] = GEN7_TYPE_DF,
};
ILO_DEV_ASSERT(inst->dev, 6, 7.5);
inst->flag_subreg = GEN_EXTRACT(dw1, GEN6_3SRC_FLAG_SUBREG);
if (ilo_dev_gen(inst->dev) >= ILO_GEN(7)) {
inst->nib_ctrl = (bool) (dw1 & GEN7_3SRC_NIBCTRL);
inst->flag_reg = GEN_EXTRACT(dw1, GEN7_3SRC_FLAG_REG);
inst->dst.base.file = GEN6_FILE_GRF;
inst->dst.base.type = GEN_EXTRACT(dw1, GEN7_3SRC_DST_TYPE);
inst->dst.base.type = type_mapping[inst->dst.base.type];
inst->src0.base.type = GEN_EXTRACT(dw1, GEN7_3SRC_SRC_TYPE);
inst->src0.base.type = type_mapping[inst->src0.base.type];
inst->src1.base.type = inst->src0.base.type;
inst->u.src2.base.type = inst->src0.base.type;
} else {
inst->dst.base.file = (dw1 & GEN6_3SRC_DST_FILE_MRF) ?
GEN6_FILE_MRF: GEN6_FILE_GRF;
inst->dst.base.type = GEN6_TYPE_F;
inst->src0.base.type = GEN6_TYPE_F;
inst->src1.base.type = GEN6_TYPE_F;
inst->u.src2.base.type = GEN6_TYPE_F;
}
inst->dst.base.addr_mode = GEN6_ADDRMODE_DIRECT;
inst->dst.base.reg = GEN_EXTRACT(dw1, GEN6_3SRC_DST_REG);
inst->dst.base.subreg = GEN_EXTRACT(dw1, GEN6_3SRC_DST_SUBREG) <<
GEN6_3SRC_DST_SUBREG__SHR;
inst->dst.horz_stride = GEN6_HORZSTRIDE_1;
inst->dst.writemask = GEN_EXTRACT(dw1, GEN6_3SRC_DST_WRITEMASK);
inst->src0.base.file = GEN6_FILE_GRF;
inst->src0.negate = (bool) (dw1 & GEN6_3SRC_SRC0_NEGATE);
inst->src0.absolute = (bool) (dw1 & GEN6_3SRC_SRC0_ABSOLUTE);
inst->src1.base.file = GEN6_FILE_GRF;
inst->src1.negate = (bool) (dw1 & GEN6_3SRC_SRC1_NEGATE);
inst->src1.absolute = (bool) (dw1 & GEN6_3SRC_SRC1_ABSOLUTE);
inst->u.src2.base.file = GEN6_FILE_GRF;
inst->u.src2.negate = (bool) (dw1 & GEN6_3SRC_SRC2_NEGATE);
inst->u.src2.absolute = (bool) (dw1 & GEN6_3SRC_SRC2_ABSOLUTE);
}
static void
disasm_inst_decode_3src_dw1_gen8(struct disasm_inst *inst, uint32_t dw1)
{
static const unsigned type_mapping[8] = {
[GEN7_TYPE_F_3SRC] = GEN6_TYPE_F,
[GEN7_TYPE_D_3SRC] = GEN6_TYPE_D,
[GEN7_TYPE_UD_3SRC] = GEN6_TYPE_UD,
[GEN7_TYPE_DF_3SRC] = GEN7_TYPE_DF,
/* map unknown types to unknown types */
[0x4] = 0xf,
[0x5] = 0xf,
[0x6] = 0xf,
[0x7] = 0xf,
};
ILO_DEV_ASSERT(inst->dev, 8, 8);
inst->flag_subreg = GEN_EXTRACT(dw1, GEN8_3SRC_FLAG_SUBREG);
inst->flag_reg = GEN_EXTRACT(dw1, GEN8_3SRC_FLAG_REG);
inst->mask_ctrl = GEN_EXTRACT(dw1, GEN8_3SRC_MASKCTRL);
inst->src0.absolute = (bool) (dw1 & GEN8_3SRC_SRC0_ABSOLUTE);
inst->src0.negate = (bool) (dw1 & GEN8_3SRC_SRC0_NEGATE);
inst->src1.negate = (bool) (dw1 & GEN8_3SRC_SRC1_NEGATE);
inst->src1.absolute = (bool) (dw1 & GEN8_3SRC_SRC1_ABSOLUTE);
inst->u.src2.negate = (bool) (dw1 & GEN8_3SRC_SRC2_NEGATE);
inst->u.src2.absolute = (bool) (dw1 & GEN8_3SRC_SRC2_ABSOLUTE);
inst->src0.base.file = GEN6_FILE_GRF;
inst->src0.base.type = GEN_EXTRACT(dw1, GEN8_3SRC_SRC_TYPE);
inst->src0.base.type = type_mapping[inst->src0.base.type];
inst->src1.base.file = GEN6_FILE_GRF;
inst->src1.base.type = inst->src0.base.type;
inst->u.src2.base.file = GEN6_FILE_GRF;
inst->u.src2.base.type = inst->src0.base.type;
inst->dst.base.file = GEN6_FILE_GRF;
inst->dst.base.type = GEN_EXTRACT(dw1, GEN8_3SRC_DST_TYPE);
inst->dst.base.type = type_mapping[inst->dst.base.type];
inst->dst.base.addr_mode = GEN6_ADDRMODE_DIRECT;
inst->dst.horz_stride = GEN6_HORZSTRIDE_1;
inst->dst.writemask = GEN_EXTRACT(dw1, GEN6_3SRC_DST_WRITEMASK);
inst->dst.base.subreg = GEN_EXTRACT(dw1, GEN6_3SRC_DST_SUBREG) <<
GEN6_3SRC_DST_SUBREG__SHR;
inst->dst.base.reg = GEN_EXTRACT(dw1, GEN6_3SRC_DST_REG);
}
static void
disasm_inst_decode_3src_dw2_dw3_gen6(struct disasm_inst *inst,
uint32_t dw2, uint32_t dw3)
{
const uint64_t qw = (uint64_t) dw3 << 32 | dw2;
int i;
ILO_DEV_ASSERT(inst->dev, 6, 8);
for (i = 0; i < 3; i++) {
struct disasm_src_operand *src = (i == 0) ? &inst->src0 :
(i == 1) ? &inst->src1 :
&inst->u.src2;
const uint32_t dw = (i == 0) ? GEN_EXTRACT(qw, GEN6_3SRC_SRC_0) :
(i == 1) ? GEN_EXTRACT(qw, GEN6_3SRC_SRC_1) :
GEN_EXTRACT(qw, GEN6_3SRC_SRC_2);
src->base.addr_mode = GEN6_ADDRMODE_DIRECT;
src->base.reg = GEN_EXTRACT(dw, GEN6_3SRC_SRC_REG);
src->base.subreg = GEN_EXTRACT(dw, GEN6_3SRC_SRC_SUBREG) <<
GEN6_3SRC_SRC_SUBREG__SHR;
if (dw & GEN6_3SRC_SRC_REPCTRL) {
src->vert_stride = GEN6_VERTSTRIDE_0;
src->width = GEN6_WIDTH_1;
src->horz_stride = GEN6_HORZSTRIDE_0;
} else {
src->vert_stride = GEN6_VERTSTRIDE_4;
src->width = GEN6_WIDTH_4;
src->horz_stride = GEN6_HORZSTRIDE_1;
}
src->swizzle_x = GEN_EXTRACT(dw, GEN6_3SRC_SRC_SWIZZLE_X);
src->swizzle_y = GEN_EXTRACT(dw, GEN6_3SRC_SRC_SWIZZLE_Y);
src->swizzle_z = GEN_EXTRACT(dw, GEN6_3SRC_SRC_SWIZZLE_Z);
src->swizzle_w = GEN_EXTRACT(dw, GEN6_3SRC_SRC_SWIZZLE_W);
}
}
/*
* When GEN6_INST_CMPTCTRL of DW0 is set, the instruction has 64 bits and is
* in EU_INSTRUCTION_COMPACT_TWO_SRC form. We should have expanded it to its
* original form.
*
* Depending on the opcode, the 128-bits instruction is in one of the
* following forms
*
* - EU_INSTRUCTION_BASIC_ONE_SRC
* - EU_INSTRUCTION_BASIC_TWO_SRC
* - EU_INSTRUCTION_BASIC_THREE_SRC
* - EU_INSTRUCTION_BRANCH_CONDITIONAL
* - EU_INSTRUCTION_BRANCH_ONE_SRC
* - EU_INSTRUCTION_BRANCH_TWO_SRC
* - EU_INSTRUCTION_ILLEGAL
* - EU_INSTRUCTION_MATH
* - EU_INSTRUCTION_NOP
* - EU_INSTRUCTION_SEND
*
* In EU_INSTRUCTION_BASIC_ONE_SRC form,
*
* - DW0 is EU_INSTRUCTION_HEADER
* - DW1 is EU_INSTRUCTION_OPERAND_CONTROLS
* - DW2 is Source 0 and EU_INSTRUCTION_FLAGS
* - DW3 is reserved unless Source 0 is an immediate
*
* All other forms except EU_INSTRUCTION_BASIC_THREE_SRC are quite compatible
* with EU_INSTRUCTION_BASIC_ONE_SRC.
*/
static void
disasm_inst_decode(struct disasm_inst *inst,
const uint32_t *dw)
{
assert(!(dw
[0] & GEN6_INST_CMPTCTRL
));
disasm_inst_decode_dw0_gen6(inst, dw[0]);
if (disasm_opcode_table[inst->opcode].src_count == 3) {
if (ilo_dev_gen(inst->dev) >= ILO_GEN(8))
disasm_inst_decode_3src_dw1_gen8(inst, dw[1]);
else
disasm_inst_decode_3src_dw1_gen6(inst, dw[1]);
disasm_inst_decode_3src_dw2_dw3_gen6(inst, dw[2], dw[3]);
} else {
disasm_inst_decode_dw1_gen6(inst, dw[1]);
disasm_inst_decode_dw2_dw3_gen6(inst, dw[2], dw[3]);
}
}
static const char *
disasm_inst_opcode(const struct disasm_inst *inst)
{
return (disasm_opcode_table[inst->opcode].name) ?
disasm_opcode_table[inst->opcode].name : "BAD";
}
static const char *
disasm_inst_pred_ctrl(const struct disasm_inst *inst)
{
if (inst->access_mode == GEN6_ALIGN_1) {
switch (inst->pred_ctrl) {
case GEN6_PREDCTRL_NORMAL: return "";
case GEN6_PREDCTRL_ANYV: return ".anyv";
case GEN6_PREDCTRL_ALLV: return ".allv";
case GEN6_PREDCTRL_ANY2H: return ".any2h";
case GEN6_PREDCTRL_ALL2H: return ".all2h";
case GEN6_PREDCTRL_ANY4H: return ".any4h";
case GEN6_PREDCTRL_ALL4H: return ".all4h";
case GEN6_PREDCTRL_ANY8H: return ".any8h";
case GEN6_PREDCTRL_ALL8H: return ".all8h";
case GEN6_PREDCTRL_ANY16H: return ".any16h";
case GEN6_PREDCTRL_ALL16H: return ".all16h";
case GEN7_PREDCTRL_ANY32H: return ".any32h";
case GEN7_PREDCTRL_ALL32H: return ".all32h";
default: return ".BAD";
}
} else {
switch (inst->pred_ctrl) {
case GEN6_PREDCTRL_NORMAL: return "";
case GEN6_PREDCTRL_X: return ".x";
case GEN6_PREDCTRL_Y: return ".y";
case GEN6_PREDCTRL_Z: return ".z";
case GEN6_PREDCTRL_W: return ".w";
default: return ".BAD";
}
}
}
static char
disasm_inst_pred_inv(const struct disasm_inst *inst)
{
return (inst->pred_inv) ? '-' : '+';
}
static const char *
disasm_inst_exec_size(const struct disasm_inst *inst)
{
switch (inst->exec_size) {
case GEN6_EXECSIZE_1: return "1";
case GEN6_EXECSIZE_2: return "2";
case GEN6_EXECSIZE_4: return "4";
case GEN6_EXECSIZE_8: return "8";
case GEN6_EXECSIZE_16: return "16";
case GEN6_EXECSIZE_32: return "32";
default: return "BAD";
}
}
static const char *
disasm_inst_fc(const struct disasm_inst *inst)
{
assert(inst
->opcode
== GEN6_OPCODE_MATH
);
switch (inst->fc) {
case GEN6_MATH_INV: return "inv";
case GEN6_MATH_LOG: return "log";
case GEN6_MATH_EXP: return "exp";
case GEN6_MATH_SQRT: return "sqrt";
case GEN6_MATH_RSQ: return "rsq";
case GEN6_MATH_SIN: return "sin";
case GEN6_MATH_COS: return "cos";
case GEN6_MATH_FDIV: return "fdiv";
case GEN6_MATH_POW: return "pow";
case GEN6_MATH_INT_DIV: return "int_div";
case GEN6_MATH_INT_DIV_QUOTIENT: return "int_div_quotient";
case GEN6_MATH_INT_DIV_REMAINDER: return "int_div_remainder";
case GEN8_MATH_INVM: return "invm";
case GEN8_MATH_RSQRTM: return "rsqrtm";
default: return "BAD";
}
}
static const char *
disasm_inst_sfid(const struct disasm_inst *inst)
{
assert(inst
->opcode
== GEN6_OPCODE_SEND
||
inst->opcode == GEN6_OPCODE_SENDC);
switch (inst->sfid) {
case GEN6_SFID_NULL: return "null";
case GEN6_SFID_SAMPLER: return "sampler";
case GEN6_SFID_GATEWAY: return "gateway";
case GEN6_SFID_DP_SAMPLER: return "dp sampler";
case GEN6_SFID_DP_RC: return "dp render";
case GEN6_SFID_URB: return "urb";
case GEN6_SFID_SPAWNER: return "spawner";
case GEN6_SFID_VME: return "vme";
case GEN6_SFID_DP_CC: return "dp const";
case GEN7_SFID_DP_DC0: return "dp data 0";
case GEN7_SFID_PI: return "pixel interp";
case GEN75_SFID_DP_DC1: return "dp data 1";
default: return "BAD";
}
}
static const char *
disasm_inst_cond_modifier(const struct disasm_inst *inst)
{
switch (inst->cond_modifier) {
case GEN6_COND_NONE: return "";
case GEN6_COND_Z: return ".z";
case GEN6_COND_NZ: return ".nz";
case GEN6_COND_G: return ".g";
case GEN6_COND_GE: return ".ge";
case GEN6_COND_L: return ".l";
case GEN6_COND_LE: return ".le";
case GEN6_COND_O: return ".o";
case GEN6_COND_U: return ".u";
default: return ".BAD";
}
}
static const char *
disasm_inst_debug_ctrl(const struct disasm_inst *inst)
{
return (inst->debug_ctrl) ? ".breakpoint" : "";
}
static const char *
disasm_inst_saturate(const struct disasm_inst *inst)
{
return (inst->saturate) ? ".sat" : "";
}
static const char *
disasm_inst_flag_reg(const struct disasm_inst *inst)
{
static const char *flag_names[2][2] = {
{ "f0", "f0.1" },
{ "f1.0", "f1.1" },
};
return (inst->flag_reg <= 1 && inst->flag_subreg <= 1) ?
flag_names[inst->flag_reg][inst->flag_subreg] : "fBAD";
}
static const char *
disasm_inst_access_mode(const struct disasm_inst *inst)
{
switch (inst->access_mode) {
case GEN6_ALIGN_1: return " align1";
case GEN6_ALIGN_16: return " align16";
default: return " alignBAD";
}
}
static const char *
disasm_inst_mask_ctrl(const struct disasm_inst *inst)
{
switch (inst->mask_ctrl) {
case GEN6_MASKCTRL_NORMAL: return "";
case GEN6_MASKCTRL_NOMASK: return " WE_all";
default: return " WE_BAD";
}
}
static const char *
disasm_inst_dep_ctrl(const struct disasm_inst *inst)
{
switch (inst->dep_ctrl) {
case GEN6_DEPCTRL_NORMAL: return "";
case GEN6_DEPCTRL_NODDCLR: return " NoDDClr";
case GEN6_DEPCTRL_NODDCHK: return " NoDDChk";
case GEN6_DEPCTRL_NEITHER: return " NoDDClr,NoDDChk";
default: return " NoDDBAD";
}
}
static const char *
disasm_inst_qtr_ctrl(const struct disasm_inst *inst)
{
switch (inst->exec_size) {
case GEN6_EXECSIZE_8:
switch (inst->qtr_ctrl) {
case GEN6_QTRCTRL_1Q: return " 1Q";
case GEN6_QTRCTRL_2Q: return " 2Q";
case GEN6_QTRCTRL_3Q: return " 3Q";
case GEN6_QTRCTRL_4Q: return " 4Q";
default: return " BADQ";
}
break;
case GEN6_EXECSIZE_16:
switch (inst->qtr_ctrl) {
case GEN6_QTRCTRL_1H: return " 1H";
case GEN6_QTRCTRL_2H: return " 2H";
default: return " BADH";
}
break;
default:
return "";
}
}
static const char *
disasm_inst_thread_ctrl(const struct disasm_inst *inst)
{
switch (inst->thread_ctrl) {
case GEN6_THREADCTRL_NORMAL: return "";
case GEN6_THREADCTRL_ATOMIC: return " atomic";
case GEN6_THREADCTRL_SWITCH: return " switch";
default: return " BAD";
}
}
static const char *
disasm_inst_acc_wr_ctrl(const struct disasm_inst *inst)
{
return (inst->acc_wr_ctrl) ? " AccWrEnable" : "";
}
static const char *
disasm_inst_cmpt_ctrl(const struct disasm_inst *inst)
{
return (inst->cmpt_ctrl) ? " compacted" : "";
}
static const char *
disasm_inst_eot(const struct disasm_inst *inst)
{
const uint32_t mdesc = inst->u.ud;
if (inst->opcode == GEN6_OPCODE_SEND ||
inst->opcode == GEN6_OPCODE_SENDC)
return (mdesc & GEN6_MSG_EOT) ? " EOT" : "";
else
return "";
}
static const char *
disasm_inst_file(const struct disasm_inst *inst,
const struct disasm_operand *operand,
bool *multi_regs)
{
switch (operand->file) {
case GEN6_FILE_ARF:
switch (operand->reg & 0xf0) {
case GEN6_ARF_NULL: *multi_regs = false; return "null";
case GEN6_ARF_A0: *multi_regs = true; return "a";
case GEN6_ARF_ACC0: *multi_regs = true; return "acc";
case GEN6_ARF_F0: *multi_regs = true; return "f";
case GEN6_ARF_SR0: *multi_regs = true; return "sr";
case GEN6_ARF_CR0: *multi_regs = true; return "cr";
case GEN6_ARF_N0: *multi_regs = true; return "n";
case GEN6_ARF_IP: *multi_regs = false; return "ip";
case GEN6_ARF_TDR: *multi_regs = false; return "tdr";
case GEN7_ARF_TM0: *multi_regs = true; return "tm";
default: *multi_regs = false; return "BAD";
}
break;
case GEN6_FILE_GRF: *multi_regs = true; return "g";
case GEN6_FILE_MRF: *multi_regs = true; return "m";
case GEN6_FILE_IMM: *multi_regs = true; return "";
default: *multi_regs = false; return "BAD";
}
}
static const char *
disasm_inst_type(const struct disasm_inst *inst,
const struct disasm_operand *operand)
{
if (operand->file == GEN6_FILE_IMM) {
switch (operand->type) {
case GEN6_TYPE_UD: return "UD";
case GEN6_TYPE_D: return "D";
case GEN6_TYPE_UW: return "UW";
case GEN6_TYPE_W: return "W";
case GEN6_TYPE_UV_IMM: return "UV";
case GEN6_TYPE_VF_IMM: return "VF";
case GEN6_TYPE_V_IMM: return "V";
case GEN6_TYPE_F: return "F";
case GEN8_TYPE_DF_IMM: return "DF";
case GEN8_TYPE_HF_IMM: return "HF";
default: return "BAD";
}
} else {
switch (operand->type) {
case GEN6_TYPE_UD: return "UD";
case GEN6_TYPE_D: return "D";
case GEN6_TYPE_UW: return "UW";
case GEN6_TYPE_W: return "W";
case GEN6_TYPE_UB: return "UB";
case GEN6_TYPE_B: return "B";
case GEN7_TYPE_DF: return "DF";
case GEN6_TYPE_F: return "F";
case GEN8_TYPE_UQ: return "UQ";
case GEN8_TYPE_Q: return "Q";
case GEN8_TYPE_HF: return "HF";
default: return "BAD";
}
}
}
static const char *
disasm_inst_vert_stride(const struct disasm_inst *inst, unsigned vert_stride)
{
switch (vert_stride) {
case GEN6_VERTSTRIDE_0: return "0";
case GEN6_VERTSTRIDE_1: return "1";
case GEN6_VERTSTRIDE_2: return "2";
case GEN6_VERTSTRIDE_4: return "4";
case GEN6_VERTSTRIDE_8: return "8";
case GEN6_VERTSTRIDE_16: return "16";
case GEN6_VERTSTRIDE_32: return "32";
case GEN6_VERTSTRIDE_VXH: return "VxH";
default: return "BAD";
}
}
static const char *
disasm_inst_width(const struct disasm_inst *inst, unsigned width)
{
switch (width) {
case GEN6_WIDTH_1: return "1";
case GEN6_WIDTH_2: return "2";
case GEN6_WIDTH_4: return "4";
case GEN6_WIDTH_8: return "8";
case GEN6_WIDTH_16: return "16";
default: return "BAD";
}
}
static const char *
disasm_inst_horz_stride(const struct disasm_inst *inst, unsigned horz_stride)
{
switch (horz_stride) {
case GEN6_HORZSTRIDE_0: return "0";
case GEN6_HORZSTRIDE_1: return "1";
case GEN6_HORZSTRIDE_2: return "2";
case GEN6_HORZSTRIDE_4: return "4";
default: return "BAD";
}
}
static const char *
disasm_inst_writemask(const struct disasm_inst *inst, unsigned writemask)
{
switch (writemask) {
case 0x0: return ".";
case 0x1: return ".x";
case 0x2: return ".y";
case 0x3: return ".xy";
case 0x4: return ".z";
case 0x5: return ".xz";
case 0x6: return ".yz";
case 0x7: return ".xyz";
case 0x8: return ".w";
case 0x9: return ".xw";
case 0xa: return ".yw";
case 0xb: return ".xyw";
case 0xc: return ".zw";
case 0xd: return ".xzw";
case 0xe: return ".yzw";
case 0xf: return "";
default: return ".BAD";
}
}
static const char *
disasm_inst_negate(const struct disasm_inst *inst, bool negate)
{
if (ilo_dev_gen(inst->dev) >= ILO_GEN(8)) {
switch (inst->opcode) {
case GEN6_OPCODE_AND:
case GEN6_OPCODE_NOT:
case GEN6_OPCODE_OR:
case GEN6_OPCODE_XOR:
return (negate) ? "~" : "";
break;
default:
break;
}
}
return (negate) ? "-" : "";
}
static const char *
disasm_inst_absolute(const struct disasm_inst *inst, bool absolute)
{
return (absolute) ? "(abs)" : "";
}
static const char *
disasm_inst_mdesc_sampler_op(const struct disasm_inst *inst, int op)
{
switch (op) {
case GEN6_MSG_SAMPLER_SAMPLE: return "sample";
case GEN6_MSG_SAMPLER_SAMPLE_B: return "sample_b";
case GEN6_MSG_SAMPLER_SAMPLE_L: return "sample_l";
case GEN6_MSG_SAMPLER_SAMPLE_C: return "sample_c";
case GEN6_MSG_SAMPLER_SAMPLE_D: return "sample_d";
case GEN6_MSG_SAMPLER_SAMPLE_B_C: return "sample_b_c";
case GEN6_MSG_SAMPLER_SAMPLE_L_C: return "sample_l_c";
case GEN6_MSG_SAMPLER_LD: return "ld";
case GEN6_MSG_SAMPLER_GATHER4: return "gather4";
case GEN6_MSG_SAMPLER_LOD: return "lod";
case GEN6_MSG_SAMPLER_RESINFO: return "resinfo";
case GEN6_MSG_SAMPLER_SAMPLEINFO: return "sampleinfo";
case GEN7_MSG_SAMPLER_GATHER4_C: return "gather4_c";
case GEN7_MSG_SAMPLER_GATHER4_PO: return "gather4_po";
case GEN7_MSG_SAMPLER_GATHER4_PO_C: return "gather4_po_c";
case GEN7_MSG_SAMPLER_SAMPLE_D_C: return "sample_d_c";
case GEN7_MSG_SAMPLER_SAMPLE_LZ: return "sample_lz";
case GEN7_MSG_SAMPLER_SAMPLE_C_LC: return "sample_c_lc";
case GEN7_MSG_SAMPLER_LD_LZ: return "ld_lz";
case GEN7_MSG_SAMPLER_LD_MCS: return "ld_mcs";
case GEN7_MSG_SAMPLER_LD2DMS: return "ld2dms";
case GEN7_MSG_SAMPLER_LD2DSS: return "ld2dss";
default: return "BAD";
}
}
static const char *
disasm_inst_mdesc_sampler_simd(const struct disasm_inst *inst, int simd)
{
switch (simd) {
case GEN6_MSG_SAMPLER_SIMD4X2: return "SIMD4x2";
case GEN6_MSG_SAMPLER_SIMD8: return "SIMD8";
case GEN6_MSG_SAMPLER_SIMD16: return "SIMD16";
case GEN6_MSG_SAMPLER_SIMD32_64: return "SIMD32";
default: return "BAD";
}
}
static const char *
disasm_inst_mdesc_urb_op(const struct disasm_inst *inst, int op)
{
if (ilo_dev_gen(inst->dev) >= ILO_GEN(7)) {
switch (op) {
case GEN7_MSG_URB_WRITE_HWORD: return "write HWord";
case GEN7_MSG_URB_WRITE_OWORD: return "write OWord";
case GEN7_MSG_URB_READ_HWORD: return "read HWord";
case GEN7_MSG_URB_READ_OWORD: return "read OWord";
case GEN7_MSG_URB_ATOMIC_MOV: return "atomic mov";
case GEN7_MSG_URB_ATOMIC_INC: return "atomic inc";
default: return "BAD";
}
} else {
switch (op) {
case GEN6_MSG_URB_WRITE: return "urb_write";
case GEN6_MSG_URB_FF_SYNC: return "ff_sync";
default: return "BAD";
}
}
}
static const char *
disasm_inst_mdesc_dp_op_gen6(const struct disasm_inst *inst,
int sfid, int op)
{
ILO_DEV_ASSERT(inst->dev, 6, 6);
switch (op) {
case GEN6_MSG_DP_OWORD_BLOCK_READ: return "OWORD block read";
case GEN6_MSG_DP_RT_UNORM_READ: return "RT UNORM read";
case GEN6_MSG_DP_OWORD_DUAL_BLOCK_READ: return "OWORD dual block read";
case GEN6_MSG_DP_MEDIA_BLOCK_READ: return "media block read";
case GEN6_MSG_DP_UNALIGNED_OWORD_BLOCK_READ: return "unaligned OWORD block read";
case GEN6_MSG_DP_DWORD_SCATTERED_READ: return "DWORD scattered read";
case GEN6_MSG_DP_DWORD_ATOMIC_WRITE: return "DWORD atomic write";
case GEN6_MSG_DP_OWORD_BLOCK_WRITE: return "OWORD block write";
case GEN6_MSG_DP_OWORD_DUAL_BLOCK_WRITE: return "OWORD dual block_write";
case GEN6_MSG_DP_MEDIA_BLOCK_WRITE: return "media block write";
case GEN6_MSG_DP_DWORD_SCATTERED_WRITE: return "DWORD scattered write";
case GEN6_MSG_DP_RT_WRITE: return "RT write";
case GEN6_MSG_DP_SVB_WRITE: return "SVB write";
case GEN6_MSG_DP_RT_UNORM_WRITE: return "RT UNORM write";
default: return "BAD";
}
}
static const char *
disasm_inst_mdesc_dp_op_gen7(const struct disasm_inst *inst,
int sfid, int op)
{
ILO_DEV_ASSERT(inst->dev, 7, 7);
switch (sfid) {
case GEN6_SFID_DP_SAMPLER:
switch (op) {
case GEN7_MSG_DP_SAMPLER_UNALIGNED_OWORD_BLOCK_READ: return "OWORD block read";
case GEN7_MSG_DP_SAMPLER_MEDIA_BLOCK_READ: return "media block read";
default: return "BAD";
}
case GEN6_SFID_DP_RC:
switch (op) {
case GEN7_MSG_DP_RC_MEDIA_BLOCK_READ: return "media block read";
case GEN7_MSG_DP_RC_TYPED_SURFACE_READ: return "typed surface read";
case GEN7_MSG_DP_RC_TYPED_ATOMIC_OP: return "typed atomic op";
case GEN7_MSG_DP_RC_MEMORY_FENCE: return "memory fence";
case GEN7_MSG_DP_RC_MEDIA_BLOCK_WRITE: return "media block write";
case GEN7_MSG_DP_RC_RT_WRITE: return "RT write";
case GEN7_MSG_DP_RC_TYPED_SURFACE_WRITE: return "typed surface write";
default: return "BAD";
}
case GEN6_SFID_DP_CC:
switch (op) {
case GEN7_MSG_DP_CC_OWORD_BLOCK_READ: return "OWROD block read";
case GEN7_MSG_DP_CC_UNALIGNED_OWORD_BLOCK_READ: return "unaligned OWORD block read";
case GEN7_MSG_DP_CC_OWORD_DUAL_BLOCK_READ: return "OWORD dual block read";
case GEN7_MSG_DP_CC_DWORD_SCATTERED_READ: return "DWORD scattered read";
default: return "BAD";
}
case GEN7_SFID_DP_DC0:
switch (op) {
case GEN7_MSG_DP_DC0_OWORD_BLOCK_READ: return "OWORD block read";
case GEN7_MSG_DP_DC0_UNALIGNED_OWORD_BLOCK_READ: return "unaligned OWORD block read";
case GEN7_MSG_DP_DC0_OWORD_DUAL_BLOCK_READ: return "OWORD dual block read";
case GEN7_MSG_DP_DC0_DWORD_SCATTERED_READ: return "DWORD scattered read";
case GEN7_MSG_DP_DC0_BYTE_SCATTERED_READ: return "BYTE scattered read";
case GEN7_MSG_DP_DC0_UNTYPED_SURFACE_READ: return "untyped surface read";
case GEN7_MSG_DP_DC0_UNTYPED_ATOMIC_OP: return "untyped atomic op";
case GEN7_MSG_DP_DC0_MEMORY_FENCE: return "memory fence";
case GEN7_MSG_DP_DC0_OWORD_BLOCK_WRITE: return "OWORD block write";
case GEN7_MSG_DP_DC0_OWORD_DUAL_BLOCK_WRITE: return "OWORD dual block write";
case GEN7_MSG_DP_DC0_DWORD_SCATTERED_WRITE: return "OWORD scattered write";
case GEN7_MSG_DP_DC0_BYTE_SCATTERED_WRITE: return "BYTE scattered write";
case GEN7_MSG_DP_DC0_UNTYPED_SURFACE_WRITE: return "untyped surface write";
default: return "BAD";
}
default: return "BAD";
}
}
static const char *
disasm_inst_mdesc_dp_op_gen75(const struct disasm_inst *inst,
int sfid, int op)
{
ILO_DEV_ASSERT(inst->dev, 7.5, 8);
switch (sfid) {
case GEN6_SFID_DP_SAMPLER:
switch (op) {
case GEN75_MSG_DP_SAMPLER_READ_SURFACE_INFO: return "read surface info";
case GEN75_MSG_DP_SAMPLER_UNALIGNED_OWORD_BLOCK_READ: return "unaligned OWORD block read";
case GEN75_MSG_DP_SAMPLER_MEDIA_BLOCK_READ: return "media block read";
default: return "BAD";
}
case GEN6_SFID_DP_RC:
switch (op) {
case GEN75_MSG_DP_RC_MEDIA_BLOCK_READ: return "media block read";
case GEN75_MSG_DP_RC_MEMORY_FENCE: return "memory fence";
case GEN75_MSG_DP_RC_MEDIA_BLOCK_WRITE: return "media block write";
case GEN75_MSG_DP_RC_RT_WRITE: return "RT write";
default: return "BAD";
}
case GEN6_SFID_DP_CC:
switch (op) {
case GEN75_MSG_DP_CC_OWORD_BLOCK_READ: return "OWROD block read";
case GEN75_MSG_DP_CC_UNALIGNED_OWORD_BLOCK_READ: return "unaligned OWORD block read";
case GEN75_MSG_DP_CC_OWORD_DUAL_BLOCK_READ: return "OWORD dual block read";
case GEN75_MSG_DP_CC_DWORD_SCATTERED_READ: return "DWORD scattered read";
default: return "BAD";
}
case GEN7_SFID_DP_DC0:
switch (op) {
case GEN75_MSG_DP_DC0_OWORD_BLOCK_READ: return "OWORD block read";
case GEN75_MSG_DP_DC0_UNALIGNED_OWORD_BLOCK_READ: return "unaligned OWORD block read";
case GEN75_MSG_DP_DC0_OWORD_DUAL_BLOCK_READ: return "OWORD dual block read";
case GEN75_MSG_DP_DC0_DWORD_SCATTERED_READ: return "DWORD scattered read";
case GEN75_MSG_DP_DC0_BYTE_SCATTERED_READ: return "BYTE scattered read";
case GEN75_MSG_DP_DC0_MEMORY_FENCE: return "memory fence";
case GEN75_MSG_DP_DC0_OWORD_BLOCK_WRITE: return "OWORD block write";
case GEN75_MSG_DP_DC0_OWORD_DUAL_BLOCK_WRITE: return "OWORD dual block write";
case GEN75_MSG_DP_DC0_DWORD_SCATTERED_WRITE: return "OWORD scattered write";
case GEN75_MSG_DP_DC0_BYTE_SCATTERED_WRITE: return "BYTE scattered write";
default: return "BAD";
}
case GEN75_SFID_DP_DC1:
switch (op) {
case GEN75_MSG_DP_DC1_UNTYPED_SURFACE_READ: return "untyped surface read";
case GEN75_MSG_DP_DC1_UNTYPED_ATOMIC_OP: return "DC untyped atomic op";
case GEN75_MSG_DP_DC1_UNTYPED_ATOMIC_OP_SIMD4X2: return "DC untyped 4x2 atomic op";
case GEN75_MSG_DP_DC1_MEDIA_BLOCK_READ: return "DC media block read";
case GEN75_MSG_DP_DC1_TYPED_SURFACE_READ: return "DC typed surface read";
case GEN75_MSG_DP_DC1_TYPED_ATOMIC_OP: return "DC typed atomic";
case GEN75_MSG_DP_DC1_TYPED_ATOMIC_OP_SIMD4X2: return "DC typed 4x2 atomic op";
case GEN75_MSG_DP_DC1_UNTYPED_SURFACE_WRITE: return "DC untyped surface write";
case GEN75_MSG_DP_DC1_MEDIA_BLOCK_WRITE: return "DC media block write";
case GEN75_MSG_DP_DC1_ATOMIC_COUNTER_OP: return "DC atomic counter op";
case GEN75_MSG_DP_DC1_ATOMIC_COUNTER_OP_SIMD4X2: return "DC 4x2 atomic counter op";
case GEN75_MSG_DP_DC1_TYPED_SURFACE_WRITE: return "DC typed surface write";
default: return "BAD";
}
default: return "BAD";
}
}
static const char *
disasm_inst_mdesc_dp_op(const struct disasm_inst *inst, int sfid, int op)
{
switch (ilo_dev_gen(inst->dev)) {
case ILO_GEN(8):
case ILO_GEN(7.5): return disasm_inst_mdesc_dp_op_gen75(inst, sfid, op);
case ILO_GEN(7): return disasm_inst_mdesc_dp_op_gen7(inst, sfid, op);
case ILO_GEN(6): return disasm_inst_mdesc_dp_op_gen6(inst, sfid, op);
default: return "BAD";
}
}
static const char *
disasm_inst_mdesc_dp_untyped_surface_simd_mode(const struct disasm_inst *inst,
uint32_t mdesc)
{
switch (mdesc & GEN7_MSG_DP_UNTYPED_MODE__MASK) {
case GEN7_MSG_DP_UNTYPED_MODE_SIMD4X2: return "SIMD4x2";
case GEN7_MSG_DP_UNTYPED_MODE_SIMD16: return "SIMD16";
case GEN7_MSG_DP_UNTYPED_MODE_SIMD8: return "SIMD8";
default: return "BAD";
}
}
static const char *
disasm_inst_mdesc_dp_rt_write_simd_mode(const struct disasm_inst *inst,
uint32_t mdesc)
{
switch (mdesc & GEN6_MSG_DP_RT_MODE__MASK) {
case GEN6_MSG_DP_RT_MODE_SIMD16: return "SIMD16";
case GEN6_MSG_DP_RT_MODE_SIMD16_REPDATA: return "SIMD16/RepData";
case GEN6_MSG_DP_RT_MODE_SIMD8_DUALSRC_LO: return "SIMD8/DualSrcLow";
case GEN6_MSG_DP_RT_MODE_SIMD8_DUALSRC_HI: return "SIMD8/DualSrcHigh";
case GEN6_MSG_DP_RT_MODE_SIMD8_LO: return "SIMD8";
case GEN6_MSG_DP_RT_MODE_SIMD8_IMAGE_WR: return "SIMD8/ImageWrite";
default: return "BAD";
}
}
static bool
disasm_inst_is_null(const struct disasm_inst *inst,
const struct disasm_operand *operand)
{
return (operand->file == GEN6_FILE_ARF && operand->reg == GEN6_ARF_NULL);
}
static int
disasm_inst_type_size(const struct disasm_inst *inst,
const struct disasm_operand *operand)
{
assert(operand
->file
!= GEN6_FILE_IMM
);
switch (operand->type) {
case GEN6_TYPE_UD: return 4;
case GEN6_TYPE_D: return 4;
case GEN6_TYPE_UW: return 2;
case GEN6_TYPE_W: return 2;
case GEN6_TYPE_UB: return 1;
case GEN6_TYPE_B: return 1;
case GEN7_TYPE_DF: return 8;
case GEN6_TYPE_F: return 4;
default: return 1;
}
}
static void
disasm_printer_reset(struct disasm_printer *printer)
{
printer->buf[0] = '\0';
printer->len = 0;
}
static const char *
disasm_printer_get_string(struct disasm_printer *printer)
{
return printer->buf;
}
static void _util_printf_format(2, 3)
disasm_printer_add(struct disasm_printer *printer, const char *format, ...)
{
const size_t avail = sizeof(printer->buf) - printer->len;
va_list ap;
int written;
written = vsnprintf(printer->buf + printer->len, avail, format, ap);
/* truncated */
if (written < 0 || written >= avail) {
memcpy(printer
->buf
+ sizeof(printer
->buf
) - 4, "...", 4);
printer->len = sizeof(printer->buf) - 1;
} else {
printer->len += written;
}
}
/**
* Pad to the specified column.
*/
static void
disasm_printer_column(struct disasm_printer *printer, int col)
{
int len = DISASM_PRINTER_COLUMN_WIDTH * col;
if (len <= printer->len) {
if (!printer->len)
return;
/* at least one space */
len = printer->len + 1;
}
if (len >= sizeof(printer->buf)) {
len = sizeof(printer->buf) - 1;
if (len <= printer->len)
return;
}
memset(printer
->buf
+ printer
->len
, ' ', len
- printer
->len
);
printer->len = len;
printer->buf[printer->len] = '\0';
}
static void
disasm_printer_add_op(struct disasm_printer *printer,
const struct disasm_inst *inst)
{
if (inst->pred_ctrl != GEN6_PREDCTRL_NONE) {
disasm_printer_add(printer, "(%c%s%s) ",
disasm_inst_pred_inv(inst),
disasm_inst_flag_reg(inst),
disasm_inst_pred_ctrl(inst));
}
disasm_printer_add(printer, "%s%s%s%s",
disasm_inst_opcode(inst),
disasm_inst_saturate(inst),
disasm_inst_debug_ctrl(inst),
disasm_inst_cond_modifier(inst));
if (inst->cond_modifier != GEN6_COND_NONE) {
switch (inst->opcode) {
case GEN6_OPCODE_SEL:
case GEN6_OPCODE_IF:
case GEN6_OPCODE_WHILE:
/* these do not update flag registers */
break;
default:
disasm_printer_add(printer, ".%s", disasm_inst_flag_reg(inst));
break;
}
}
if (inst->opcode == GEN6_OPCODE_MATH)
disasm_printer_add(printer, " %s", disasm_inst_fc(inst));
if (inst->opcode != GEN6_OPCODE_NOP)
disasm_printer_add(printer, "(%s)", disasm_inst_exec_size(inst));
}
static void
disasm_printer_add_operand(struct disasm_printer *printer,
const struct disasm_inst *inst,
const struct disasm_operand *operand)
{
const char *name;
bool multi_regs;
name = disasm_inst_file(inst, operand, &multi_regs);
if (!multi_regs) {
disasm_printer_add(printer, "%s", name);
return;
}
if (operand->file == GEN6_FILE_IMM) {
switch (operand->type) {
case GEN6_TYPE_UD:
disasm_printer_add(printer, "0x%08xUD", inst->u.ud);
break;
case GEN6_TYPE_D:
disasm_printer_add(printer, "%dD", inst->u.d);
break;
case GEN6_TYPE_UW:
disasm_printer_add(printer, "0x%04xUW", inst->u.uw);
break;
case GEN6_TYPE_W:
disasm_printer_add(printer, "%dW", inst->u.w);
break;
case GEN6_TYPE_UV_IMM:
disasm_printer_add(printer, "0x%08xUV", inst->u.ud);
break;
case GEN6_TYPE_VF_IMM:
disasm_printer_add(printer, "Vector Float");
break;
case GEN6_TYPE_V_IMM:
disasm_printer_add(printer, "0x%08xV", inst->u.ud);
break;
case GEN6_TYPE_F:
disasm_printer_add(printer, "%-gF", uif(inst->u.f));
break;
default:
disasm_printer_add(printer, "BAD");
break;
}
return;
}
if (operand->addr_mode == GEN6_ADDRMODE_DIRECT) {
unsigned reg, subreg;
reg = operand->reg;
if (operand->file == GEN6_FILE_ARF)
reg &= 0xf;
subreg = operand->subreg / disasm_inst_type_size(inst, operand);
if (subreg)
disasm_printer_add(printer, "%s%d.%d", name, reg, subreg);
else
disasm_printer_add(printer, "%s%d", name, reg);
} else {
disasm_printer_add(printer, "%s[a0.%d %d]",
name, operand->addr_subreg, operand->addr_imm);
}
}
static void
disasm_printer_add_dst(struct disasm_printer *printer,
const struct disasm_inst *inst,
const struct disasm_dst_operand *dst)
{
disasm_printer_add_operand(printer, inst, &dst->base);
/* dst is an immediate when in EU_INSTRUCTION_BRANCH_CONDITIONAL form */
if (disasm_inst_is_null(inst, &dst->base) ||
dst->base.file == GEN6_FILE_IMM)
return;
disasm_printer_add(printer, "<%s>%s%s",
disasm_inst_horz_stride(inst, dst->horz_stride),
disasm_inst_writemask(inst, dst->writemask),
disasm_inst_type(inst, &dst->base));
}
static void
disasm_printer_add_src(struct disasm_printer *printer,
const struct disasm_inst *inst,
const struct disasm_src_operand *src)
{
static const char swizzle_chars[4] = { 'x', 'y', 'z', 'w' };
char swizzle[5];
disasm_printer_add(printer, "%s%s",
disasm_inst_negate(inst, src->negate),
disasm_inst_absolute(inst, src->absolute));
disasm_printer_add_operand(printer, inst, &src->base);
if (disasm_inst_is_null(inst, &src->base) ||
src->base.file == GEN6_FILE_IMM)
return;
if (src->swizzle_x == 0 && src->swizzle_y == 1 &&
src->swizzle_z == 2 && src->swizzle_w == 3) {
swizzle[0] = '\0';
} else if (src->swizzle_x == src->swizzle_y &&
src->swizzle_x == src->swizzle_z &&
src->swizzle_x == src->swizzle_w) {
swizzle[0] = swizzle_chars[src->swizzle_x];
swizzle[1] = '\0';
} else {
swizzle[0] = swizzle_chars[src->swizzle_x];
swizzle[1] = swizzle_chars[src->swizzle_y];
swizzle[2] = swizzle_chars[src->swizzle_z];
swizzle[3] = swizzle_chars[src->swizzle_w];
swizzle[4] = '\0';
}
disasm_printer_add(printer, "<%s,%s,%s>%s%s",
disasm_inst_vert_stride(inst, src->vert_stride),
disasm_inst_width(inst, src->width),
disasm_inst_horz_stride(inst, src->horz_stride),
swizzle,
disasm_inst_type(inst, &src->base));
}
static void
disasm_printer_add_ctrl(struct disasm_printer *printer,
const struct disasm_inst *inst)
{
if (inst->opcode == GEN6_OPCODE_NOP) {
disasm_printer_add(printer, ";");
return;
}
disasm_printer_add(printer, "{%s%s%s%s%s%s%s%s };",
disasm_inst_access_mode(inst),
disasm_inst_mask_ctrl(inst),
disasm_inst_dep_ctrl(inst),
disasm_inst_qtr_ctrl(inst),
disasm_inst_cmpt_ctrl(inst),
disasm_inst_thread_ctrl(inst),
disasm_inst_acc_wr_ctrl(inst),
disasm_inst_eot(inst));
}
static void
disasm_printer_add_mdesc_sampler(struct disasm_printer *printer,
const struct disasm_inst *inst,
uint32_t mdesc)
{
int op, simd;
if (ilo_dev_gen(inst->dev) >= ILO_GEN(7)) {
op = GEN_EXTRACT(mdesc, GEN7_MSG_SAMPLER_OP);
simd = GEN_EXTRACT(mdesc, GEN7_MSG_SAMPLER_SIMD);
} else {
op = GEN_EXTRACT(mdesc, GEN6_MSG_SAMPLER_OP);
simd = GEN_EXTRACT(mdesc, GEN6_MSG_SAMPLER_SIMD);
}
disasm_printer_add(printer,
"%s %s samp %d surf %d",
disasm_inst_mdesc_sampler_op(inst, op),
disasm_inst_mdesc_sampler_simd(inst, simd),
GEN_EXTRACT(mdesc, GEN6_MSG_SAMPLER_INDEX),
GEN_EXTRACT(mdesc, GEN6_MSG_SAMPLER_SURFACE));
}
static void
disasm_printer_add_mdesc_urb(struct disasm_printer *printer,
const struct disasm_inst *inst,
uint32_t mdesc)
{
int op, offset;
bool interleaved, complete, allocate, used;
if (ilo_dev_gen(inst->dev) >= ILO_GEN(7)) {
op = GEN_EXTRACT(mdesc, GEN7_MSG_URB_OP);
offset = GEN_EXTRACT(mdesc, GEN7_MSG_URB_GLOBAL_OFFSET);
interleaved = mdesc & GEN7_MSG_URB_INTERLEAVED;
complete = (ilo_dev_gen(inst->dev) >= ILO_GEN(8)) ?
false : (mdesc & GEN7_MSG_URB_COMPLETE);
allocate = false;
used = false;
} else {
op = GEN_EXTRACT(mdesc, GEN6_MSG_URB_OP);
offset = GEN_EXTRACT(mdesc, GEN6_MSG_URB_OFFSET);
interleaved = mdesc & GEN6_MSG_URB_INTERLEAVED;
complete = mdesc & GEN6_MSG_URB_COMPLETE;
allocate = mdesc & GEN6_MSG_URB_ALLOCATE;
used = mdesc & GEN6_MSG_URB_USED;
}
disasm_printer_add(printer, "%s offset %d%s%s%s%s",
disasm_inst_mdesc_urb_op(inst, op),
offset,
(interleaved) ? " interleave" : "",
(allocate) ? " allocate" : "",
(used) ? " used" : "",
(complete) ? " complete" : "");
}
static void
disasm_printer_add_mdesc_spawner(struct disasm_printer *printer,
const struct disasm_inst *inst,
uint32_t mdesc)
{
const char *requester, *op;
switch (mdesc & GEN6_MSG_TS_REQUESTER_TYPE__MASK) {
case GEN6_MSG_TS_REQUESTER_TYPE_ROOT: requester = "root"; break;
case GEN6_MSG_TS_REQUESTER_TYPE_CHILD: requester = "child"; break;
default: requester = "BAD"; break;
}
switch (mdesc & GEN6_MSG_TS_OPCODE__MASK) {
case GEN6_MSG_TS_OPCODE_DEREF:
op = (mdesc & GEN6_MSG_TS_RESOURCE_SELECT_NO_DEREF) ?
"no deref" : "deref";
break;
case GEN6_MSG_TS_OPCODE_SPAWN:
op = (mdesc & GEN6_MSG_TS_RESOURCE_SELECT_ROOT) ?
"spawn root" : "spawn child";
break;
default:
op = "BAD";
break;
}
disasm_printer_add(printer, "%s thread %s", requester, op);
}
static void
disasm_printer_add_mdesc_dp_sampler(struct disasm_printer *printer,
const struct disasm_inst *inst,
uint32_t mdesc)
{
const int op = (ilo_dev_gen(inst->dev) >= ILO_GEN(7)) ?
GEN_EXTRACT(mdesc, GEN7_MSG_DP_OP) : GEN_EXTRACT(mdesc, GEN6_MSG_DP_OP);
const bool write_commit = (ilo_dev_gen(inst->dev) == ILO_GEN(6)) ?
(mdesc & GEN6_MSG_DP_SEND_WRITE_COMMIT) : 0;
disasm_printer_add(printer, "%s block size %d commit %d surf %d",
disasm_inst_mdesc_dp_op(inst, GEN6_SFID_DP_SAMPLER, op),
GEN_EXTRACT(mdesc, GEN6_MSG_DP_OWORD_BLOCK_SIZE),
write_commit,
GEN_EXTRACT(mdesc, GEN6_MSG_DP_SURFACE));
}
static void
disasm_printer_add_mdesc_dp_dc0(struct disasm_printer *printer,
const struct disasm_inst *inst,
uint32_t mdesc)
{
const int op = GEN_EXTRACT(mdesc, GEN7_MSG_DP_OP);
ILO_DEV_ASSERT(inst->dev, 7, 7.5);
if (ilo_dev_gen(inst->dev) >= ILO_GEN(7.5)) {
disasm_printer_add(printer, "%s ctrl 0x%x surf %d",
disasm_inst_mdesc_dp_op(inst, GEN7_SFID_DP_DC0, op),
GEN_EXTRACT(mdesc, GEN6_MSG_DP_CTRL),
GEN_EXTRACT(mdesc, GEN6_MSG_DP_SURFACE));
} else {
switch (op) {
case GEN7_MSG_DP_DC0_UNTYPED_SURFACE_READ:
case GEN7_MSG_DP_DC0_UNTYPED_SURFACE_WRITE:
disasm_printer_add(printer, "%s %s mask 0x%x surf %d",
disasm_inst_mdesc_dp_op(inst, GEN7_SFID_DP_DC0, op),
disasm_inst_mdesc_dp_untyped_surface_simd_mode(inst, mdesc),
GEN_EXTRACT(mdesc, GEN7_MSG_DP_UNTYPED_MASK),
GEN_EXTRACT(mdesc, GEN6_MSG_DP_SURFACE));
break;
default:
disasm_printer_add(printer, "%s ctrl 0x%x surf %d",
disasm_inst_mdesc_dp_op(inst, GEN7_SFID_DP_DC0, op),
GEN_EXTRACT(mdesc, GEN6_MSG_DP_CTRL),
GEN_EXTRACT(mdesc, GEN6_MSG_DP_SURFACE));
break;
}
}
}
static void
disasm_printer_add_mdesc_dp_dc1(struct disasm_printer *printer,
const struct disasm_inst *inst,
uint32_t mdesc)
{
const int op = GEN_EXTRACT(mdesc, GEN7_MSG_DP_OP);
ILO_DEV_ASSERT(inst->dev, 7.5, 7.5);
switch (op) {
case GEN75_MSG_DP_DC1_UNTYPED_SURFACE_READ:
case GEN75_MSG_DP_DC1_UNTYPED_SURFACE_WRITE:
disasm_printer_add(printer, "%s %s mask 0x%x surf %d",
disasm_inst_mdesc_dp_op(inst, GEN75_SFID_DP_DC1, op),
disasm_inst_mdesc_dp_untyped_surface_simd_mode(inst, mdesc),
GEN_EXTRACT(mdesc, GEN7_MSG_DP_UNTYPED_MASK),
GEN_EXTRACT(mdesc, GEN6_MSG_DP_SURFACE));
break;
default:
disasm_printer_add(printer, "%s ctrl 0x%x surf %d",
disasm_inst_mdesc_dp_op(inst, GEN75_SFID_DP_DC1, op),
GEN_EXTRACT(mdesc, GEN6_MSG_DP_CTRL),
GEN_EXTRACT(mdesc, GEN6_MSG_DP_SURFACE));
break;
}
}
static void
disasm_printer_add_mdesc_dp_rc(struct disasm_printer *printer,
const struct disasm_inst *inst,
uint32_t mdesc)
{
const int op = (ilo_dev_gen(inst->dev) >= ILO_GEN(7)) ?
GEN_EXTRACT(mdesc, GEN7_MSG_DP_OP) : GEN_EXTRACT(mdesc, GEN6_MSG_DP_OP);
bool is_rt_write;
if (ilo_dev_gen(inst->dev) >= ILO_GEN(7.5))
is_rt_write = (op == GEN75_MSG_DP_RC_RT_WRITE);
else if (ilo_dev_gen(inst->dev) >= ILO_GEN(7))
is_rt_write = (op == GEN7_MSG_DP_RC_RT_WRITE);
else
is_rt_write = (op == GEN6_MSG_DP_RT_WRITE);
disasm_printer_add(printer, "%s",
disasm_inst_mdesc_dp_op(inst, GEN6_SFID_DP_RC, op));
if (is_rt_write) {
disasm_printer_add(printer, " %s%s%s%s",
disasm_inst_mdesc_dp_rt_write_simd_mode(inst, mdesc),
(mdesc & GEN6_MSG_DP_RT_SLOTGRP_HI) ? " Hi" : "",
(mdesc & GEN6_MSG_DP_RT_LAST) ? " LastRT" : "",
(ilo_dev_gen(inst->dev) == ILO_GEN(6) &&
(mdesc & GEN6_MSG_DP_SEND_WRITE_COMMIT)) ? " WriteCommit" : "");
}
disasm_printer_add(printer, " surf %d",
GEN_EXTRACT(mdesc, GEN6_MSG_DP_SURFACE));
}
static void
disasm_printer_add_mdesc(struct disasm_printer *printer,
const struct disasm_inst *inst)
{
const uint32_t mdesc = inst->u.ud;
assert(inst
->opcode
== GEN6_OPCODE_SEND
||
inst->opcode == GEN6_OPCODE_SENDC);
assert(inst
->src1.
base.
file == GEN6_FILE_IMM
);
disasm_printer_add(printer, " %s (", disasm_inst_sfid(inst));
switch (inst->sfid) {
case GEN6_SFID_SAMPLER:
disasm_printer_add_mdesc_sampler(printer, inst, mdesc);
break;
case GEN6_SFID_DP_SAMPLER:
disasm_printer_add_mdesc_dp_sampler(printer, inst, mdesc);
break;
case GEN6_SFID_DP_RC:
disasm_printer_add_mdesc_dp_rc(printer, inst, mdesc);
break;
case GEN6_SFID_URB:
disasm_printer_add_mdesc_urb(printer, inst, mdesc);
break;
case GEN6_SFID_SPAWNER:
disasm_printer_add_mdesc_spawner(printer, inst, mdesc);
break;
case GEN7_SFID_DP_DC0:
disasm_printer_add_mdesc_dp_dc0(printer, inst, mdesc);
break;
case GEN75_SFID_DP_DC1:
disasm_printer_add_mdesc_dp_dc1(printer, inst, mdesc);
break;
case GEN6_SFID_DP_CC:
case GEN7_SFID_PI:
default:
break;
}
disasm_printer_add(printer, ") mlen %d rlen %d",
GEN_EXTRACT(mdesc, GEN6_MSG_MLEN),
GEN_EXTRACT(mdesc, GEN6_MSG_RLEN));
}
static void
disasm_printer_print_inst(struct disasm_printer *printer,
const struct disasm_inst *inst)
{
int col = 0;
disasm_printer_reset(printer);
disasm_printer_column(printer, col++);
disasm_printer_add_op(printer, inst);
if (inst->has_jip || inst->has_uip) {
if (inst->has_jip) {
const int32_t jip = (ilo_dev_gen(inst->dev) >= ILO_GEN(8)) ?
inst->u.ip32.jip : inst->u.ip16.jip;
disasm_printer_column(printer, col++);
disasm_printer_add(printer, "JIP: %d", jip);
}
if (inst->has_uip) {
const int32_t uip = (ilo_dev_gen(inst->dev) >= ILO_GEN(8)) ?
inst->u.ip32.uip : inst->u.ip16.uip;
disasm_printer_column(printer, col++);
disasm_printer_add(printer, "UIP: %d", uip);
}
} else {
const int src_count = disasm_opcode_table[inst->opcode].src_count;
if (src_count) {
const struct disasm_src_operand *src[3] = {
&inst->src0, &inst->src1, &inst->u.src2
};
int i;
disasm_printer_column(printer, col++);
disasm_printer_add_dst(printer, inst, &inst->dst);
for (i = 0; i < src_count; i++) {
disasm_printer_column(printer, col++);
disasm_printer_add_src(printer, inst, src[i]);
}
}
}
if (inst->opcode == GEN6_OPCODE_SEND ||
inst->opcode == GEN6_OPCODE_SENDC) {
/* start a new line */
ilo_printf("%s\n", disasm_printer_get_string(printer));
disasm_printer_reset(printer);
col = 0;
disasm_printer_column(printer, col++);
disasm_printer_column(printer, col++);
disasm_printer_add_mdesc(printer, inst);
}
if (col < 4)
col = 4;
disasm_printer_column(printer, col++);
disasm_printer_add_ctrl(printer, inst);
ilo_printf("%s\n", disasm_printer_get_string(printer));
}
static void
disasm_uncompact_3src(const struct ilo_dev *dev,
uint64_t compact, uint32_t *dw)
{
const struct toy_compaction_table *tbl =
toy_compiler_get_compaction_table(dev);
uint32_t src[3], tmp;
uint64_t tmp64;
ILO_DEV_ASSERT(dev, 8, 8);
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_OPCODE);
dw[0] = GEN_SHIFT32(tmp, GEN6_INST_OPCODE);
/* ControlIndex */
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_CONTROL_INDEX);
tmp = tbl->control_3src[tmp];
dw[0] |= (tmp & 0x1fffff) << GEN6_INST_ACCESSMODE__SHIFT;
dw[1] = (tmp >> 21) & ((ilo_dev_gen(dev) >= ILO_GEN(9)) ? 0x1f : 0x7);
/* SourceIndex */
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_SOURCE_INDEX);
tmp64 = tbl->source_3src[tmp];
dw[1] |= (tmp64 & 0x7ffff) << 5;
src[0] = ((tmp64 >> 19) & 0xff) << 1;
src[1] = ((tmp64 >> 27) & 0xff) << 1;
src[2] = ((tmp64 >> 35) & 0xff) << 1;
if (ilo_dev_gen(dev) >= ILO_GEN(9)) {
src[0] |= ((tmp64 >> 43) & 0x3) << 19;
src[1] |= ((tmp64 >> 45) & 0x3) << 19;
src[2] |= ((tmp64 >> 47) & 0x3) << 19;
} else {
src[0] |= ((tmp64 >> 43) & 0x1) << 19;
src[1] |= ((tmp64 >> 44) & 0x1) << 19;
src[2] |= ((tmp64 >> 45) & 0x1) << 19;
}
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_DST_REG);
dw[1] |= GEN_SHIFT32(tmp, GEN6_3SRC_DST_REG);
if (compact & GEN8_COMPACT_3SRC_SRC0_REPCTRL)
src[0] |= GEN6_3SRC_SRC_REPCTRL;
assert(compact
& GEN8_COMPACT_3SRC_CMPTCTRL
);
if (compact & GEN8_COMPACT_3SRC_DEBUGCTRL)
dw[0] |= GEN6_INST_DEBUGCTRL;
if (compact & GEN8_COMPACT_3SRC_SATURATE)
dw[0] |= GEN6_INST_SATURATE;
if (compact & GEN8_COMPACT_3SRC_SRC1_REPCTRL)
src[1] |= GEN6_3SRC_SRC_REPCTRL;
if (compact & GEN8_COMPACT_3SRC_SRC2_REPCTRL)
src[2] |= GEN6_3SRC_SRC_REPCTRL;
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_SRC0_SUBREG);
src[0] |= GEN_SHIFT32(tmp, GEN6_3SRC_SRC_SUBREG);
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_SRC1_SUBREG);
src[1] |= GEN_SHIFT32(tmp, GEN6_3SRC_SRC_SUBREG);
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_SRC2_SUBREG);
src[2] |= GEN_SHIFT32(tmp, GEN6_3SRC_SRC_SUBREG);
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_SRC0_REG);
src[0] |= GEN_SHIFT32(tmp, GEN6_3SRC_SRC_REG);
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_SRC1_REG);
src[1] |= GEN_SHIFT32(tmp, GEN6_3SRC_SRC_REG);
tmp = GEN_EXTRACT(compact, GEN8_COMPACT_3SRC_SRC2_REG);
src[2] |= GEN_SHIFT32(tmp, GEN6_3SRC_SRC_REG);
tmp64 = (uint64_t) src[2] << 42 |
(uint64_t) src[1] << 21 |
(uint64_t) src[0];
dw[2] = (uint32_t) tmp64;
dw[3] = (uint32_t) (tmp64 >> 32);
}
static void
disasm_uncompact(const struct ilo_dev *dev,
uint64_t compact, uint32_t *dw)
{
const struct toy_compaction_table *tbl =
toy_compiler_get_compaction_table(dev);
bool src_is_imm;
uint32_t tmp;
ILO_DEV_ASSERT(dev, 6, 8);
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_OPCODE);
if (disasm_opcode_table[tmp].src_count == 3) {
disasm_uncompact_3src(dev, compact, dw);
return;
}
memset(dw
, 0, sizeof(*dw
) * 4);
dw[0] |= GEN_SHIFT32(tmp, GEN6_INST_OPCODE);
if (ilo_dev_gen(dev) >= ILO_GEN(7) && (compact & GEN6_COMPACT_DEBUGCTRL))
dw[0] |= GEN6_INST_DEBUGCTRL;
/* ControlIndex */
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_CONTROL_INDEX);
tmp = tbl->control[tmp];
dw[0] |= (tmp & 0xffff) << GEN6_INST_ACCESSMODE__SHIFT;
if (tmp & 0x10000)
dw[0] |= GEN6_INST_SATURATE;
if (ilo_dev_gen(dev) >= ILO_GEN(7))
dw[2] |= (tmp >> 17) << GEN6_INST_FLAG_SUBREG__SHIFT;
/* DataTypeIndex */
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_DATATYPE_INDEX);
tmp = tbl->datatype[tmp];
dw[1] |= (tmp & 0x7fff) << GEN6_INST_DST_FILE__SHIFT;
dw[1] |= (tmp >> 15) << GEN6_INST_DST_HORZSTRIDE__SHIFT;
/* SubRegIndex */
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_SUBREG_INDEX);
tmp = tbl->subreg[tmp];
dw[1] |= (tmp & 0x1f) << 16;
dw[2] |= ((tmp >> 5) & 0x1f);
dw[3] |= ((tmp >> 10) & 0x1f);
if (compact & GEN6_COMPACT_ACCWRCTRL)
dw[0] |= GEN6_INST_ACCWRCTRL;
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_CONDMODIFIER);
dw[0] |= GEN_SHIFT32(tmp, GEN6_INST_CONDMODIFIER);
if (ilo_dev_gen(dev) == ILO_GEN(6)) {
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_FLAG_SUBREG);
dw[2] |= GEN_SHIFT32(compact, GEN6_INST_FLAG_SUBREG);
}
assert(compact
& GEN6_COMPACT_CMPTCTRL
);
/* Src0Index */
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_SRC0_INDEX);
tmp = tbl->src[tmp];
dw[2] |= tmp << 13;
src_is_imm = (GEN_EXTRACT(dw[1], GEN6_INST_SRC0_FILE) == GEN6_FILE_IMM) ||
(GEN_EXTRACT(dw[1], GEN6_INST_SRC1_FILE) == GEN6_FILE_IMM);
/* Src1Index */
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_SRC1_INDEX);
if (src_is_imm) {
if (tmp & 0x10)
tmp |= 0xfffff0;
dw[3] |= tmp << 8;
} else {
tmp = tbl->src[tmp];
dw[3] |= tmp << 13;
}
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_DST_REG);
dw[1] |= GEN_SHIFT32(tmp, GEN6_INST_DST_REG);
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_SRC0_REG);
dw[2] |= GEN_SHIFT32(tmp, GEN6_INST_SRC_REG);
tmp = GEN_EXTRACT(compact, GEN6_COMPACT_SRC1_REG);
if (src_is_imm)
dw[3] |= tmp;
else
dw[3] |= GEN_SHIFT32(tmp, GEN6_INST_SRC_REG);
}
void
toy_compiler_disassemble(const struct ilo_dev *dev,
const void *kernel, int size,
bool dump_hex)
{
const uint32_t *cur = (const uint32_t *) kernel;
const uint32_t *end = cur + size / sizeof(*cur);
struct disasm_printer printer;
disasm_printer_reset(&printer);
while (cur < end) {
struct disasm_inst inst;
const bool compacted = (cur[0] & GEN6_INST_CMPTCTRL);
const uint32_t *dw = cur;
uint32_t temp[4];
cur += (compacted) ? 2 : 4;
/* incomplete instruction */
if (cur > end)
break;
if (compacted) {
const uint64_t compact = (uint64_t) dw[1] << 32 | dw[0];
disasm_uncompact(dev, compact, temp);
dw = temp;
}
if (dump_hex) {
ilo_printf("0x%08x 0x%08x 0x%08x 0x%08x ",
dw[0], dw[1], dw[2], dw[3]);
}
memset(&inst
, 0, sizeof(inst
));
inst.dev = dev;
disasm_inst_decode(&inst, dw);
inst.cmpt_ctrl = compacted;
disasm_printer_print_inst(&printer, &inst);
}
}