Files
intel-graphics-compiler/visa/IsaDisassembly.cpp
Gu, Junjie 600dbce903 Rename d16c32 to d16u32
As the specification uses d8u32/d16u32, change visaasm type
d8c32 to d8u32 and d16c32 to d16u32.
2025-05-30 22:28:55 +02:00

3586 lines
105 KiB
C++

/*========================== begin_copyright_notice ============================
Copyright (C) 2017-2021 Intel Corporation
SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/
//
// vISA IR Disassembler
//
// A vISA assembly file (.visaasm) has one or more kernels and global functions.
//
// A kernel (.kernel) represents an entry point of GPU execution. It may only be
// invoked from the host program.
//
// A global function (.global_function) may be invoked by a kernel or another
// function through the FCALL/IFCALL instruction.
//
// Each kernel or function contains a number of variables (.decl). By default
// the declares are printed at the top of the kernel/function (i.e., they are
// visible everywhere inside the function), though they can also be interleaved
// with the instructions so long as each variable is declared before its use.
// C-style block scope support also exists with the {} operator.
//
// A kernel may declare input variables via the .input directive, which also
// binds the variable to a fixed GRF location. Input variables are considered
// live-in to the kernel.
//
// Each kernel/function may declare a number of attributes (.kernel_attr
// directive) to communicate additional information about the function. They
// have to be declared before any instructions in the function.
//
// A kernel/function's instructions are organized into subroutines (.function)
// that are invoked through the CALL instruction. By convention the first
// subroutine represents the entry point of the kernel/function, and it may not
// be the target of a CALL.
//
#include "IGC/common/StringMacros.hpp"
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <list>
#include <sstream>
#include <string>
#include "Common_ISA.h"
#include "Common_ISA_framework.h"
#include "Common_ISA_util.h"
#include "IsaDisassembly.h"
#include "JitterDataStruct.h"
#include "Option.h"
#include "PlatformInfo.h"
#include "PreDefinedVars.h"
#include "VISABuilderAPIDefinition.h"
#include "VISAKernel.h"
#include "common.h"
#include "visa_igc_common_header.h"
using namespace vISA;
/// Output flags to control vISA assembly printing.
static const bool g_shortRegionPrint =
false; /// Use shorthand names for common regions.
static const bool g_inlineTypePrint =
false; /// Print the type information with operands.
static const bool g_prettyPrint = true; /// Line up the comments.
static const bool g_ignorelocs = false; /// Ignore printing LOCs.
static const bool g_noinstid = false; /// Ignore printing instruction id comments.
const char *getGenVarName(int id, const print_format_provider_t &header) {
int numPredefined = Get_CISA_PreDefined_Var_Count();
if (id < numPredefined) {
return getPredefinedVarString(mapExternalToInternalPreDefVar(id));
} else {
vISA_ASSERT_INPUT((id - numPredefined) < (int)header.getVarCount(),
"invalid vISA general variable id");
return header.getString(header.getVar(id - numPredefined)->name_index);
}
}
static std::string printSurfaceName(uint32_t declID) {
std::stringstream sstr;
unsigned numPreDefinedSurf = Get_CISA_PreDefined_Surf_Count();
if (declID < numPreDefinedSurf) {
sstr << vISAPreDefSurf[declID].name;
} else {
sstr << "T" << declID;
}
return sstr.str();
}
std::string printVariableDeclName(
const print_format_provider_t *header, unsigned declID,
const Options *options,
Common_ISA_State_Opnd_Class operand_prefix_kind = NOT_A_STATE_OPND) {
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
std::stringstream sstr;
unsigned numPreDefinedVars = Get_CISA_PreDefined_Var_Count();
if (operand_prefix_kind == NOT_A_STATE_OPND) {
sstr << getGenVarName(declID, *header);
} else {
switch (operand_prefix_kind) {
case STATE_OPND_SURFACE:
sstr << printSurfaceName(declID);
break;
case STATE_OPND_SAMPLER:
sstr << "S" << declID;
break;
default:
if (options->getuInt32Option(vISA_PlatformSet) == GENX_NONE) {
// If platform is not set then dcl instances are
// not created causing a crash. So print dcl name
// the old way.
if (declID < numPreDefinedVars) {
sstr << "V" << declID;
} else {
declID -= numPreDefinedVars;
const var_info_t *var = header->getVar(declID);
std::string name = header->getString(var->name_index);
sstr << name;
}
} else {
if (declID < numPreDefinedVars) {
sstr << "V" << declID;
} else {
G4_Declare *aliasDcl =
header->getVar(declID - numPreDefinedVars)->dcl;
unsigned int aliasOff = 0;
std::string type = TypeSymbol(aliasDcl->getElemType());
while (aliasDcl->getAliasDeclare() != NULL) {
aliasOff += aliasDcl->getAliasOffset();
aliasDcl = aliasDcl->getAliasDeclare();
}
// aliasDcl is top most dcl with aliasOff
// Lets find out declID of aliasDcl
for (unsigned int i = 0; i < header->getVarCount(); i++) {
if (header->getVar(i)->dcl == aliasDcl) {
declID = i + numPreDefinedVars;
break;
}
}
sstr << "V" << declID << "_" << type;
if (aliasOff != 0) {
sstr << "_" << aliasOff;
}
}
}
break;
}
}
return sstr.str();
}
static std::string printRegion(uint16_t region) {
std::stringstream sstr;
Common_ISA_Region_Val v_stride = (Common_ISA_Region_Val)(region & 0xF);
Common_ISA_Region_Val width = (Common_ISA_Region_Val)((region >> 4) & 0xF);
Common_ISA_Region_Val h_stride = (Common_ISA_Region_Val)((region >> 8) & 0xF);
if (width == REGION_NULL) {
// dst operand, only have horizontal stride
sstr << "<" << Common_ISA_Get_Region_Value(h_stride) << ">";
} else if (v_stride == REGION_NULL) {
// VxH mode for indirect operand
sstr << "<" << Common_ISA_Get_Region_Value(width) << ","
<< Common_ISA_Get_Region_Value(h_stride) << ">";
} else {
if (g_shortRegionPrint && 0 == Common_ISA_Get_Region_Value(v_stride) &&
1 == Common_ISA_Get_Region_Value(width) &&
0 == Common_ISA_Get_Region_Value(h_stride)) {
sstr << ".s";
} else if (g_shortRegionPrint &&
Common_ISA_Get_Region_Value(v_stride) ==
Common_ISA_Get_Region_Value(width) &&
1 == Common_ISA_Get_Region_Value(h_stride)) {
sstr << ".v";
} else {
sstr << "<" << Common_ISA_Get_Region_Value(v_stride) << ";"
<< Common_ISA_Get_Region_Value(width) << ","
<< Common_ISA_Get_Region_Value(h_stride) << ">";
}
}
return sstr.str();
}
std::string printVectorOperand(const print_format_provider_t *header,
const VISA_opnd *parentOpnd, const Options *opt,
bool showRegion) {
std::stringstream sstr;
auto opnd = parentOpnd->_opnd.v_opnd;
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
VISA_Modifier modifier = (VISA_Modifier)((opnd.tag >> 3) & 0x7);
/// .sat is dumped with the opcode
if (modifier == MODIFIER_SAT)
modifier = MODIFIER_NONE;
sstr << " ";
switch (opnd.tag & 0x7) {
case OPERAND_GENERAL: {
sstr << Common_ISA_Get_Modifier_Name(modifier)
<< printVariableDeclName(header, opnd.getOperandIndex(), opt,
NOT_A_STATE_OPND);
if ((!g_shortRegionPrint) ||
(!(opnd.opnd_val.gen_opnd.row_offset == 0 &&
(((opnd.opnd_val.gen_opnd.col_offset == 0)))))) {
sstr << "(" << (unsigned)opnd.opnd_val.gen_opnd.row_offset << ","
<< (unsigned)opnd.opnd_val.gen_opnd.col_offset << ")";
}
if (showRegion) {
sstr << printRegion(opnd.opnd_val.gen_opnd.region);
}
break;
}
case OPERAND_ADDRESS: {
sstr << Common_ISA_Get_Modifier_Name(modifier) << "A"
<< opnd.opnd_val.addr_opnd.index << "("
<< (unsigned)opnd.opnd_val.addr_opnd.offset << ")<"
<< Get_VISA_Exec_Size(
(VISA_Exec_Size)(opnd.opnd_val.addr_opnd.width & 0xF))
<< ">";
break;
}
case OPERAND_PREDICATE: {
sstr << Common_ISA_Get_Modifier_Name(modifier) << "P"
<< parentOpnd->convertToPred().getId();
break;
}
case OPERAND_INDIRECT: {
sstr << Common_ISA_Get_Modifier_Name(modifier) << "r[A"
<< opnd.opnd_val.indirect_opnd.index << "("
<< (unsigned)opnd.opnd_val.indirect_opnd.addr_offset << "),"
<< (short)opnd.opnd_val.indirect_opnd.indirect_offset << "]";
sstr << printRegion(opnd.opnd_val.indirect_opnd.region);
VISA_Type type =
(VISA_Type)(opnd.opnd_val.indirect_opnd.bit_property & 0xf);
sstr << ":" << CISATypeTable[type].typeName;
break;
}
case OPERAND_ADDRESSOF: {
sstr << "&"
<< printVariableDeclName(header, opnd.getOperandIndex(), opt,
NOT_A_STATE_OPND);
if (opnd.opnd_val.addressof_opnd.addr_offset >= 0) {
sstr << "[" << (((short)opnd.opnd_val.addressof_opnd.addr_offset)) << "]";
}
break;
}
case OPERAND_IMMEDIATE: {
VISA_Type type = (VISA_Type)(opnd.opnd_val.const_opnd.type & 0xF);
if (type == ISA_TYPE_DF)
sstr << "0x" << std::hex
<< *((uint64_t *)&opnd.opnd_val.const_opnd._val.dval) << ":"
<< CISATypeTable[type].typeName << std::dec;
else if (type == ISA_TYPE_Q || type == ISA_TYPE_UQ)
sstr << "0x" << std::hex << opnd.opnd_val.const_opnd._val.lval << ":"
<< CISATypeTable[type].typeName << std::dec;
else
sstr << "0x" << std::hex << opnd.opnd_val.const_opnd._val.ival << ":"
<< CISATypeTable[type].typeName << std::dec;
break;
}
case OPERAND_STATE: {
sstr << printVariableDeclName(header, opnd.getOperandIndex(), opt,
(Common_ISA_State_Opnd_Class)
opnd.opnd_val.state_opnd.opnd_class)
<< "(" << (unsigned)opnd.opnd_val.state_opnd.offset << ")";
break;
}
default:
vISA_ASSERT_UNREACHABLE(
"Attempted to dump an invalid or unimplemented vector operand type.");
}
return sstr.str();
}
std::string printRawOperand(const print_format_provider_t *header,
const raw_opnd &opnd, const Options *opt) {
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
std::stringstream sstr;
sstr << " "
<< printVariableDeclName(header, opnd.index, opt, NOT_A_STATE_OPND)
<< "." << opnd.offset;
return sstr.str();
}
static std::string printOperand(const print_format_provider_t *header,
const CISA_INST *inst, unsigned i,
const Options *opt) {
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
vISA_ASSERT_INPUT(inst, "Argument Exception: argument inst is NULL.");
vISA_ASSERT_INPUT(inst->opnd_num > i,
"No such operand, i, for instruction inst.");
std::stringstream sstr;
switch (getOperandType(inst, i)) {
case CISA_OPND_OTHER:
sstr << (getPrimitiveOperand<unsigned>(inst, i));
break;
case CISA_OPND_VECTOR:
sstr << printVectorOperand(header, inst->opnd_array[i], opt, true);
break;
case CISA_OPND_RAW:
sstr << printRawOperand(header, getRawOperand(inst, i), opt);
break;
default:
vISA_ASSERT_UNREACHABLE("Invalid operand type.");
}
return sstr.str();
}
static void encodeStringLiteral(std::stringstream &ss, std::string_view str) {
ss << '"';
for (size_t i = 0, slen = str.size(); i < slen; i++) {
switch (str[i]) { // unsigned so >0x7F doesn't sign ext.
case '\a':
ss << '\\';
ss << 'a';
break;
case '\b':
ss << '\\';
ss << 'b';
break;
case 0x1B:
ss << '\\';
ss << 'e';
break;
case '\f':
ss << '\\';
ss << 'f';
break;
case '\n':
ss << '\\';
ss << 'n';
break;
case '\r':
ss << '\\';
ss << 'r';
break;
case '\t':
ss << '\\';
ss << 't';
break;
case '\v':
ss << '\\';
ss << 'v';
break;
//
case '\'':
ss << '\\';
ss << '\'';
break;
case '"':
ss << '\\';
ss << '"';
break;
//
case '\\':
ss << '\\';
ss << '\\';
break;
default:
if (std::isprint((unsigned char)str[i])) {
ss << str[i];
} else {
ss << "\\x" << std::setw(2) << std::setfill('0') << std::hex
<< (unsigned)((unsigned char)str[i]);
}
}
}
ss << '"';
}
// Return true if str matches IDENT: [[:alpha:]_][[:alnum:]_]*
static bool isIdentifier(const char *str) {
if (str == nullptr || *str == '\0')
return false;
if (!std::isalpha((unsigned char)str[0]) && str[0] != '_')
return false;
for (size_t i = 1; str[i] != '\0'; i++) {
if (!std::isalnum((unsigned char)str[i]) && str[i] != '_')
return false;
}
return true;
}
std::string printAttributes(const print_format_provider_t *header,
const int attr_count,
const attribute_info_t *attrs) {
std::stringstream sstr;
if (attr_count > 0) {
// decl's attr in the form: attr=<attr0, attr1, ...>
sstr << " attrs={" << printOneAttribute(header, &attrs[0]);
for (int j = 1; j < attr_count; j++) {
sstr << ", " << printOneAttribute(header, &attrs[j]);
}
sstr << "}";
}
return sstr.str();
}
std::string printOneAttribute(const print_format_provider_t *kernel,
const attribute_info_t *attr) {
std::stringstream sstr;
const char *attrName = kernel->getString(attr->nameIndex);
Attributes::ID aID = Attributes::getAttributeID(attrName);
vISA_ASSERT_INPUT(aID != Attributes::ATTR_INVALID, "Invalid Attribute names!");
sstr << attrName;
if (attr->isInt && Attributes::isInt32(aID)) {
sstr << "=";
if (Attributes::isAttribute(Attributes::ATTR_Target, attrName)) {
switch (attr->value.intVal) {
case VISA_CM:
sstr << "\"cm\"";
break;
case VISA_3D:
sstr << "\"3d\"";
break;
default:
sstr << attr->value.intVal;
}
} else {
sstr << attr->value.intVal;
}
} else if (Attributes::isCStr(aID) && attr->size > 0) {
sstr << "=";
encodeStringLiteral(sstr, attr->value.stringVal);
}
return sstr.str();
}
std::string printPredicateDecl(const print_format_provider_t *header,
unsigned declID) {
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
std::stringstream sstr;
const pred_info_t *pred = header->getPred(declID);
sstr << ".decl P" << declID + COMMON_ISA_NUM_PREDEFINED_PRED << " "
<< "v_type=P "
<< "num_elts=" << pred->num_elements;
sstr << printAttributes(header, pred->attribute_count, pred->attributes);
return sstr.str();
}
std::string printAddressDecl(const print_format_provider_t *header,
unsigned declID) {
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
std::stringstream sstr;
const addr_info_t *addr = header->getAddr(declID);
sstr << ".decl A" << declID << " "
<< "v_type=A "
<< "num_elts=" << addr->num_elements;
sstr << printAttributes(header, addr->attribute_count, addr->attributes);
return sstr.str();
}
std::string printSamplerDecl(const print_format_provider_t *header,
unsigned declID) {
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
std::stringstream sstr;
const state_info_t *info = header->getSampler(declID);
sstr << ".decl S" << declID << " v_type=S";
sstr << " num_elts=" << info->num_elements;
sstr << " v_name=" << header->getString(info->name_index);
sstr << printAttributes(header, info->attribute_count, info->attributes);
return sstr.str();
}
std::string printSurfaceDecl(const print_format_provider_t *header,
unsigned declID, unsigned numPredefinedSurfaces) {
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
std::stringstream sstr;
const state_info_t *info = header->getSurface(declID);
sstr << ".decl T" << declID + numPredefinedSurfaces << " v_type=T";
sstr << " num_elts=" << info->num_elements;
sstr << " v_name=" << header->getString(info->name_index);
sstr << printAttributes(header, info->attribute_count, info->attributes);
return sstr.str();
}
std::string printFuncInput(const print_format_provider_t *header,
unsigned declID, const Options *options) {
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
std::stringstream sstr;
const input_info_t *input = header->getInput(declID);
if (!input->getImplicitKind()) {
sstr << ".input ";
} else {
sstr << input->getImplicitKindString() << " ";
}
if (INPUT_GENERAL == input->getInputClass()) {
sstr << printVariableDeclName(header, input->index, options);
} else {
const char *Input_Class_String[] = {"V", "S", "T"};
sstr << Input_Class_String[input->getInputClass()] << input->index;
}
sstr << " offset=" << input->offset;
sstr << " size=" << input->size;
return sstr.str();
}
// declID is in the range of [0..#user-var], pre-defnied are not included
std::string printVariableDecl(const print_format_provider_t *header,
unsigned declID, const Options *options) {
vISA_ASSERT_INPUT(header, "Argument Exception: argument header is NULL.");
std::stringstream sstr;
const var_info_t *var = header->getVar(declID);
VISA_Type isa_type = (VISA_Type)((var->bit_properties) & 0xF);
VISA_Align align = var->getAlignment();
G4_Declare *aliasDcl = var->dcl->getRootDeclare();
unsigned numPreDefinedVars = Get_CISA_PreDefined_Var_Count();
sstr << ".decl "
<< printVariableDeclName(header, declID + numPreDefinedVars, options)
<< " v_type=G"
<< " type=" << CISATypeTable[isa_type].typeName
<< " num_elts=" << var->num_elements;
if (align != ALIGN_UNDEF)
sstr << " align=" << Common_ISA_Get_Align_Name(align);
if (var->alias_index ||
(aliasDcl && strcmp(aliasDcl->getName(), "%null") == 0)) {
sstr << " alias=<";
sstr << printVariableDeclName(header, var->alias_index, options);
sstr << ", " << var->alias_offset << ">";
}
sstr << printAttributes(header, var->attribute_count, var->attributes);
return sstr.str();
}
static std::string printExecutionSize(uint8_t opcode, uint8_t execSize,
uint8_t subOp = 0) {
std::stringstream sstr;
if (hasExecSize((ISA_Opcode)opcode, subOp)) {
sstr << "(";
uint8_t emsk = ((execSize >> 0x4) & 0xF);
sstr << emask_str[emsk] << ", ";
sstr << (unsigned)Get_VISA_Exec_Size((VISA_Exec_Size)(execSize & 0xF));
sstr << ")";
}
if (g_shortRegionPrint && !strcmp("(1)", sstr.str().c_str()))
return " ";
return sstr.str();
}
// execution size is formatted differently for
// scatter/gather/scatter4/gather4/scatter4_typed/gather4_typed
static std::string printExecutionSizeForScatterGather(uint8_t sizeAndMask) {
std::stringstream sstr;
sstr << "(";
VISA_EMask_Ctrl emask = (VISA_EMask_Ctrl)((sizeAndMask >> 0x4) & 0xF);
sstr << emask_str[emask] << ", ";
unsigned execSize = 0;
switch (sizeAndMask & 0x3) {
case 0:
execSize = 8;
break;
case 1:
execSize = 16;
break;
case 2:
execSize = 1;
break;
default:
vISA_ASSERT_UNREACHABLE("illegal execution size for scatter/gather message");
}
sstr << execSize;
sstr << ")";
return sstr.str();
}
static std::string printPredicate(uint8_t opcode, PredicateOpnd predOpnd) {
std::stringstream sstr;
if (hasPredicate((ISA_Opcode)opcode) && !predOpnd.isNullPred()) {
sstr << "(";
if (predOpnd.isInverse())
sstr << "!";
sstr << "P" << predOpnd.getId();
VISA_PREDICATE_CONTROL control = predOpnd.getControl();
switch (control) {
case PRED_CTRL_ANY:
sstr << ".any";
break;
case PRED_CTRL_ALL:
sstr << ".all";
break;
default:
break;
}
sstr << ") ";
}
return sstr.str();
}
static void printAtomicSubOpc(std::stringstream &sstr, uint8_t value) {
VISAAtomicOps op = static_cast<VISAAtomicOps>(value & 0x1F);
sstr << "." << CISAAtomicOpNames[op];
if ((value >> 5) == 1) {
sstr << ".16";
} else if ((value >> 6) == 1) {
sstr << ".64";
}
}
constexpr const char *channel_mask_str[CHANNEL_MASK_NUM] = {
"", // 0000
"R", // 0001
"G", // 0010
"RG", // 0011
"B", // 0100
"RB", // 0101
"GB", // 0110
"RGB", // 0111
"A", // 1000
"RA", // 1001
"GA", // 1010
"RGA", // 1011
"BA", // 1100
"RBA", // 1101
"GBA", // 1110
"RGBA" // 1111
};
static std::string printInstructionSVM(const print_format_provider_t *header,
const CISA_INST *inst,
const Options *opt) {
unsigned i = 0;
std::stringstream sstr;
SVMSubOpcode subOpcode =
(SVMSubOpcode)getPrimitiveOperand<uint8_t>(inst, i++);
if ((subOpcode != SVM_BLOCK_LD) && (subOpcode != SVM_BLOCK_ST)) {
sstr << printPredicate(inst->opcode, inst->pred);
}
sstr << "svm_";
switch (subOpcode) {
case SVM_BLOCK_ST:
case SVM_BLOCK_LD: {
sstr << "block_" << (subOpcode == SVM_BLOCK_ST ? "st" : "ld");
uint8_t properties = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << (properties & 0x80 ? ".unaligned" : ".aligned");
VISA_Oword_Num numOwords = (VISA_Oword_Num)(properties & 0x7);
sstr << " (" << Get_VISA_Oword_Num(numOwords) << ")";
break;
}
case SVM_GATHER:
case SVM_SCATTER: {
sstr << (subOpcode == SVM_GATHER ? "gather" : "scatter");
uint8_t block_size = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t num_blocks = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << "."
<< Get_Common_ISA_SVM_Block_Size((VISA_SVM_Block_Type)block_size);
sstr << "." << Get_Common_ISA_SVM_Block_Num((VISA_SVM_Block_Num)num_blocks);
sstr << " " << printExecutionSize(inst->opcode, inst->execsize, subOpcode);
break;
}
case SVM_ATOMIC: {
sstr << "atomic";
/// TODO: Need platform information for this to work.
printAtomicSubOpc(sstr, getPrimitiveOperand<uint8_t>(inst, i++));
sstr << " " << printExecutionSize(inst->opcode, inst->execsize, subOpcode);
/// element offset
sstr << printOperand(header, inst, i++, opt);
/// DWORD_ATOMIC is weird and has the text version
/// putting the dst operand before the src operands.
std::stringstream sstr1;
/// src0
sstr1 << printOperand(header, inst, i++, opt);
/// src1
sstr1 << printOperand(header, inst, i++, opt);
/// message operand (src or dst)
sstr << printOperand(header, inst, i++, opt);
sstr << sstr1.str();
break;
}
case SVM_GATHER4SCALED:
case SVM_SCATTER4SCALED: {
sstr << (subOpcode == SVM_GATHER4SCALED ? "gather4scaled"
: "scatter4scaled");
unsigned chMask = getPrimitiveOperand<uint8_t>(inst, i++);
// scale is ignored (MBZ)
(void)getPrimitiveOperand<uint16_t>(inst, i++);
sstr << "." << channel_mask_str[chMask];
sstr << " " << printExecutionSize(inst->opcode, inst->execsize, subOpcode);
sstr << printOperand(header, inst, i++, opt);
sstr << printOperand(header, inst, i++, opt);
sstr << printOperand(header, inst, i++, opt);
break;
}
default:
vISA_ASSERT_UNREACHABLE("Unimplemented or Illegal SVM Sub Opcode.");
}
for (; i < inst->opnd_num; i++)
sstr << printOperand(header, inst, i, opt);
return sstr.str();
}
static std::string printInstructionCommon(const print_format_provider_t *header,
const CISA_INST *inst,
const Options *opt) {
ISA_Opcode opcode = (ISA_Opcode)inst->opcode;
// TODO: Revisit to see if there's a better way to access platform info in
// this file. Now it should be fine to get platform and PlatformInfo from
// the option as this is not a performance critical path.
TARGET_PLATFORM platform =
static_cast<TARGET_PLATFORM>(opt->getuInt32Option(vISA_PlatformSet));
const PlatformInfo *platInfo = PlatformInfo::LookupPlatformInfo(platform);
vISA_ASSERT_INPUT(platInfo != nullptr, "Failed to look up platform");
std::stringstream sstr;
sstr << printPredicate(inst->opcode, inst->pred);
unsigned i = 0;
/// Print opcode
if (opcode == ISA_FMINMAX) {
CISA_MIN_MAX_SUB_OPCODE sub_opcode =
(CISA_MIN_MAX_SUB_OPCODE)getPrimitiveOperand<uint8_t>(inst, i++);
sstr << (sub_opcode == CISA_DM_FMIN ? "min" : "max");
} else {
sstr << ISA_Inst_Table[opcode].str;
}
if (ISA_Inst_Sync != ISA_Inst_Table[opcode].type) {
unsigned int Count = inst->opnd_num;
if (ISA_Inst_Compare == ISA_Inst_Table[opcode].type) {
uint8_t relOp = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << (((relOp >> 7) & 0x1)
? "n"
: ""); /// INFO: cmpn opcode print support here.
sstr << "." << Rel_op_str[(unsigned)(relOp & 0x7)];
} else if (opcode == ISA_BFN) {
// print BooleanFuncCtrl right after op name
sstr << ".x" << std::hex
<< (uint32_t)(getPrimitiveOperand<uint8_t>(inst, Count - 1))
<< std::dec;
// The following shall skip booleanFuncCtrl opnd
--Count;
}
if (ISA_Inst_Arith == ISA_Inst_Table[opcode].type ||
ISA_Inst_Mov == ISA_Inst_Table[opcode].type ||
ISA_Inst_Logic == ISA_Inst_Table[opcode].type ||
ISA_Inst_Address == ISA_Inst_Table[opcode].type ||
ISA_Inst_Compare == ISA_Inst_Table[opcode].type) {
bool saturate =
(((VISA_Modifier)((getVectorOperand(inst, i).tag >> 3) & 0x7)) ==
MODIFIER_SAT);
sstr << (saturate ? ".sat" : "");
}
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
if (opcode == ISA_GOTO) {
uint32_t label_id = getPrimitiveOperand<uint32_t>(inst, i++);
sstr << " " << header->getString(header->getLabel(label_id)->name_index);
}
for (; i < Count; i++) {
if (opcode == ISA_ADDR_ADD && i == 1) /// Only for src0 of addr_add
{
const vector_opnd &curOpnd = getVectorOperand(inst, i);
if (curOpnd.getOperandClass() == OPERAND_ADDRESS) {
sstr << printVectorOperand(header, inst->opnd_array[i], opt, true);
} else {
sstr << " "
<< Common_ISA_Get_Modifier_Name(
(VISA_Modifier)((curOpnd.tag >> 3) & 0x7));
unsigned opnd_index = curOpnd.getOperandIndex();
if (curOpnd.getOperandClass() == OPERAND_GENERAL) {
uint32_t numPredefined = Get_CISA_PreDefined_Var_Count();
VISA_Type type =
opnd_index < numPredefined
? getPredefinedVarType(
mapExternalToInternalPreDefVar(opnd_index))
: header->getVar(opnd_index - numPredefined)->getType();
sstr << "&" << printVariableDeclName(header, opnd_index, opt);
int offset = curOpnd.opnd_val.gen_opnd.col_offset *
CISATypeTable[type].typeSize +
curOpnd.opnd_val.gen_opnd.row_offset *
platInfo->numEltPerGRF<Type_UB>();
if (offset) {
sstr << "[" << offset << "]";
}
} else if (curOpnd.getOperandClass() == OPERAND_STATE) {
auto OpClass = curOpnd.getStateOpClass();
sstr << "&"
<< printVariableDeclName(header, opnd_index, opt, OpClass);
int offset = curOpnd.opnd_val.state_opnd.offset *
CISATypeTable[ISA_TYPE_D].typeSize;
if (offset) {
sstr << "[" << offset << "]";
}
} else {
/// TODO: Should we just assert here? Is this allowed?
sstr << printOperand(header, inst, i, opt);
}
}
} else {
sstr << printOperand(header, inst, i, opt);
}
}
} else {
if (opcode == ISA_FENCE) {
uint8_t mask = getPrimitiveOperand<uint8_t>(inst, i);
const int SWFenceMask = 0x80;
if (mask & SWFenceMask) {
sstr << "_sw";
} else {
#define BTI_MASK 0x20 // bit 5
sstr << ((mask & BTI_MASK) ? "_local" : "_global");
if (mask != 0) {
sstr << ".";
if (mask & 1)
sstr << "E";
if (mask & (1 << 1))
sstr << "I";
if (mask & (1 << 2))
sstr << "S";
if (mask & (1 << 3))
sstr << "C";
if (mask & (1 << 4))
sstr << "R";
if (mask & (1 << 6))
sstr << "L1";
}
}
} else if (opcode == ISA_WAIT) {
sstr << printOperand(header, inst, 0, opt);
} else if (opcode == ISA_SBARRIER) {
uint8_t mode = getPrimitiveOperand<uint8_t>(inst, i);
sstr << (mode ? ".signal" : ".wait");
} else if (opcode == ISA_NBARRIER) {
uint8_t mode = getPrimitiveOperand<uint8_t>(inst, i);
bool isSignal = (mode > 0);
sstr << (isSignal ? ".signal" : ".wait");
sstr << printOperand(header, inst, 1, opt);
if (isSignal) {
if (mode == 1) {
// nbarrier.signal <id> <num_threads>
sstr << printOperand(header, inst, 3, opt);
} else {
// nbarrier.signal <id> <<type> <num_prods> <num_cons>
sstr << printOperand(header, inst, 2, opt);
sstr << printOperand(header, inst, 3, opt);
sstr << printOperand(header, inst, 4, opt);
}
}
}
}
return sstr.str();
}
static std::string
printInstructionControlFlow(const print_format_provider_t *header,
const CISA_INST *inst, const Options *opt) {
const ISA_Opcode opcode = (ISA_Opcode)inst->opcode;
unsigned i = 0;
uint32_t label_id = 0;
std::stringstream sstr;
// Subroutine function name may contains "." or "$" generated by llvm when
// cloning functions. Replace those to underline in visaasm so that it can be
// accepted by visaasm reader.
auto replaceInvalidCharToUnderline = [](std::string str) {
std::replace(str.begin(), str.end(), '.', '_');
std::replace(str.begin(), str.end(), '$', '_');
return str;
};
auto getUniqueSuffixStr = [](uint32_t labelID) {
std::stringstream uniqueSuffixSstr;
uniqueSuffixSstr << '_' << labelID;
return uniqueSuffixSstr.str();
};
if (ISA_SUBROUTINE == opcode || ISA_LABEL == opcode) {
label_id = getPrimitiveOperand<uint16_t>(inst, i++);
sstr << "\n";
std::string labelName(
header->getString(header->getLabel(label_id)->name_index));
switch (opcode) {
case ISA_SUBROUTINE: {
std::string uniqueSuffixStr = getUniqueSuffixStr(label_id);
auto replacedName = replaceInvalidCharToUnderline(labelName);
bool printOrigName = replacedName != labelName;
// Avoid repeatedly appending the unique suffix.
if (!strEndsWith(replacedName, uniqueSuffixStr))
replacedName += uniqueSuffixStr;
sstr << "\n.function ";
encodeStringLiteral(sstr, replacedName.c_str());
// add a comment to specify the original name if the name change
if (printOrigName) {
sstr << " /// Original Name: ";
encodeStringLiteral(sstr, labelName.c_str());
}
sstr << "\n" << replacedName;
break;
}
case ISA_LABEL: {
sstr << replaceInvalidCharToUnderline(labelName);
break;
}
default:
break;
}
sstr << ":";
} else if (opcode == ISA_CALL) {
// Special handlling of CALL to distinguish fc_call from subroutine call
label_id = getPrimitiveOperand<uint32_t>(inst, i++);
const label_info_t *lblinfo = header->getLabel(label_id);
const char *instName =
(lblinfo->kind == LABEL_FC ? "fccall" : ISA_Inst_Table[opcode].str);
auto replacedName =
replaceInvalidCharToUnderline(header->getString(lblinfo->name_index));
std::string uniqueSuffixStr = getUniqueSuffixStr(label_id);
sstr << printPredicate(opcode, inst->pred) << instName << " "
<< printExecutionSize(opcode, inst->execsize) << " " << replacedName;
// Avoid repeatedly appending the unique suffix.
if (!strEndsWith(replacedName, uniqueSuffixStr))
sstr << uniqueSuffixStr;
} else {
sstr << printPredicate(inst->opcode, inst->pred)
<< ISA_Inst_Table[opcode].str;
switch (opcode) {
case ISA_JMP:
case ISA_GOTO:
case ISA_FCALL: {
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
/// label / function id to jump / call to.
label_id = getPrimitiveOperand<uint32_t>(inst, i++);
if (opcode == ISA_FCALL) {
/// function name in string
sstr << " "
<< replaceInvalidCharToUnderline(header->getString(label_id));
} else {
sstr << " "
<< replaceInvalidCharToUnderline(
header->getString(header->getLabel(label_id)->name_index));
vISA_ASSERT(header->getLabel(label_id)->kind != LABEL_SUBROUTINE,
"JMP/GOTO are not allowed to have subroutine label target");
}
if (opcode == ISA_FCALL) {
/// arg size
sstr << " " << getPrimitiveOperand<unsigned>(inst, i++);
/// return size
sstr << " " << getPrimitiveOperand<unsigned>(inst, i++);
}
break;
}
case ISA_IFCALL: {
/// isUniform
auto isUniform = getPrimitiveOperand<unsigned>(inst, i++);
if (isUniform)
sstr << ".uniform";
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
sstr << printOperand(header, inst, i++, opt);
/// arg size
sstr << " " << getPrimitiveOperand<unsigned>(inst, i++);
/// return size
sstr << " " << getPrimitiveOperand<unsigned>(inst, i++);
break;
}
case ISA_FADDR: {
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
/// symbol name in string
const char *sym =
header->getString(getPrimitiveOperand<uint32_t>(inst, i++));
if (isIdentifier(sym)) {
sstr << sym;
} else {
encodeStringLiteral(sstr, sym);
}
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_SWITCHJMP: {
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
/// skip num_labels
i++;
/// index
sstr << printOperand(header, inst, i++, opt);
sstr << " (";
for (bool first = true; i < inst->opnd_num; i++) {
if (!first) {
sstr << ", ";
}
label_id = getPrimitiveOperand<uint32_t>(inst, i);
sstr << header->getString(header->getLabel(label_id)->name_index);
if (first) {
first = false;
}
}
sstr << ")";
break;
}
default:
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
break; // Prevent gcc warning
}
}
return sstr.str();
}
static std::string printInstructionMisc(const print_format_provider_t *header,
const CISA_INST *inst,
const Options *opt) {
ISA_Opcode opcode = (ISA_Opcode)inst->opcode;
unsigned i = 0;
std::stringstream sstr;
if (opcode == ISA_3D_URB_WRITE) {
sstr << printPredicate(inst->opcode, inst->pred);
}
switch (opcode) {
case ISA_FILE: {
uint32_t filename_index = getPrimitiveOperand<uint32_t>(inst, i++);
sstr << "FILE ";
encodeStringLiteral(sstr, header->getString(filename_index));
break;
}
case ISA_LOC: {
unsigned line_number = getPrimitiveOperand<unsigned>(inst, i++);
sstr << "LOC " << line_number;
break;
}
case ISA_RAW_SEND: {
uint8_t modifiers = inst->modifier;
i++; // skip the modifier
uint32_t exMsgDesc = getPrimitiveOperand<uint32_t>(inst, i++); // 32b
uint8_t numSrc = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t numDst = getPrimitiveOperand<uint8_t>(inst, i++);
std::string opstring = modifiers == 1 ? "raw_sendc " : "raw_send ";
sstr << printPredicate(inst->opcode, inst->pred) << opstring.c_str()
<< printExecutionSize(inst->opcode, inst->execsize) << " "
<< "0x" << std::hex << (uint32_t)exMsgDesc << std::dec << " "
<< (unsigned)numSrc << " " << (unsigned)numDst << " ";
/// desc
sstr << printOperand(header, inst, i++, opt);
/// src
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_RAW_SENDS: {
uint8_t modifiers = inst->modifier;
i++; // skip the modifier
uint8_t numSrc0 = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t numSrc1 = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t numDst = getPrimitiveOperand<uint8_t>(inst, i++);
bool isCond = modifiers & (0x1 << 0);
bool issuesEoT = modifiers & (0x1 << 1);
std::string opstring = !issuesEoT && !isCond ? "raw_sends."
: issuesEoT && !isCond ? "raw_sends_eot."
: !issuesEoT && isCond ? "raw_sendsc."
: "raw_sendsc_eot.";
sstr << printPredicate(inst->opcode, inst->pred) << opstring.c_str();
uint8_t ffid = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << (unsigned)ffid << ".";
sstr << (unsigned)numSrc0 << "." << (unsigned)numSrc1 << "."
<< (unsigned)numDst << " "
<< printExecutionSize(inst->opcode, inst->execsize) << " ";
/// exMsgDesc: could be imm or vector
sstr << printOperand(header, inst, i++, opt);
/// desc
sstr << printOperand(header, inst, i++, opt);
/// src0
sstr << printOperand(header, inst, i++, opt);
/// src1
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_VME_FBR: {
/// My typical pattern of printing these things doesn't work here since
/// these VME instructions weirdly put the surface as the third operand.
std::stringstream sstr1;
/// uni input
sstr1 << printOperand(header, inst, i++, opt);
/// fbr input
sstr1 << printOperand(header, inst, i++, opt);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
std::stringstream sstr2;
sstr2 << " T" << (unsigned)surface << " " << sstr1.str();
sstr << ISA_Inst_Table[opcode].str << " (";
/// FBRMbMode
sstr << printOperand(header, inst, i++, opt);
sstr << ",";
/// FBRSubMbShape
sstr << printOperand(header, inst, i++, opt);
sstr << ",";
/// FBRSubPredMode
sstr << printOperand(header, inst, i++, opt);
sstr << ")";
/// vme output
sstr2 << printOperand(header, inst, i++, opt);
sstr << sstr2.str();
break;
}
case ISA_VME_IME: {
uint8_t streamMode = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t searchCtrl = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "(" << (unsigned)streamMode << ","
<< (unsigned)searchCtrl << ")";
std::stringstream sstr1;
/// uni imput
sstr1 << printOperand(header, inst, i++, opt);
/// ime input
sstr1 << printOperand(header, inst, i++, opt);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " T" << (unsigned)surface << " " << sstr1.str();
/// ref0
sstr << printOperand(header, inst, i++, opt);
/// ref1
sstr << printOperand(header, inst, i++, opt);
/// cost center
sstr << printOperand(header, inst, i++, opt);
/// vme output
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_VME_SIC: {
/// My typical pattern of printing these things doesn't work here since
/// these VME instructions weirdly put the surface as the third operand.
std::stringstream sstr1;
/// uni input
sstr1 << printOperand(header, inst, i++, opt);
/// sic input
sstr1 << printOperand(header, inst, i++, opt);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << " T" << (unsigned)surface << " "
<< sstr1.str();
/// vme output
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_VME_IDM: {
/// My typical pattern of printing these things doesn't work here since
/// these VME instructions weirdly put the surface as the third operand.
std::stringstream sstr1;
/// uni input
sstr1 << printOperand(header, inst, i++, opt);
/// sic input
sstr1 << printOperand(header, inst, i++, opt);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << " T" << (unsigned)surface << " "
<< sstr1.str();
/// vme output
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_3D_URB_WRITE: {
sstr << ISA_Inst_Table[opcode].str;
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
// num out
sstr << " " << printOperand(header, inst, i++, opt);
// channel mask
// FIXME: change the order of channel mask and global offset in vISA binary
std::string channelMask = printOperand(header, inst, i++, opt);
// global offset
sstr << " " << printOperand(header, inst, i++, opt);
sstr << channelMask;
// urb handle
sstr << printOperand(header, inst, i++, opt);
// per slot offset
sstr << printOperand(header, inst, i++, opt);
// vertex data
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_DPAS:
case ISA_DPASW:
{
const VISA_opnd *dpasOpnd = inst->opnd_array[inst->opnd_num - 1];
GenPrecision A, W;
uint8_t D, C;
UI32ToDpasInfo(dpasOpnd->_opnd.other_opnd, A, W, D, C);
sstr << ISA_Inst_Table[opcode].str << "." << toString(W) << "."
<< toString(A) << "." << (int)D << "." << (int)C;
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
// dst
sstr << printRawOperand(header, getRawOperand(inst, i++), opt);
// src0
sstr << printRawOperand(header, getRawOperand(inst, i++), opt);
// src1
sstr << printRawOperand(header, getRawOperand(inst, i++), opt);
// src2
sstr << printVectorOperand(header, inst->opnd_array[i++], opt, false);
break;
}
case ISA_LIFETIME: {
uint8_t properties = getPrimitiveOperand<uint8_t>(inst, i++);
uint32_t varId = getPrimitiveOperand<uint32_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str;
sstr << ".";
if ((VISAVarLifetime)(properties & 1) == LIFETIME_START) {
sstr << "start ";
} else {
sstr << "end ";
}
// Since variable id is in non-standard form, we cannot invoke
// printOperand directly on it
unsigned char type = (properties >> 4) & 0x3;
if (type == OPERAND_GENERAL) {
// General variable
sstr << printVariableDeclName(header, varId, opt, NOT_A_STATE_OPND);
} else if (type == OPERAND_ADDRESS) {
// Address variable
sstr << "A" << varId;
} else if (type == OPERAND_PREDICATE) {
// Predicate variable
sstr << "P" << varId;
}
break;
}
case ISA_BREAKPOINT: {
sstr << "breakpoint";
break;
}
default: {
vISA_ASSERT_UNREACHABLE("Unimplemented or Illegal Misc Opcode.");
}
}
return sstr.str();
}
// For 3D sampler instructions, subOpcode, pixel null mask and CPS LOD
// compensation enable share the same byte:
//
// Bit 0-4: subOpcode
// Bit 5: pixelNullMask
// Bit 6: cpsEnable
//
static VISA3DSamplerOp getSamplerSubOpcode(uint16_t majorV,
const CISA_INST *inst, unsigned i) {
// The vISA version that widens the opcode field is 4
// the below check is necessary to pick the correct
// width during decoding based on input binary version
const int WIDE_OPCODE_ISA_VER = 4;
auto val = (majorV >= WIDE_OPCODE_ISA_VER)
? getPrimitiveOperand<uint16_t>(inst, i)
: getPrimitiveOperand<uint8_t>(inst, i);
return VISA3DSamplerOp::extractSamplerOp(val);
}
constexpr const char *mmf_enable_mode[3] = {"VA_MINMAX_ENABLE", "VA_MAX_ENABLE",
"VA_MIN_ENABLE"};
constexpr const char *va_sub_names[26] = {
"avs", // 0x0
"convolve", // 0x1
"minmax", // 0x2
"minmaxfilter", // 0x3
"erode", // 0x4
"dilate", // 0x5
"boolcentroid", // 0x6
"centroid", // 0x7
"CONV_1D_HORIZONTAL", // 0x8
"CONV_1D_VERTICAL", // 0x9
"CONV_1PIXEL", // 0x10
"FLOOD_FILL", // 0x11
"LBP_CREATION", // 0x12
"LBP_CORRELATION", // 0x13
"", // 0x14
"CORRELATION_SEARCH", // 0x15
"HDC_CONVOLVE_2D", // 0x10
"HDC_MIN_MAX_FILTER", // 0x11
"HDC_ERODE", // 0x12
"HDC_DILATE", // 0x13
"HDC_LBP_CORRELATION", // 0x14
"HDC_LBP_CREATION", // 0x15
"HDC_CONVOLVE_1D_H", // 0x16
"HDC_CONVOLVE_1D_V", // 0x17
"HDC_CONVOLVE_1P", // 0x18
"UNDEFINED" // 0x19
};
constexpr const char *lbp_creation_mode[3] = {"VA_5x5_mode", "VA_3x3_mode",
"VA_BOTH_mode"};
static std::string
printInstructionSampler(const print_format_provider_t *header,
const CISA_INST *inst, const Options *opt) {
std::stringstream sstr;
ISA_Opcode opcode = (ISA_Opcode)inst->opcode;
if (opcode == ISA_3D_SAMPLE || opcode == ISA_3D_LOAD ||
opcode == ISA_3D_GATHER4) {
sstr << printPredicate(inst->opcode, inst->pred);
}
unsigned i = 0;
switch (opcode) {
case ISA_LOAD:
case ISA_SAMPLE: {
uint8_t mod = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t sampler = 0;
if (opcode == ISA_SAMPLE)
sampler = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t channel = (mod)&0xF;
uint8_t SIMD_mode = (mod >> 4) & 0x3;
if ((unsigned)SIMD_mode == 0) {
SIMD_mode = 8;
} else if ((unsigned)SIMD_mode == 1) {
SIMD_mode = 16;
}
sstr << ISA_Inst_Table[opcode].str << "." << channel_mask_str[channel]
<< " (" << (unsigned)SIMD_mode << ")";
if (opcode == ISA_SAMPLE)
sstr << " S" << (unsigned)sampler;
sstr << " " << printSurfaceName(surface);
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
/// r offset
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_3D_SAMPLE: {
// [(P)] SAMPLE_3d[.pixel_null_mask][.cps][.divS].<channels> (exec_size)
// [(u_aoffimmi, v_aoffimii, r_aoffimmi)] <sampler> <surface>
// <dst> <u> <v> <r> <ai>
auto subop = getSamplerSubOpcode(header->getMajorVersion(), inst, i++);
TARGET_PLATFORM platform =
static_cast<TARGET_PLATFORM>(opt->getuInt32Option(vISA_PlatformSet));
sstr << getSampleOp3DName(subop.opcode, platform) << ".";
// Print the pixel null mask if it is enabled.
if (subop.pixelNullMask) {
sstr << "pixel_null_mask.";
}
// Print CPS LOD compensation if it is enabled.
// The last '.' is for the channels.
if (subop.cpsEnable) {
sstr << "cps.";
}
if (subop.nonUniformSampler) {
sstr << "divS.";
}
uint8_t channels = getPrimitiveOperand<uint8_t>(inst, i++);
if (channels & 0x1)
sstr << "R";
if (channels & 0x2)
sstr << "G";
if (channels & 0x4)
sstr << "B";
if (channels & 0x8)
sstr << "A";
sstr << " " << printExecutionSize(inst->opcode, inst->execsize) << " ";
sstr << printOperand(header, inst, i++, opt);
// sampler
{
sstr << " S" << printOperand(header, inst, i++, opt);
i++; // skip reserved
}
// surface
{
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
i++; // skip reserved
}
// dst
sstr << printOperand(header, inst, i++, opt);
// paired resource
sstr << printOperand(header, inst, i++, opt);
// skip the param count
i++;
while (i < inst->opnd_num) {
sstr << printOperand(header, inst, i++, opt);
}
break;
}
case ISA_3D_LOAD: {
auto subop = getSamplerSubOpcode(header->getMajorVersion(), inst, i++);
TARGET_PLATFORM platform =
static_cast<TARGET_PLATFORM>(opt->getuInt32Option(vISA_PlatformSet));
sstr << getSampleOp3DName(subop.opcode, platform) << ".";
// Print the pixel null mask if it is enabled.
// The last '.' is for the channels.
if (subop.pixelNullMask) {
sstr << "pixel_null_mask.";
}
uint8_t channels = getPrimitiveOperand<uint8_t>(inst, i++);
if (channels & 0x1)
sstr << "R";
if (channels & 0x2)
sstr << "G";
if (channels & 0x4)
sstr << "B";
if (channels & 0x8)
sstr << "A";
sstr << " " << printExecutionSize(inst->opcode, inst->execsize) << " ";
sstr << printOperand(header, inst, i++, opt);
{
// surface
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
i++; // skip reserved
}
// dst
sstr << printOperand(header, inst, i++, opt);
// paired resource
sstr << printOperand(header, inst, i++, opt);
// skip the param count
i++;
while (i < inst->opnd_num) {
sstr << printOperand(header, inst, i++, opt);
}
break;
}
case ISA_3D_GATHER4: {
auto subop = getSamplerSubOpcode(header->getMajorVersion(), inst, i++);
TARGET_PLATFORM platform =
static_cast<TARGET_PLATFORM>(opt->getuInt32Option(vISA_PlatformSet));
sstr << getSampleOp3DName(subop.opcode, platform) << ".";
// Print the pixel null mask if it is enabled.
// The last '.' is for the channels.
if (subop.pixelNullMask) {
sstr << "pixel_null_mask.";
}
uint8_t channels = getPrimitiveOperand<uint8_t>(inst, i++);
if (channels == 0x0) {
sstr << "R";
} else if (channels == 0x1) {
sstr << "G";
} else if (channels == 0x2) {
sstr << "B";
} else if (channels == 0x3) {
sstr << "A";
} else {
sstr << "illegal";
}
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
sstr << printOperand(header, inst, i++, opt);
// sampler
{
sstr << " S" << printOperand(header, inst, i++, opt);
i++; // skip reserved
}
// surface
{
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
i++; // skip reserved
}
// dst
sstr << printOperand(header, inst, i++, opt);
// paired resource
sstr << printOperand(header, inst, i++, opt);
// skip the param count
i++;
while (i < inst->opnd_num) {
sstr << printOperand(header, inst, i++, opt);
}
break;
}
case ISA_3D_INFO: {
VISASampler3DSubOpCode subop =
(VISASampler3DSubOpCode)getPrimitiveOperand<uint8_t>(inst, i++);
TARGET_PLATFORM platform =
static_cast<TARGET_PLATFORM>(opt->getuInt32Option(vISA_PlatformSet));
sstr << getSampleOp3DName(subop, platform);
if (subop == VISA_3D_RESINFO || subop == VISA_3D_SAMPLEINFO) {
// channelMask
uint8_t channels = getPrimitiveOperand<uint8_t>(inst, i++);
ChannelMask chMask = ChannelMask::createFromBinary(ISA_3D_INFO, channels);
sstr << "." << chMask.getString();
}
sstr << " " << printExecutionSize(inst->opcode, inst->execsize) << " ";
{
// surface
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
i++; // skip the surface index
}
if (subop == VISA_3D_RESINFO) {
// lod
sstr << printOperand(header, inst, i++, opt);
}
// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_SAMPLE_UNORM: {
uint8_t channel = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t sampler = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "."
<< channel_mask_str[(channel & 0xf)] << "."
<< sampler_channel_output_str[ChannelMask::getChannelOutputFormat(
channel)]
<< " S" << (unsigned)sampler << " " << printSurfaceName(surface);
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
/// deltaU
sstr << printOperand(header, inst, i++, opt);
/// deltaV
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_AVS: {
uint8_t channel = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t sampler = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << channel_mask_str[channel]
<< " " << printSurfaceName(surface) << " S" << (unsigned)sampler;
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
/// delta u
sstr << printOperand(header, inst, i++, opt);
/// delta v
sstr << printOperand(header, inst, i++, opt);
/// u2d
sstr << printOperand(header, inst, i++, opt);
/// groupID
sstr << printOperand(header, inst, i++, opt);
/// verticalBlockNumber
sstr << printOperand(header, inst, i++, opt);
uint8_t cntrl = ((getPrimitiveOperand<uint8_t>(inst, i++)) & 0xF);
sstr << " " << avs_control_str[cntrl];
/// v2d
sstr << printOperand(header, inst, i++, opt);
uint8_t execMode = (getPrimitiveOperand<uint8_t>(inst, i++) & 0xF);
sstr << " " << avs_exec_mode[execMode];
// eifbypass
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_VA: {
ISA_VA_Sub_Opcode subOpcode =
(ISA_VA_Sub_Opcode)getPrimitiveOperand<uint8_t>(inst, i++);
switch (subOpcode) {
case MINMAX_FOPCODE: {
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode]
<< " " << printSurfaceName(surface);
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
/// mmf mode
if (getVectorOperand(inst, i).getOperandClass() == OPERAND_IMMEDIATE) {
unsigned val =
getVectorOperand(inst, i++).opnd_val.const_opnd._val.ival;
sstr << " " << mmf_enable_mode[val];
} else {
sstr << printOperand(header, inst, i++, opt);
}
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case MINMAXFILTER_FOPCODE: {
constexpr const char *mmf_exec_mode[4] = {"VA_MMF_16x4", "VA_MMF_INVALID",
"VA_MMF_16x1", "VA_MMF_1x1"};
uint8_t sampler = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode]
<< " " << printSurfaceName(surface) << " S" << (unsigned)sampler;
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
uint8_t cntrl = ((getPrimitiveOperand<uint8_t>(inst, i++)) & 0xF);
uint8_t execMode = ((getPrimitiveOperand<uint8_t>(inst, i++)) & 0xF);
sstr << " " << avs_control_str[cntrl] << " " << mmf_exec_mode[execMode];
/// mmf mode
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case BoolCentroid_FOPCODE:
case Centroid_FOPCODE: {
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode]
<< " " << printSurfaceName(surface);
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
/// v size
sstr << printOperand(header, inst, i++, opt);
/// h size
if (subOpcode == BoolCentroid_FOPCODE)
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case Convolve_FOPCODE:
case Dilate_FOPCODE:
case ERODE_FOPCODE: {
constexpr const char *conv_exec_mode[4] = {
"VA_CONV_16x4", "VA_CONV_INVALID", "VA_CONV_16x1", "VA_CONV_1x1"};
constexpr const char *ed_exec_mode[4] = {"VA_ED_64x4", "VA_ED_32x4",
"VA_ED_64x1", "VA_ED_32x1"};
uint8_t sampler = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode]
<< " " << printSurfaceName(surface) << " S" << (unsigned)sampler;
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
uint8_t execMode = getPrimitiveOperand<uint8_t>(inst, i) & 0x3;
uint8_t regionSize =
(getPrimitiveOperand<uint8_t>(inst, i++) & 0xC) >> 0x2;
sstr << " "
<< (Convolve_FOPCODE == subOpcode ? conv_exec_mode[execMode]
: ed_exec_mode[execMode]);
if (Convolve_FOPCODE == subOpcode)
sstr << " " << (regionSize & 0x1 ? "31x31" : "15x15");
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
default:
vISA_ASSERT_UNREACHABLE("Invalid VA sub-opcode");
}
break;
}
case ISA_VA_SKL_PLUS: {
ISA_VA_Sub_Opcode subOpcode =
(ISA_VA_Sub_Opcode)getPrimitiveOperand<uint8_t>(inst, i++);
switch (subOpcode) {
case VA_OP_CODE_LBP_CORRELATION: {
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode]
<< " " << printSurfaceName(surface);
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
/// disparity
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case VA_OP_CODE_1PIXEL_CONVOLVE:
case VA_OP_CODE_1D_CONVOLVE_VERTICAL:
case VA_OP_CODE_1D_CONVOLVE_HORIZONTAL: {
uint8_t sampler = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode]
<< " " << printSurfaceName(surface) << " S" << (unsigned)sampler;
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
uint8_t mode = ((getPrimitiveOperand<uint8_t>(inst, i++)) & 0xF);
switch (mode & 0x3) {
case 0:
sstr << " 4x16";
break;
case 2:
sstr << " 1x16";
break;
case 3:
sstr << " 1x1";
break;
}
/// offsets
if (subOpcode == VA_OP_CODE_1PIXEL_CONVOLVE)
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case VA_OP_CODE_LBP_CREATION: {
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode]
<< " " << printSurfaceName(surface);
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
uint8_t mode = ((getPrimitiveOperand<uint8_t>(inst, i++)) & 0xF);
sstr << " " << lbp_creation_mode[(int)mode];
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case VA_OP_CODE_FLOOD_FILL: {
uint8_t is8Connect = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode]
<< " " << (is8Connect & 0x1 ? "8_connect" : "4_connect");
/// pixel mask h direction
sstr << printOperand(header, inst, i++, opt);
/// pixel mask v left direction
sstr << printOperand(header, inst, i++, opt);
/// pixel mask v right direction
sstr << printOperand(header, inst, i++, opt);
/// loop count
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case VA_OP_CODE_CORRELATION_SEARCH: {
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode]
<< " " << printSurfaceName(surface);
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
/// vertical origin
sstr << printOperand(header, inst, i++, opt);
/// horizontal origin
sstr << printOperand(header, inst, i++, opt);
/// x direction size
sstr << printOperand(header, inst, i++, opt);
/// y direction size
sstr << printOperand(header, inst, i++, opt);
/// x direction search size
sstr << printOperand(header, inst, i++, opt);
/// y direction search size
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_HDC_CONV:
case ISA_HDC_ERODE:
case ISA_HDC_DILATE:
case ISA_HDC_LBPCORRELATION:
case ISA_HDC_LBPCREATION:
case ISA_HDC_MMF:
case ISA_HDC_1PIXELCONV:
case ISA_HDC_1DCONV_H:
case ISA_HDC_1DCONV_V: {
sstr << ISA_Inst_Table[opcode].str << "." << va_sub_names[subOpcode];
if (subOpcode != ISA_HDC_LBPCORRELATION &&
subOpcode != ISA_HDC_LBPCREATION) {
uint8_t sampler = getPrimitiveOperand<uint8_t>(inst, i++);
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface) << " S" << (unsigned)sampler;
} else {
/// surface
uint8_t surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
}
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
if (subOpcode == ISA_HDC_CONV || subOpcode == ISA_HDC_MMF ||
subOpcode == ISA_HDC_1PIXELCONV || subOpcode == ISA_HDC_1DCONV_H ||
subOpcode == ISA_HDC_1DCONV_V) {
// pixel size
uint8_t pixel_size = getPrimitiveOperand<uint8_t>(inst, i++);
int isBigKernel = 0;
constexpr const char *pixel_size_str[2] = {"VA_Y16_FORMAT",
"VA_Y8_FORMAT"};
if (subOpcode == ISA_HDC_CONV) {
isBigKernel = (pixel_size & (1 << 4));
pixel_size = pixel_size & 0xF;
}
sstr << " " << pixel_size_str[pixel_size];
if (subOpcode == ISA_HDC_CONV && isBigKernel) {
sstr << " 31x31";
} else if (subOpcode == ISA_HDC_CONV && !isBigKernel) {
sstr << " 15x15";
}
}
if (subOpcode == ISA_HDC_MMF) {
// mode
uint8_t mode = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << mmf_enable_mode[(int)mode];
}
if (subOpcode == ISA_HDC_LBPCREATION) {
// mode
uint8_t mode = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << lbp_creation_mode[(int)mode];
}
if (subOpcode == ISA_HDC_LBPCORRELATION) {
/// disparity
sstr << printOperand(header, inst, i++, opt);
}
if (subOpcode == ISA_HDC_1PIXELCONV) {
/// offsets
sstr << printOperand(header, inst, i++, opt);
}
/// dst surface
uint8_t dst_surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(dst_surface);
/// x offset
sstr << printOperand(header, inst, i++, opt);
/// y offset
sstr << printOperand(header, inst, i++, opt);
break;
}
default:
vISA_ASSERT_UNREACHABLE("Invalid VA sub-opcode");
}
break;
}
default:
vISA_ASSERT_UNREACHABLE("illegal opcode for sampler instruction");
}
return sstr.str();
}
static std::string
printInstructionDataport(const print_format_provider_t *header,
const CISA_INST *inst, const Options *opt) {
ISA_Opcode opcode = (ISA_Opcode)inst->opcode;
unsigned i = 0;
uint8_t surface = 0;
uint8_t modifier = 0;
std::stringstream sstr;
switch (opcode) {
default:
break;
case ISA_3D_RT_WRITE:
case ISA_GATHER4_SCALED:
case ISA_SCATTER4_SCALED:
case ISA_GATHER_SCALED:
case ISA_SCATTER_SCALED:
case ISA_DWORD_ATOMIC:
case ISA_3D_TYPED_ATOMIC:
case ISA_QW_GATHER:
case ISA_QW_SCATTER:
sstr << printPredicate(inst->opcode, inst->pred);
break;
}
sstr << ISA_Inst_Table[opcode].str;
switch (opcode) {
case ISA_MEDIA_ST:
case ISA_MEDIA_LD: {
uint8_t plane = 0;
uint8_t block_width = 0;
uint8_t block_height = 0;
if (ISA_MEDIA_LD == opcode || ISA_MEDIA_ST == opcode) {
modifier = getPrimitiveOperand<uint8_t>(inst, i++); // inst->modifier;
}
surface = getPrimitiveOperand<uint8_t>(inst, i++);
if (ISA_MEDIA_LD == opcode || ISA_MEDIA_ST == opcode) {
plane = getPrimitiveOperand<uint8_t>(inst, i++);
}
constexpr const char *media_st_mod_str[MEDIA_ST_Mod_NUM] = {
"nomod",
"reserved", // this is useless since it is for MEDIA_ST_reserved
"top", "bottom"};
if (opcode == ISA_MEDIA_LD)
sstr << "." << media_ld_mod_str[modifier];
if (opcode == ISA_MEDIA_ST)
sstr << "." << media_st_mod_str[modifier];
sstr << " (";
block_width = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << (unsigned)block_width;
sstr << ",";
block_height = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << (unsigned)block_height;
sstr << ")";
sstr << " " << printSurfaceName(surface);
sstr << " " << (unsigned)plane;
/// x offset
sstr << printOperand(header, inst, i++, opt);
/// y offset
sstr << printOperand(header, inst, i++, opt);
/// message operand (src or dst)
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_OWORD_ST:
case ISA_OWORD_LD:
case ISA_OWORD_LD_UNALIGNED: {
uint8_t size = getPrimitiveOperand<uint8_t>(inst, i++);
size = size & 0x7;
unsigned num_oword = Get_VISA_Oword_Num((VISA_Oword_Num)size);
if (ISA_OWORD_ST != opcode) {
modifier = getPrimitiveOperand<uint8_t>(inst, i++);
if (modifier & 0x1)
sstr << ".mod";
}
sstr << " (" << num_oword << ")";
surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
/// offset
sstr << printOperand(header, inst, i++, opt);
/// message operand (src or dst)
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_GATHER:
case ISA_SCATTER: {
uint8_t elt_size = 0;
uint8_t num_elts = 0;
elt_size = getPrimitiveOperand<uint8_t>(inst, i++);
elt_size = elt_size & 0x3;
switch ((GATHER_SCATTER_ELEMENT_SIZE)elt_size) {
case GATHER_SCATTER_BYTE:
elt_size = 1;
break;
case GATHER_SCATTER_WORD:
elt_size = 2;
break;
case GATHER_SCATTER_DWORD:
elt_size = 4;
break;
default:
vISA_ASSERT_UNREACHABLE("Incorrect element size for Gather/Scatter CISA inst.");
break;
}
if (ISA_GATHER == opcode) {
modifier = getPrimitiveOperand<uint8_t>(inst, i++);
}
num_elts = getPrimitiveOperand<uint8_t>(inst, i++);
// modifier
if (ISA_GATHER == opcode && modifier & 0x1) {
sstr << ".mod";
}
// num_elts
sstr << "." << (unsigned)elt_size;
// execution size
sstr << " " << printExecutionSizeForScatterGather(num_elts);
// modifier
if (ISA_GATHER == opcode && modifier & 0x1) {
sstr << ".mod";
}
// surface
surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
/// global offset
sstr << printOperand(header, inst, i++, opt);
/// element offset
sstr << printOperand(header, inst, i++, opt);
/// message operand (src or dst)
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_GATHER4_TYPED:
case ISA_SCATTER4_TYPED: {
ChannelMask chMask = ChannelMask::createFromBinary(
opcode, getPrimitiveOperand<uint8_t>(inst, i++));
sstr << "." << chMask.getString();
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
/// u offset
sstr << printOperand(header, inst, i++, opt);
/// v offset
sstr << printOperand(header, inst, i++, opt);
/// r offset
sstr << printOperand(header, inst, i++, opt);
/// lod
sstr << printOperand(header, inst, i++, opt);
/// message operand (src or dst)
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_GATHER4_SCALED:
case ISA_SCATTER4_SCALED: {
ChannelMask chMask = ChannelMask::createFromBinary(
opcode, getPrimitiveOperand<uint8_t>(inst, i++));
sstr << "." << chMask.getString();
// ignore scale which must be 0
(void)getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
/// surface
surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
/// global offset
sstr << printOperand(header, inst, i++, opt);
/// offsets
sstr << printOperand(header, inst, i++, opt);
/// src/dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_GATHER_SCALED:
case ISA_SCATTER_SCALED: {
VISA_SVM_Block_Num numBlocks;
// block size : ignored
(void)getPrimitiveOperand<uint8_t>(inst, i++);
numBlocks = static_cast<VISA_SVM_Block_Num>(
getPrimitiveOperand<uint8_t>(inst, i++));
sstr << "." << Get_Common_ISA_SVM_Block_Num(numBlocks);
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
// scale (MBZ) : ignored
(void)getPrimitiveOperand<uint8_t>(inst, i++);
/// surface
surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
/// global offset
sstr << printOperand(header, inst, i++, opt);
/// offsets
sstr << printOperand(header, inst, i++, opt);
/// src/dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_3D_RT_WRITE: {
// mode
uint16_t mode = getPrimitiveOperand<uint16_t>(inst, i++);
uint8_t surface;
if ((mode) != 0) {
sstr << ".";
if (mode & (0x1 << 2))
sstr << "<RTI>";
if (mode & (0x1 << 3))
sstr << "<A>";
if (mode & (0x1 << 4))
sstr << "<O>";
if (mode & (0x1 << 5))
sstr << "<Z>";
if (mode & (0x1 << 6))
sstr << "<ST>";
if (mode & (0x1 << 7))
sstr << "<LRTW>";
if (mode & (0x1 << 8))
sstr << "<CPS>";
if (mode & (0x1 << 9))
sstr << "<PS>";
if (mode & (0x1 << 10))
sstr << "<CM>";
if (mode & (0x1 << 11))
sstr << "<SI>";
if (mode & (0x1 << 12))
sstr << "<NULLRT>";
}
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
{
// surface
surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
}
while (i < inst->opnd_num) {
sstr << printOperand(header, inst, i++, opt);
}
break;
}
case ISA_DWORD_ATOMIC: {
printAtomicSubOpc(sstr, getPrimitiveOperand<uint8_t>(inst, i++));
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
/// surface
surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
/// offsets
sstr << printOperand(header, inst, i++, opt);
/// src0
sstr << printOperand(header, inst, i++, opt);
/// src1
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_3D_TYPED_ATOMIC: {
printAtomicSubOpc(sstr, getPrimitiveOperand<uint8_t>(inst, i++));
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
/// surface
surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
/// u
sstr << printOperand(header, inst, i++, opt);
/// v
sstr << printOperand(header, inst, i++, opt);
/// r
sstr << printOperand(header, inst, i++, opt);
/// lod
sstr << printOperand(header, inst, i++, opt);
/// src0
sstr << printOperand(header, inst, i++, opt);
/// src1
sstr << printOperand(header, inst, i++, opt);
/// dst
sstr << printOperand(header, inst, i++, opt);
break;
}
case ISA_QW_GATHER:
case ISA_QW_SCATTER: {
// QW_GATHER/SCATTER.<num_blocks> (<exec_size>) <surface> <offset> <dst>
VISA_SVM_Block_Num numBlocks;
numBlocks = static_cast<VISA_SVM_Block_Num>(
getPrimitiveOperand<uint8_t>(inst, i++));
sstr << "." << Get_Common_ISA_SVM_Block_Num(numBlocks);
sstr << " " << printExecutionSize(inst->opcode, inst->execsize);
/// surface
surface = getPrimitiveOperand<uint8_t>(inst, i++);
sstr << " " << printSurfaceName(surface);
/// offsets
sstr << printOperand(header, inst, i++, opt);
/// src/dst
sstr << printOperand(header, inst, i++, opt);
break;
}
default: {
vISA_ASSERT_UNREACHABLE("Unimplemented or Illegal DataPort Opcode.");
}
}
return sstr.str();
}
class LscInstFormatter {
ISA_Opcode opcode;
LSC_OP subOp;
LscOpInfo opInfo;
std::stringstream ss;
const print_format_provider_t *header;
const CISA_INST *inst;
const Options *opts;
int currOpIx = 0;
bool error = false;
public:
LscInstFormatter(ISA_Opcode _opcode, const print_format_provider_t *_header,
const CISA_INST *_inst, const Options *_opts)
: opcode(_opcode), header(_header), inst(_inst), opts(_opts) {
if (_opcode == ISA_LSC_FENCE) {
subOp = LSC_FENCE;
} else {
subOp = getNextEnumU8<LSC_OP>();
}
opInfo = LscOpInfoGet(subOp);
}
private:
template <typename T> T getNextEnumU8() {
return (T)getPrimitive<uint8_t>(currOpIx++);
}
template <typename T> T getNext() { return getPrimitive<T>(currOpIx++); }
template <typename T> T getPrimitive(int absOpIx) {
return getPrimitiveOperand<T>(inst, absOpIx);
}
// LSC_TYPED and non-block2d LSC_UNTYPED
// "next" because it advances the operand pointer
LSC_DATA_SHAPE getNextDataShape() {
auto dataSize = getNextEnumU8<LSC_DATA_SIZE>();
auto dataOrder = getNextEnumU8<LSC_DATA_ORDER>();
auto dataElems = getNextEnumU8<LSC_DATA_ELEMS>();
// chmask only valid on LSC_LOAD_QUAD/LSC_STORE_QUAD
// but retained in the binary format
int chMask = (int)getNextEnumU8<int>();
LSC_DATA_SHAPE dataShape{};
dataShape.size = dataSize;
dataShape.order = dataOrder;
if (opInfo.hasChMask()) {
dataShape.chmask = chMask;
} else {
dataShape.elems = dataElems;
}
return dataShape;
}
void formatBadEnum(int bits) {
error = true;
ss << "<<" << std::hex << std::uppercase << bits << "?>>" << std::dec;
}
void formatSfid(LSC_SFID sfid) {
ss << ".";
switch (sfid) {
case LSC_UGM:
ss << "ugm";
break;
case LSC_UGML:
ss << "ugml";
break;
case LSC_SLM:
ss << "slm";
break;
case LSC_TGM:
ss << "tgm";
break;
default:
formatBadEnum(sfid);
break;
}
}
// custom so we can conditionally print register operand suffixes
void formatVectorOperand(int opIx) {
if (getOperandType(inst, opIx) != CISA_OPND_VECTOR) {
error = true;
ss << "<<BAD_OPERAND_NOT_VECTOR>>";
} else {
const auto &vo = getVectorOperand(inst, opIx);
switch (vo.tag & 0x7) {
case OPERAND_IMMEDIATE:
ss << "0x" << std::uppercase << std::hex
<< vo.opnd_val.const_opnd._val.lval << std::dec;
break;
case OPERAND_GENERAL:
ss << printVariableDeclName(header, vo.getOperandIndex(), opts,
NOT_A_STATE_OPND);
if (vo.opnd_val.gen_opnd.row_offset != 0 ||
vo.opnd_val.gen_opnd.col_offset != 0) {
ss << std::dec << "(" << (unsigned)vo.opnd_val.gen_opnd.row_offset
<< "," << (unsigned)vo.opnd_val.gen_opnd.col_offset << ")";
}
break;
default:
error = true;
ss << "<<BAD_OPERAND_VECTOR_KIND>>";
break;
}
}
}
// Prints the address type operands
// We conditionally print these.
// * flat omits the offset if it's 0x0 (include if reg)
// * anything else includes an operand argument
// * ssIdx is assumed to be after the surface operand 'surfaceOpIx'
//
// flat[...] (flat omits surface offset if it's flat(0x0))
// ^^^^
// bss(VAR)[...]
// bss(VAR,SSOFF)[...]
//
// bti(0x0)[...]
void formatAddrTypeArgs(LSC_ADDR_TYPE addrType, int surfaceOpIx) {
const auto &vo = getVectorOperand(inst, surfaceOpIx);
// SS_IDX for stateful
uint32_t surfaceIndex = getPrimitive<uint32_t>(surfaceOpIx + 1);
if (getOperandType(inst, surfaceOpIx) != CISA_OPND_VECTOR) {
error = true;
ss << "(<<BAD_OPERAND_NOT_VECTOR>>)";
} else {
switch (vo.tag & 0x7) {
case OPERAND_IMMEDIATE: {
bool isZero =
vo.opnd_val.const_opnd._val.lval == 0 && surfaceIndex == 0;
bool isFlatOrArg =
addrType == LSC_ADDR_TYPE_FLAT || addrType == LSC_ADDR_TYPE_ARG;
if (!isZero || !isFlatOrArg) {
ss << "(";
ss << "0x" << std::uppercase << std::hex
<< vo.opnd_val.const_opnd._val.lval << std::dec;
if (surfaceIndex != 0) {
ss << ",0x" << std::uppercase << std::hex << surfaceIndex;
}
ss << ")";
}
break;
}
case OPERAND_GENERAL:
ss << "(";
ss << printVariableDeclName(header, vo.getOperandIndex(), opts,
NOT_A_STATE_OPND);
if (vo.opnd_val.gen_opnd.row_offset != 0 ||
vo.opnd_val.gen_opnd.col_offset != 0) {
ss << std::dec << "(" << (unsigned)vo.opnd_val.gen_opnd.row_offset
<< "," << (unsigned)vo.opnd_val.gen_opnd.col_offset << ")";
if (surfaceIndex != 0) {
ss << ",0x" << std::uppercase << std::hex << surfaceIndex;
}
}
ss << ")";
break;
default:
error = true;
ss << "(<<BAD_OPERAND_VECTOR_KIND>>)";
break;
}
}
}
void formatRawOperand(int absIx) {
if (getOperandType(inst, absIx) != CISA_OPND_RAW) {
error = true;
ss << "<<BAD_OPERAND_NOT_RAW>>";
} else {
const raw_opnd &ro = getRawOperand(inst, absIx);
ss << printVariableDeclName(header, ro.index, opts, NOT_A_STATE_OPND);
if (ro.offset != 0) // only suffix offset if non-zero
ss << "." << std::dec << (int)ro.offset;
}
}
void formatDataOperand(LSC_DATA_SHAPE dataShape, int absIx) {
formatRawOperand(absIx);
formatDataShape(dataShape);
}
void formatAddrType(LSC_ADDR_TYPE addrType, int absSurfOpIx) {
switch (addrType) {
case LSC_ADDR_TYPE_FLAT:
ss << "flat";
break;
case LSC_ADDR_TYPE_BSS:
ss << "bss";
break;
case LSC_ADDR_TYPE_SS:
ss << "ss";
break;
case LSC_ADDR_TYPE_BTI:
ss << "bti";
break;
case LSC_ADDR_TYPE_ARG:
ss << "arg";
break;
default:
formatBadEnum(addrType);
break;
}
formatAddrTypeArgs(addrType, absSurfOpIx);
} // formatAddrType
void formatAddrSize(LSC_ADDR_SIZE addrSize) {
ss << ":";
switch (addrSize) {
case LSC_ADDR_SIZE_16b:
ss << "a16";
break;
case LSC_ADDR_SIZE_32b:
ss << "a32";
break;
case LSC_ADDR_SIZE_64b:
ss << "a64";
break;
default:
formatBadEnum(addrSize);
break;
}
}
void formatDataSize(LSC_DATA_SIZE dataSize) {
ss << ":";
switch (dataSize) {
case LSC_DATA_SIZE_8b:
ss << "d8";
break;
case LSC_DATA_SIZE_16b:
ss << "d16";
break;
case LSC_DATA_SIZE_32b:
ss << "d32";
break;
case LSC_DATA_SIZE_64b:
ss << "d64";
break;
case LSC_DATA_SIZE_8c32b:
ss << "d8u32";
break;
case LSC_DATA_SIZE_16c32b:
ss << "d16u32";
break;
case LSC_DATA_SIZE_16c32bH:
ss << "d16u32h";
break;
default:
formatBadEnum(dataSize);
break;
}
}
void formatChannelMaskSuffix(int chEnMask) {
ss << ".";
auto VALID_MASKS = LSC_DATA_CHMASK_X | LSC_DATA_CHMASK_Y |
LSC_DATA_CHMASK_Z | LSC_DATA_CHMASK_W;
if (chEnMask & ~VALID_MASKS) {
formatBadEnum(chEnMask);
} else {
if (LSC_DATA_CHMASK_X & chEnMask) {
ss << "x";
}
if (LSC_DATA_CHMASK_Y & chEnMask) {
ss << "y";
}
if (LSC_DATA_CHMASK_Z & chEnMask) {
ss << "z";
}
if (LSC_DATA_CHMASK_W & chEnMask) {
ss << "w";
}
}
}
void formatDataElemsSuffix(LSC_DATA_ELEMS dataElems,
LSC_DATA_ORDER dataOrder) {
switch (dataElems) {
case LSC_DATA_ELEMS_1:
break;
case LSC_DATA_ELEMS_2:
ss << "x2";
break;
case LSC_DATA_ELEMS_3:
ss << "x3";
break;
case LSC_DATA_ELEMS_4:
ss << "x4";
break;
case LSC_DATA_ELEMS_8:
ss << "x8";
break;
case LSC_DATA_ELEMS_16:
ss << "x16";
break;
case LSC_DATA_ELEMS_32:
ss << "x32";
break;
case LSC_DATA_ELEMS_64:
ss << "x64";
break;
default:
formatBadEnum(dataElems);
break;
}
formatDataOrder(dataOrder);
}
void formatDataOrder(LSC_DATA_ORDER dataOrder) {
switch (dataOrder) {
case LSC_DATA_ORDER_NONTRANSPOSE:
break;
case LSC_DATA_ORDER_TRANSPOSE:
ss << "t";
break;
default:
formatBadEnum(dataOrder);
break;
}
}
void formatDataShape(LSC_DATA_SHAPE dataShape) {
formatDataSize(dataShape.size);
if (opInfo.hasChMask()) {
formatChannelMaskSuffix(dataShape.chmask);
} else {
formatDataElemsSuffix(dataShape.elems, dataShape.order);
}
}
void formatDataShape2D(LSC_DATA_SHAPE_BLOCK2D dataShape2D) {
formatDataSize(dataShape2D.size);
ss << '.';
if (dataShape2D.blocks != 1) {
ss << std::dec << dataShape2D.blocks << 'x';
}
ss << std::dec << dataShape2D.width << 'x' << dataShape2D.height;
ss << (dataShape2D.order == LSC_DATA_ORDER_TRANSPOSE ? 't' : 'n');
ss << (dataShape2D.vnni ? 't' : 'n');
}
void formatCacheOpt(LSC_CACHE_OPT val) {
switch (val) {
case LSC_CACHING_DEFAULT:
ss << ".df";
break;
case LSC_CACHING_UNCACHED:
ss << ".uc";
break;
case LSC_CACHING_CACHED:
ss << ".ca";
break;
case LSC_CACHING_WRITEBACK:
ss << ".wb";
break;
case LSC_CACHING_WRITETHROUGH:
ss << ".wt";
break;
case LSC_CACHING_STREAMING:
ss << ".st";
break;
case LSC_CACHING_READINVALIDATE:
ss << ".ri";
break;
case LSC_CACHING_CONSTCACHED:
ss << ".cc";
break;
default:
formatBadEnum(val);
break;
}
}
void formatCachingOpts() {
auto l1 = getNextEnumU8<LSC_CACHE_OPT>();
auto l3 = getNextEnumU8<LSC_CACHE_OPT>();
bool cachingDefault = l1 == LSC_CACHE_OPT::LSC_CACHING_DEFAULT &&
l3 == LSC_CACHE_OPT::LSC_CACHING_DEFAULT;
if (!cachingDefault) {
// only format cache control if it's non-default
// NOTE: cache control doesn't have meaning on SLM, but should the
// IR be malformed and accidentally have it, we'll indulge the user
// (for debugging sake)
formatCacheOpt(l1); // L1
formatCacheOpt(l3); // L3
}
}
/////////////////////////////////////////////////////////
// top-level formatters for each instruction type
/////////////////////////////////////////////////////////
void formatFence() {
//
ss << "lsc_fence";
//
auto lscSfid = getNextEnumU8<LSC_SFID>();
formatSfid(lscSfid);
//
auto fenceOp = getNextEnumU8<LSC_FENCE_OP>();
switch (fenceOp) {
case LSC_FENCE_OP_NONE:
ss << ".none";
break;
case LSC_FENCE_OP_EVICT:
ss << ".evict";
break;
case LSC_FENCE_OP_INVALIDATE:
ss << ".invalidate";
break;
case LSC_FENCE_OP_DISCARD:
ss << ".discard";
break;
case LSC_FENCE_OP_CLEAN:
ss << ".clean";
break;
case LSC_FENCE_OP_FLUSHL3:
ss << ".flushl3";
break;
case LSC_FENCE_OP_TYPE6:
ss << ".type6";
break;
default:
ss << ".???";
break;
}
//
auto scope = getNextEnumU8<LSC_SCOPE>();
switch (scope) {
case LSC_SCOPE_GROUP:
ss << ".group";
break;
case LSC_SCOPE_LOCAL:
ss << ".local";
break;
case LSC_SCOPE_TILE:
ss << ".tile";
break;
case LSC_SCOPE_GPU:
ss << ".gpu";
break;
case LSC_SCOPE_GPUS:
ss << ".gpus";
break;
case LSC_SCOPE_SYSREL:
ss << ".sysrel";
break;
case LSC_SCOPE_SYSACQ:
ss << ".sysacq";
break;
default:
ss << ".???";
break;
}
} // formatFence
bool isVectorOpV0(const vector_opnd &vo) const {
return (vo.tag & 0x7) == OPERAND_GENERAL &&
(vo.opnd_val.gen_opnd.index == 0);
}
///////////////////////////////////////////////////////////////////////////
// for all but block2d and append counter atomic
void formatUntypedSimple() {
//
ss << opInfo.mnemonic;
//////////////////
// sfid (e.g. .ugm, .ugml, or .slm)
auto sfid = getNextEnumU8<LSC_SFID>();
formatSfid(sfid);
//
//////////////////
// caching
formatCachingOpts();
// ov
if (hasOV(sfid, opInfo.op)) {
unsigned ov = getNext<unsigned>();
if (ov)
ss << ".ov";
}
// execution size and offset
ss << " " << printExecutionSize(inst->opcode, inst->execsize, subOp);
//
auto addrType = getNextEnumU8<LSC_ADDR_TYPE>();
uint16_t immediateScale = getNext<uint16_t>();
int32_t immediateOffset = getNext<int32_t>();
auto addrSize = getNextEnumU8<LSC_ADDR_SIZE>();
//
auto dataShape = getNextDataShape();
//
ss << " ";
// parameter order (c.f. IsaDescription.cpp)
// (currOpIx is at surface)
// =============================+===========================
// regular | strided
// =============================+===========================
// 0 - surface | surface
// 1 - surface index | surface index
// 2 - dst (data read) | dst (data read)
// 3 - src0 (addr) | src0 (addr-base)
// 4 - src1 (data sent) | src0-stride (addr-stride)
// 5 - src2 (atomic arg) | src1 (store data)
// | (src2 doesn't exist in strided)
// =============================+===========================
int src1AbsIx = opInfo.isStrided() ? currOpIx + 5 : currOpIx + 4;
// see the table below for operand indices
auto fmtAddrOperand = [&]() {
formatAddrType(addrType, currOpIx);
//
ss << "[";
if (immediateScale > 1) {
ss << "0x" << std::hex << immediateScale << "*";
}
const unsigned int src0Ix = currOpIx + 3;
formatRawOperand(src0Ix);
if (immediateOffset != 0) {
if (immediateOffset < 0) {
immediateOffset = -immediateOffset;
ss << "-";
} else {
ss << "+";
}
ss << "0x" << std::hex << immediateOffset;
}
if (opInfo.isStrided()) {
const vector_opnd &vo = getVectorOperand(inst, src0Ix);
if (!isVectorOpV0(vo)) {
// only non-V0 values
ss << ", ";
formatVectorOperand(src0Ix + 1);
}
}
ss << "]";
formatAddrSize(addrSize);
};
if (opInfo.isLoad()) {
formatDataOperand(dataShape, currOpIx + 2); // dst
ss << " ";
fmtAddrOperand(); // src0
} else if (opInfo.isStore()) {
fmtAddrOperand(); // src0
ss << " ";
formatDataOperand(dataShape, src1AbsIx); // src1
} else if (opInfo.isAtomic()) {
formatDataOperand(dataShape, currOpIx + 2); // dst
ss << " ";
fmtAddrOperand(); // src0
ss << " ";
formatRawOperand(src1AbsIx); // src1
ss << " ";
formatRawOperand(src1AbsIx + 1); // src2
} else {
vISA_ASSERT_UNREACHABLE("must be load or store or atomic");
}
} // formatUntypedSimple
///////////////////////////////////////////////////////////////////////////
void formatUntypedBlock2D() {
ss << opInfo.mnemonic;
auto sfid = getNextEnumU8<LSC_SFID>();
formatSfid(sfid);
formatCachingOpts();
// execution size and offset
ss << " " << printExecutionSize(inst->opcode, inst->execsize, subOp);
//
LSC_DATA_SHAPE_BLOCK2D dataShape{};
dataShape.size = getNextEnumU8<LSC_DATA_SIZE>();
dataShape.order = getNextEnumU8<LSC_DATA_ORDER>();
dataShape.blocks = (int)getNext<uint8_t>();
dataShape.width = (int)getNext<uint16_t>();
dataShape.height = (int)getNext<uint16_t>();
dataShape.vnni = getNext<uint8_t>() != 0;
auto formatDataOperand = [&](int absOpIx) {
formatRawOperand(absOpIx);
formatDataShape2D(dataShape);
};
ss << " ";
///////////////////////////////////////////////////////
// The rest of the operands are arranged as follows.
// 0 - SurfaceBase
// 1 - SurfaceWidth
// 2 - SurfaceHeight
// 3 - SurfacePitch
// 4 - SurfaceOffsetX
// 5 - x immediate offset
// 6 - SurfaceOffsetY
// 7 - y immediate offset
// 8 - DataOperand
auto fmtAddrOperand = [&]() {
ss << "flat";
ss << "[";
if (inst->opnd_num - currOpIx < LSC_BLOCK2D_ADDR_PARAMS) {
formatVectorOperand(currOpIx + 1);
ss << " + (";
// imm x offset
ss << (int)getPrimitive<int16_t>(currOpIx + 2);
ss << ",";
// imm y offset
ss << (int)getPrimitive<int16_t>(currOpIx + 3);
ss << ")";
}
else {
formatVectorOperand(currOpIx + 1);
ss << ",";
formatVectorOperand(currOpIx + 2);
ss << ",";
formatVectorOperand(currOpIx + 3);
ss << ",";
formatVectorOperand(currOpIx + 4);
ss << ",";
formatVectorOperand(currOpIx + 5);
if ((int)getPrimitive<int16_t>(currOpIx + 6) != 0) {
if ((int)getPrimitive<int16_t>(currOpIx + 6) > 0) {
ss << "+";
}
ss << (int)getPrimitive<int16_t>(currOpIx + 6);
}
ss << ",";
formatVectorOperand(currOpIx + 7);
if ((int)getPrimitive<int16_t>(currOpIx + 8) != 0) {
if ((int)getPrimitive<int16_t>(currOpIx + 8) > 0) {
ss << "+";
}
ss << (int)getPrimitive<int16_t>(currOpIx + 8);
}
}
ss << "]";
};
if (opInfo.isLoad()) {
formatDataOperand(currOpIx + 0);
ss << " ";
fmtAddrOperand();
} else {
fmtAddrOperand();
ss << " ";
(inst->opnd_num - currOpIx < LSC_BLOCK2D_ADDR_PARAMS) ?
formatDataOperand(currOpIx + 4) : formatDataOperand(currOpIx + 9);
}
} // formatUntypedBlock2D
///////////////////////////////////////////////////////////////////////////
void formatUntyped() {
if (subOp == LSC_LOAD_BLOCK2D || subOp == LSC_STORE_BLOCK2D) {
formatUntypedBlock2D();
} else if (subOp == LSC_APNDCTR_ATOMIC_ADD ||
subOp == LSC_APNDCTR_ATOMIC_SUB ||
subOp == LSC_APNDCTR_ATOMIC_STORE) {
formatUntypedAppendCounterAtomic();
} else {
formatUntypedSimple();
}
} // formatUntyped
///////////////////////////////////////////////////////////////////////////
void formatTyped() {
ss << opInfo.mnemonic;
formatSfid(LSC_TGM);
//////////////////
// caching
formatCachingOpts();
// execution size and offset
ss << " " << printExecutionSize(inst->opcode, inst->execsize, subOp);
auto addrType = getNextEnumU8<LSC_ADDR_TYPE>();
auto addrSize = getNextEnumU8<LSC_ADDR_SIZE>();
auto dataShape = getNextDataShape();
// parameter order (cf IsaDescription.cpp)
// ... currOpIx here:
// 0 - surface
// 1 - surfaceIndex
// 2 - dst (data read)
// 3 - src0 U's (addr)
// 4 - u offset
// 5 - src0 V's (addr)
// 6 - v offset
// 7 - src0 R's (addr)
// 8 - r offset
// 9 - src0 LOD's (addr)
// 10 - src1 (data sent)
// 11 - src2 (extra data sent for atomic)
auto fmtAddrOperand = [&]() {
formatAddrType(addrType, currOpIx);
ss << "[";
for (int i = 0; i < 4; i++) {
unsigned int ix = currOpIx + 3 + (i * 2);
const raw_opnd &ro = getRawOperand(inst, ix);
auto reg =
printVariableDeclName(header, ro.index, opts, NOT_A_STATE_OPND);
if (reg == "%null")
break; // assume no coords after %null (no imm offs either)
if (i > 0)
ss << ",";
formatRawOperand(ix);
if (i < 3) {
int32_t offset = getPrimitive<int32_t>(ix + 1);
if (offset > 0)
ss << "+" << offset;
else if (offset < 0)
ss << "-" << -offset;
}
}
ss << "]";
formatAddrSize(addrSize);
};
ss << " ";
const int dstIx = currOpIx + 2;
const int src1Ix = currOpIx + 10;
const int src2Ix = currOpIx + 11;
if (opInfo.isLoad()) {
formatDataOperand(dataShape, dstIx);
ss << " ";
fmtAddrOperand();
} else if (opInfo.isStore()) {
fmtAddrOperand();
ss << " ";
formatDataOperand(dataShape, src1Ix);
} else if (opInfo.isAtomic()) {
formatDataOperand(dataShape, dstIx); // dst write back
ss << " ";
fmtAddrOperand();
ss << " ";
formatRawOperand(src1Ix); // iadd, etc
ss << " ";
formatRawOperand(src2Ix); // for {i,f}cas
} else {
error = true;
vISA_ASSERT_UNREACHABLE("printInstructionLscTyped unexpected category");
}
} // formatTyped
// e.g. lsc_read_state_info.tgm VDATA bti(0x4)[COORD]
// see formatTyped for the parameter order
void formatTypedRSI() {
ss << opInfo.mnemonic;
formatSfid(LSC_TGM);
// will be default/default (and thus suppressed)
// this consumes the operands
formatCachingOpts();
// exec size is implicit
auto addrType = getNextEnumU8<LSC_ADDR_TYPE>();
(void)getNextEnumU8<LSC_ADDR_SIZE>();
(void)getNextDataShape();
int addrSurfIx = currOpIx + 0;
// see formatTyped() for the parameter order
int dstIx = currOpIx + 2;
int src0Ix = currOpIx + 3;
ss << " ";
formatRawOperand(dstIx); // dst
ss << " ";
formatAddrType(addrType, addrSurfIx); // surface (a0.2)
ss << "[";
formatRawOperand(src0Ix);
ss << "]";
}
void formatDataShapeTyped2D(LSC_DATA_SHAPE_TYPED_BLOCK2D dataShape2D) {
ss << ":";
ss << std::dec << dataShape2D.width << 'x' << dataShape2D.height;
}
void formatTypedBlock2D() {
ss << opInfo.mnemonic;
formatSfid(LSC_TGM);
formatCachingOpts();
auto addrType = getNextEnumU8<LSC_ADDR_TYPE>();
LSC_DATA_SHAPE_TYPED_BLOCK2D dataShape{};
dataShape.width = (int)getNext<uint16_t>();
dataShape.height = (int)getNext<uint16_t>();
///////////////////////////////////////////////////////
// The rest of the operands are arranged as follows.
// 0 - Surface Base
// 1 - Surface Index
// 2 - Dst (data loaded)
// 3 - BlockStartOffsetX
// 4 - BlockImmX
// 5 - BlockStartOffsetY
// 6 - BlockImmY
// 7 - Src1 (data sent)
auto formatDataOperand = [&](int absOpIx) {
formatRawOperand(absOpIx);
formatDataShapeTyped2D(dataShape);
};
auto fmtAddrOperand = [&]() {
formatAddrType(addrType, currOpIx);
ss << "[";
formatVectorOperand(currOpIx + 3);
if ((int)getPrimitive<int16_t>(currOpIx + 4) != 0) {
if ((int)getPrimitive<int16_t>(currOpIx + 4) > 0) {
ss << "+";
}
ss << (int)getPrimitive<int16_t>(currOpIx + 4);
}
ss << ",";
formatVectorOperand(currOpIx + 5);
if ((int)getPrimitive<int16_t>(currOpIx + 6) != 0) {
if ((int)getPrimitive<int16_t>(currOpIx + 6) > 0) {
ss << "+";
}
ss << (int)getPrimitive<int16_t>(currOpIx + 6);
}
ss << "]";
};
ss << " ";
if (opInfo.isLoad()) {
formatDataOperand(currOpIx + 2);
ss << " ";
fmtAddrOperand();
} else {
fmtAddrOperand();
ss << " ";
formatDataOperand(currOpIx + 7);
}
} // formatTypedBlock2D
void formatUntypedAppendCounterAtomic() {
ss << opInfo.mnemonic;
// sfid (e.g. .ugm, .ugml, or .slm)
auto sfid = getNextEnumU8<LSC_SFID>();
formatSfid(sfid);
// caching
formatCachingOpts();
// execution size and offset
ss << " " << printExecutionSize(inst->opcode, inst->execsize, subOp);
// address type
auto addrType = getNextEnumU8<LSC_ADDR_TYPE>();
// data shape
auto dataShape = getNextDataShape();
ss << " ";
formatDataOperand(dataShape, currOpIx + 2); // dst
ss << " ";
formatAddrType(addrType, currOpIx); // surface
ss << " ";
formatDataOperand(dataShape, currOpIx + 4); // src1 holds data; src0 is null
// as append counter atomic does
// not have per lane address
// offsets
} // formatUntypedAppendCounterAtomic
public:
// the only public entry point (except the constructor)
std::string format() {
ss << printPredicate(inst->opcode, inst->pred);
if (opcode == ISA_LSC_FENCE) {
formatFence();
} else if (opcode == ISA_LSC_UNTYPED) {
formatUntyped();
} else if (opcode == ISA_LSC_TYPED) {
if (opInfo.op == LSC_READ_STATE_INFO) {
formatTypedRSI();
} else if (opInfo.op == LSC_LOAD_BLOCK2D ||
opInfo.op == LSC_STORE_BLOCK2D) {
formatTypedBlock2D();
} else {
formatTyped();
}
} else {
vISA_ASSERT_UNREACHABLE("invalid LSC op");
}
return ss.str();
}
};
static std::string printInstructionLsc(ISA_Opcode opcode,
const print_format_provider_t *header,
const CISA_INST *inst,
const Options *opt) {
LscInstFormatter formatter(opcode, header, inst, opt);
return formatter.format();
}
std::string VISAKernel_format_provider::printKernelHeader(bool printVersion) {
std::stringstream sstr;
bool isKernel = m_kernel->getIsKernel();
auto builder = m_kernel->getCISABuilder();
if (printVersion)
sstr << printBuildVersion(getMajorVersion(), getMinorVersion()) << "\n";
sstr << printFunctionDecl(this, isKernel) << "\n";
// Print all functions in the same vISA builder. This is just for
// informational purposes and does not affect codegen.
if (isKernel) {
for (auto Iter = builder->kernel_begin(), IterEnd = builder->kernel_end();
Iter != IterEnd; ++Iter) {
auto KorF = *Iter;
if (!KorF->getIsKernel()) {
sstr << ".funcdecl ";
encodeStringLiteral(sstr, KorF->getName());
sstr << "\n";
}
}
}
auto options = const_cast<VISAKernelImpl *>(m_kernel)->getOptions();
// Print the predefined variables as comments
sstr << "\n"
<< "/// VISA Predefined Variables";
for (unsigned i = 0; i < Get_CISA_PreDefined_Var_Count(); i++) {
const var_info_t *predefVar = getPredefVar(i);
if (predefVar->name_index != -1) {
sstr << "\n"
<< "// .decl V" << i << " v_type=G"
<< " v_name=" << getString(predefVar->name_index);
}
}
for (unsigned i = 0; i < Get_CISA_PreDefined_Surf_Count(); i++) {
const state_info_t *predefSurface = getPredefSurface(i);
if (predefSurface->name_index != -1) {
sstr << "\n"
<< "// .decl T" << i << " v_type=T"
<< " v_name=" << getString(predefSurface->name_index);
}
}
sstr << "\n";
// emit var decls
//.decl V<#> name=<name> type=<type> num_elts=<num_elements> [align=<align>]
//[alias=(<alias_index>,<alias_offset>)]
for (unsigned i = 0; i < getVarCount(); i++) {
sstr << "\n" << printVariableDecl(this, i, options);
}
// address decls
for (unsigned i = 0; i < getAddrCount(); i++) {
sstr << "\n" << printAddressDecl(this, i);
}
// pred decls
for (unsigned i = 0; i < getPredCount(); i++) {
// P0 is reserved; starting from P1 if there is predicate decl
sstr << "\n" << printPredicateDecl(this, i);
}
// sampler
for (unsigned i = 0; i < getSamplerCount(); i++) {
sstr << "\n" << printSamplerDecl(this, i);
}
// surface
unsigned numPreDefinedSurfs = Get_CISA_PreDefined_Surf_Count();
for (unsigned i = 0; i < getSurfaceCount(); i++) {
sstr << "\n" << printSurfaceDecl(this, i, numPreDefinedSurfs);
}
// inputs to kernel
for (unsigned i = 0; i < getInputCount(); i++) {
sstr << "\n" << printFuncInput(this, i, options);
}
bool isTargetSet = false;
for (unsigned i = 0; i < getAttrCount(); i++) {
const char *attrName = getString(getAttr(i)->nameIndex);
if (Attributes::isAttribute(Attributes::ATTR_OutputAsmPath, attrName)) {
// treat this as a transient property and skip it
// this simplifies diffs in shader dump debugging
// if you want to set an explicit name from the command line,
// then use the appropriate option to set this attribute
continue;
}
if (Attributes::isAttribute(Attributes::ATTR_Target, attrName)) {
isTargetSet = true;
}
sstr << "\n.kernel_attr " << printOneAttribute(this, getAttr(i));
}
if (!isTargetSet) {
const char *attrName =
Attributes::getAttributeName(Attributes::ATTR_Target);
sstr << "\n"
<< ".kernel_attr " << attrName << "=";
switch (options->getTarget()) {
case VISA_CM:
sstr << "\"cm\"";
break;
case VISA_3D:
sstr << "\"3d\"";
break;
default:
vISA_ASSERT_UNREACHABLE("Invalid kernel target attribute.");
break;
}
}
return sstr.str();
}
std::string printFunctionDecl(const print_format_provider_t *header,
bool isKernel) {
std::stringstream sstr;
std::string name = header->getString(header->getNameIndex());
std::replace_if(
name.begin(), name.end(), [](char c) { return c == '.'; }, ' ');
sstr << (!isKernel ? "\n.global_function " : "\n.kernel ");
encodeStringLiteral(sstr, name.c_str());
return sstr.str();
}
std::string printBuildVersion(uint16_t major, uint16_t minor) {
std::stringstream sstr;
sstr << ".version " << major << "." << minor;
return sstr.str();
}
std::string
VISAKernel_format_provider::printInstruction(const CISA_INST *instruction,
const Options *opt) const {
std::stringstream sstr;
ISA_Opcode opcode = (ISA_Opcode)instruction->opcode;
if (opcode != ISA_LOC || !g_ignorelocs) {
if (opcode != ISA_LABEL) {
sstr << " ";
}
switch (ISA_Inst_Table[opcode].type) {
case ISA_Inst_Mov:
case ISA_Inst_Sync:
case ISA_Inst_Arith:
case ISA_Inst_Logic:
case ISA_Inst_Compare:
case ISA_Inst_Address:
case ISA_Inst_SIMD_Flow:
sstr << printInstructionCommon(this, instruction, opt);
break;
case ISA_Inst_SVM:
sstr << printInstructionSVM(this, instruction, opt);
break;
case ISA_Inst_Flow:
sstr << printInstructionControlFlow(this, instruction, opt);
break;
case ISA_Inst_Misc:
sstr << printInstructionMisc(this, instruction, opt);
break;
case ISA_Inst_Sampler:
sstr << printInstructionSampler(this, instruction, opt);
break;
case ISA_Inst_Data_Port:
sstr << printInstructionDataport(this, instruction, opt);
break;
case ISA_Inst_LSC:
sstr << printInstructionLsc(opcode, this, instruction, opt);
break;
default: {
sstr << "Illegal or unimplemented CISA instruction (opcode, type): ("
<< opcode << ", " << ISA_Inst_Table[opcode].type << ").";
vISA_ASSERT_UNREACHABLE(sstr.str());
}
}
switch (opcode) {
case ISA_LOC:
case ISA_SUBROUTINE:
case ISA_FILE:
case ISA_LABEL:
break;
default: {
std::stringstream sstr2;
if (g_prettyPrint)
for (int i = 0; i < (int)80 - (int)sstr.str().length(); i++)
sstr2 << ' ';
if (!g_noinstid)
sstr << sstr2.str() << " /// $" << instruction->id;
}
}
} else {
sstr << "";
}
return sstr.str();
}