blob: a45ab85538b2841d61e89ec50b85ecb16408583c [file] [log] [blame]
/*
* Copyright (c) 2018 Lima Project
*
* Copyright (c) 2013 Codethink (http://www.codethink.co.uk)
*
* 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, sub license,
* 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 (including the
* next paragraph) 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 NON-INFRINGEMENT. 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.
*
*/
#include "util/u_half.h"
#include "ppir.h"
#include "codegen.h"
typedef struct {
char *name;
unsigned srcs;
} asm_op;
static void
print_swizzle(uint8_t swizzle)
{
if (swizzle == 0xE4)
return;
printf(".");
for (unsigned i = 0; i < 4; i++, swizzle >>= 2)
printf("%c", "xyzw"[swizzle & 3]);
}
static void
print_mask(uint8_t mask)
{
if (mask == 0xF)
return;
printf(".");
if (mask & 1) printf("x");
if (mask & 2) printf("y");
if (mask & 4) printf("z");
if (mask & 8) printf("w");
}
static void
print_reg(ppir_codegen_vec4_reg reg, const char *special)
{
if (special) {
printf("%s", special);
} else {
switch (reg)
{
case ppir_codegen_vec4_reg_constant0:
printf("^const0");
break;
case ppir_codegen_vec4_reg_constant1:
printf("^const1");
break;
case ppir_codegen_vec4_reg_texture:
printf("^texture");
break;
case ppir_codegen_vec4_reg_uniform:
printf("^uniform");
break;
default:
printf("$%u", reg);
break;
}
}
}
static void
print_vector_source(ppir_codegen_vec4_reg reg, const char *special,
uint8_t swizzle, bool abs, bool neg)
{
if (neg)
printf("-");
if (abs)
printf("abs(");
print_reg(reg, special);
print_swizzle(swizzle);
if (abs)
printf(")");
}
static void
print_source_scalar(unsigned reg, const char *special, bool abs, bool neg)
{
if (neg)
printf("-");
if (abs)
printf("abs(");
print_reg(reg >> 2, special);
if (!special)
printf(".%c", "xyzw"[reg & 3]);
if (abs)
printf(")");
}
static void
print_varying_source(ppir_codegen_field_varying *varying)
{
switch (varying->imm.alignment) {
case 0:
printf("%u.%c", varying->imm.index >> 2,
"xyzw"[varying->imm.index & 3]);
break;
case 1: {
const char *c[2] = {"xy", "zw"};
printf("%u.%s", varying->imm.index >> 1, c[varying->imm.index & 1]);
break;
}
default:
printf("%u", varying->imm.index);
break;
}
if (varying->imm.offset_vector != 15) {
unsigned reg = (varying->imm.offset_vector << 2) +
varying->imm.offset_scalar;
printf("+");
print_source_scalar(reg, NULL, false, false);
}
}
static void
print_outmod(ppir_codegen_outmod modifier)
{
switch (modifier)
{
case ppir_codegen_outmod_clamp_fraction:
printf(".sat");
break;
case ppir_codegen_outmod_clamp_positive:
printf(".pos");
break;
case ppir_codegen_outmod_round:
printf(".int");
break;
default:
break;
}
}
static void
print_dest_scalar(unsigned reg)
{
printf("$%u", reg >> 2);
printf(".%c ", "xyzw"[reg & 3]);
}
static void
print_const(unsigned const_num, uint16_t *val)
{
printf("const%u", const_num);
for (unsigned i = 0; i < 4; i++)
printf(" %f", util_half_to_float(val[i]));
}
static void
print_const0(void *code, unsigned offset)
{
(void) offset;
print_const(0, code);
}
static void
print_const1(void *code, unsigned offset)
{
(void) offset;
print_const(1, code);
}
static void
print_varying(void *code, unsigned offset)
{
(void) offset;
ppir_codegen_field_varying *varying = code;
printf("load");
bool perspective = varying->imm.source_type < 2 && varying->imm.perspective;
if (perspective)
{
printf(".perspective");
switch (varying->imm.perspective)
{
case 2:
printf(".z");
break;
case 3:
printf(".w");
break;
default:
printf(".unknown");
break;
}
}
printf(".v ");
switch (varying->imm.dest)
{
case ppir_codegen_vec4_reg_discard:
printf("^discard");
break;
default:
printf("$%u", varying->imm.dest);
break;
}
print_mask(varying->imm.mask);
printf(" ");
switch (varying->imm.source_type) {
case 1:
print_vector_source(varying->reg.source, NULL, varying->reg.swizzle,
varying->reg.absolute, varying->reg.negate);
break;
case 2:
switch (varying->imm.perspective) {
case 0:
printf("cube(");
print_varying_source(varying);
printf(")");
break;
case 1:
printf("cube(");
print_vector_source(varying->reg.source, NULL, varying->reg.swizzle,
varying->reg.absolute, varying->reg.negate);
printf(")");
break;
case 2:
printf("normalize(");
print_vector_source(varying->reg.source, NULL, varying->reg.swizzle,
varying->reg.absolute, varying->reg.negate);
printf(")");
break;
default:
printf("gl_FragCoord");
break;
}
break;
case 3:
if (varying->imm.perspective)
printf("gl_FrontFacing");
else
printf("gl_PointCoord");
break;
default:
print_varying_source(varying);
break;
}
}
static void
print_sampler(void *code, unsigned offset)
{
(void) offset;
ppir_codegen_field_sampler *sampler = code;
printf("texld");
if (sampler->lod_bias_en)
printf(".b");
switch (sampler->type) {
case ppir_codegen_sampler_type_2d:
printf(".2d");
break;
case ppir_codegen_sampler_type_cube:
printf(".cube");
break;
default:
printf("_t%u", sampler->type);
break;
}
printf(" %u", sampler->index);
if (sampler->offset_en)
{
printf("+");
print_source_scalar(sampler->index_offset, NULL, false, false);
}
if (sampler->lod_bias_en)
{
printf(" ");
print_source_scalar(sampler->lod_bias, NULL, false, false);
}
}
static void
print_uniform(void *code, unsigned offset)
{
(void) offset;
ppir_codegen_field_uniform *uniform = code;
printf("load.");
switch (uniform->source) {
case ppir_codegen_uniform_src_uniform:
printf("u");
break;
case ppir_codegen_uniform_src_temporary:
printf("t");
break;
default:
printf(".u%u", uniform->source);
break;
}
int16_t index = uniform->index;
switch (uniform->alignment) {
case 2:
printf(" %d", index);
break;
case 1:
printf(" %d.%s", index / 2, (index & 1) ? "zw" : "xy");
break;
default:
printf(" %d.%c", index / 4, "xyzw"[index & 3]);
break;
}
if (uniform->offset_en) {
printf("+");
print_source_scalar(uniform->offset_reg, NULL, false, false);
}
}
#define CASE(_name, _srcs) \
[ppir_codegen_vec4_mul_op_##_name] = { \
.name = #_name, \
.srcs = _srcs \
}
static const asm_op vec4_mul_ops[] = {
[0 ... 7] = {
.name = "mul",
.srcs = 2
},
CASE(not, 1),
CASE(and, 2),
CASE(or, 2),
CASE(xor, 2),
CASE(ne, 2),
CASE(gt, 2),
CASE(ge, 2),
CASE(eq, 2),
CASE(min, 2),
CASE(max, 2),
CASE(mov, 1),
};
#undef CASE
static void
print_vec4_mul(void *code, unsigned offset)
{
(void) offset;
ppir_codegen_field_vec4_mul *vec4_mul = code;
asm_op op = vec4_mul_ops[vec4_mul->op];
if (op.name)
printf("%s", op.name);
else
printf("op%u", vec4_mul->op);
print_outmod(vec4_mul->dest_modifier);
printf(".v0 ");
if (vec4_mul->mask) {
printf("$%u", vec4_mul->dest);
print_mask(vec4_mul->mask);
printf(" ");
}
print_vector_source(vec4_mul->arg0_source, NULL,
vec4_mul->arg0_swizzle,
vec4_mul->arg0_absolute,
vec4_mul->arg0_negate);
if (vec4_mul->op < 8 && vec4_mul->op != 0) {
printf("<<%u", vec4_mul->op);
}
printf(" ");
if (op.srcs > 1) {
print_vector_source(vec4_mul->arg1_source, NULL,
vec4_mul->arg1_swizzle,
vec4_mul->arg1_absolute,
vec4_mul->arg1_negate);
}
}
#define CASE(_name, _srcs) \
[ppir_codegen_vec4_acc_op_##_name] = { \
.name = #_name, \
.srcs = _srcs \
}
static const asm_op vec4_acc_ops[] = {
CASE(add, 2),
CASE(fract, 1),
CASE(ne, 2),
CASE(gt, 2),
CASE(ge, 2),
CASE(eq, 2),
CASE(floor, 1),
CASE(ceil, 1),
CASE(min, 2),
CASE(max, 2),
CASE(sum3, 1),
CASE(sum4, 1),
CASE(dFdx, 2),
CASE(dFdy, 2),
CASE(sel, 2),
CASE(mov, 1),
};
#undef CASE
static void
print_vec4_acc(void *code, unsigned offset)
{
(void) offset;
ppir_codegen_field_vec4_acc *vec4_acc = code;
asm_op op = vec4_acc_ops[vec4_acc->op];
if (op.name)
printf("%s", op.name);
else
printf("op%u", vec4_acc->op);
print_outmod(vec4_acc->dest_modifier);
printf(".v1 ");
if (vec4_acc->mask) {
printf("$%u", vec4_acc->dest);
print_mask(vec4_acc->mask);
printf(" ");
}
print_vector_source(vec4_acc->arg0_source, vec4_acc->mul_in ? "^v0" : NULL,
vec4_acc->arg0_swizzle,
vec4_acc->arg0_absolute,
vec4_acc->arg0_negate);
if (op.srcs > 1) {
printf(" ");
print_vector_source(vec4_acc->arg1_source, NULL,
vec4_acc->arg1_swizzle,
vec4_acc->arg1_absolute,
vec4_acc->arg1_negate);
}
}
#define CASE(_name, _srcs) \
[ppir_codegen_float_mul_op_##_name] = { \
.name = #_name, \
.srcs = _srcs \
}
static const asm_op float_mul_ops[] = {
[0 ... 7] = {
.name = "mul",
.srcs = 2
},
CASE(not, 1),
CASE(and, 2),
CASE(or, 2),
CASE(xor, 2),
CASE(ne, 2),
CASE(gt, 2),
CASE(ge, 2),
CASE(eq, 2),
CASE(min, 2),
CASE(max, 2),
CASE(mov, 1),
};
#undef CASE
static void
print_float_mul(void *code, unsigned offset)
{
(void) offset;
ppir_codegen_field_float_mul *float_mul = code;
asm_op op = float_mul_ops[float_mul->op];
if (op.name)
printf("%s", op.name);
else
printf("op%u", float_mul->op);
print_outmod(float_mul->dest_modifier);
printf(".s0 ");
if (float_mul->output_en)
print_dest_scalar(float_mul->dest);
print_source_scalar(float_mul->arg0_source, NULL,
float_mul->arg0_absolute,
float_mul->arg0_negate);
if (float_mul->op < 8 && float_mul->op != 0) {
printf("<<%u", float_mul->op);
}
if (op.srcs > 1) {
printf(" ");
print_source_scalar(float_mul->arg1_source, NULL,
float_mul->arg1_absolute,
float_mul->arg1_negate);
}
}
#define CASE(_name, _srcs) \
[ppir_codegen_float_acc_op_##_name] = { \
.name = #_name, \
.srcs = _srcs \
}
static const asm_op float_acc_ops[] = {
CASE(add, 2),
CASE(fract, 1),
CASE(ne, 2),
CASE(gt, 2),
CASE(ge, 2),
CASE(eq, 2),
CASE(floor, 1),
CASE(ceil, 1),
CASE(min, 2),
CASE(max, 2),
CASE(dFdx, 2),
CASE(dFdy, 2),
CASE(sel, 2),
CASE(mov, 1),
};
#undef CASE
static void
print_float_acc(void *code, unsigned offset)
{
(void) offset;
ppir_codegen_field_float_acc *float_acc = code;
asm_op op = float_acc_ops[float_acc->op];
if (op.name)
printf("%s", op.name);
else
printf("op%u", float_acc->op);
print_outmod(float_acc->dest_modifier);
printf(".s1 ");
if (float_acc->output_en)
print_dest_scalar(float_acc->dest);
print_source_scalar(float_acc->arg0_source, float_acc->mul_in ? "^s0" : NULL,
float_acc->arg0_absolute,
float_acc->arg0_negate);
if (op.srcs > 1) {
printf(" ");
print_source_scalar(float_acc->arg1_source, NULL,
float_acc->arg1_absolute,
float_acc->arg1_negate);
}
}
#define CASE(_name, _srcs) \
[ppir_codegen_combine_scalar_op_##_name] = { \
.name = #_name, \
.srcs = _srcs \
}
static const asm_op combine_ops[] = {
CASE(rcp, 1),
CASE(mov, 1),
CASE(sqrt, 1),
CASE(rsqrt, 1),
CASE(exp2, 1),
CASE(log2, 1),
CASE(sin, 1),
CASE(cos, 1),
CASE(atan, 1),
CASE(atan2, 1),
};
#undef CASE
static void
print_combine(void *code, unsigned offset)
{
(void) offset;
ppir_codegen_field_combine *combine = code;
if (combine->scalar.dest_vec &&
combine->scalar.arg1_en) {
/* This particular combination can only be valid for scalar * vector
* multiplies, and the opcode field is reused for something else.
*/
printf("mul");
} else {
asm_op op = combine_ops[combine->scalar.op];
if (op.name)
printf("%s", op.name);
else
printf("op%u", combine->scalar.op);
}
if (!combine->scalar.dest_vec)
print_outmod(combine->scalar.dest_modifier);
printf(".s2 ");
if (combine->scalar.dest_vec) {
printf("$%u", combine->vector.dest);
print_mask(combine->vector.mask);
} else {
print_dest_scalar(combine->scalar.dest);
}
printf(" ");
print_source_scalar(combine->scalar.arg0_src, NULL,
combine->scalar.arg0_absolute,
combine->scalar.arg0_negate);
printf(" ");
if (combine->scalar.arg1_en) {
if (combine->scalar.dest_vec) {
print_vector_source(combine->vector.arg1_source, NULL,
combine->vector.arg1_swizzle,
false, false);
} else {
print_source_scalar(combine->scalar.arg1_src, NULL,
combine->scalar.arg1_absolute,
combine->scalar.arg1_negate);
}
}
}
static void
print_temp_write(void *code, unsigned offset)
{
(void) offset;
ppir_codegen_field_temp_write *temp_write = code;
if (temp_write->fb_read.unknown_0 == 0x7) {
if (temp_write->fb_read.source)
printf("fb_color");
else
printf("fb_depth");
printf(" $%u", temp_write->fb_read.dest);
return;
}
printf("store.t");
int16_t index = temp_write->temp_write.index;
switch (temp_write->temp_write.alignment) {
case 2:
printf(" %d", index);
break;
case 1:
printf(" %d.%s", index / 2, (index & 1) ? "zw" : "xy");
break;
default:
printf(" %d.%c", index / 4, "xyzw"[index & 3]);
break;
}
if (temp_write->temp_write.offset_en) {
printf("+");
print_source_scalar(temp_write->temp_write.offset_reg,
NULL, false, false);
}
printf(" ");
if (temp_write->temp_write.alignment) {
print_reg(temp_write->temp_write.source >> 2, NULL);
} else {
print_source_scalar(temp_write->temp_write.source, NULL, false, false);
}
}
static void
print_branch(void *code, unsigned offset)
{
ppir_codegen_field_branch *branch = code;
if (branch->discard.word0 == PPIR_CODEGEN_DISCARD_WORD0 &&
branch->discard.word1 == PPIR_CODEGEN_DISCARD_WORD1 &&
branch->discard.word2 == PPIR_CODEGEN_DISCARD_WORD2) {
printf("discard");
return;
}
const char* cond[] = {
"nv", "lt", "eq", "le",
"gt", "ne", "ge", "" ,
};
unsigned cond_mask = 0;
cond_mask |= (branch->branch.cond_lt ? 1 : 0);
cond_mask |= (branch->branch.cond_eq ? 2 : 0);
cond_mask |= (branch->branch.cond_gt ? 4 : 0);
printf("branch");
if (cond_mask != 0x7) {
printf(".%s ", cond[cond_mask]);
print_source_scalar(branch->branch.arg0_source, NULL, false, false);
printf(" ");
print_source_scalar(branch->branch.arg1_source, NULL, false, false);
}
printf(" %d", branch->branch.target + offset);
}
typedef void (*print_field_func)(void *, unsigned);
static const print_field_func print_field[ppir_codegen_field_shift_count] = {
[ppir_codegen_field_shift_varying] = print_varying,
[ppir_codegen_field_shift_sampler] = print_sampler,
[ppir_codegen_field_shift_uniform] = print_uniform,
[ppir_codegen_field_shift_vec4_mul] = print_vec4_mul,
[ppir_codegen_field_shift_float_mul] = print_float_mul,
[ppir_codegen_field_shift_vec4_acc] = print_vec4_acc,
[ppir_codegen_field_shift_float_acc] = print_float_acc,
[ppir_codegen_field_shift_combine] = print_combine,
[ppir_codegen_field_shift_temp_write] = print_temp_write,
[ppir_codegen_field_shift_branch] = print_branch,
[ppir_codegen_field_shift_vec4_const_0] = print_const0,
[ppir_codegen_field_shift_vec4_const_1] = print_const1,
};
static const int ppir_codegen_field_size[] = {
34, 62, 41, 43, 30, 44, 31, 30, 41, 73, 64, 64
};
static void
bitcopy(char *src, char *dst, unsigned bits, unsigned src_offset)
{
src += src_offset / 8;
src_offset %= 8;
for (int b = bits; b > 0; b -= 8, src++, dst++) {
unsigned char out = ((unsigned char) *src) >> src_offset;
if (src_offset > 0 && src_offset + b > 8)
out |= ((unsigned char) *(src + 1)) << (8 - src_offset);
*dst = (char) out;
}
}
void
ppir_disassemble_instr(uint32_t *instr, unsigned offset)
{
ppir_codegen_ctrl *ctrl = (ppir_codegen_ctrl *) instr;
char *instr_code = (char *) (instr + 1);
unsigned bit_offset = 0;
bool first = true;
for (unsigned i = 0; i < ppir_codegen_field_shift_count; i++) {
char code[12];
if (!((ctrl->fields >> i) & 1))
continue;
unsigned bits = ppir_codegen_field_size[i];
bitcopy(instr_code, code, bits, bit_offset);
if (first)
first = false;
else
printf(", ");
print_field[i](code, offset);
bit_offset += bits;
}
if (ctrl->sync)
printf(", sync");
if (ctrl->stop)
printf(", stop");
printf("\n");
}