/* * Copyright © 2020 Google, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (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 NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "util/log.h" #include "ir3/ir3.h" #include "ir3/ir3_shader.h" #include "ir3/instr-a3xx.h" // TODO move opc's and other useful things to ir3-instr.h or so #include "isa.h" struct bitset_params; struct encode_state { struct ir3_compiler *compiler; /** * The instruction which is currently being encoded */ struct ir3_instruction *instr; }; /* * Helpers defining how to map from ir3_instruction/ir3_register/etc to fields * to be encoded: */ static inline bool extract_SRC1_R(struct ir3_instruction *instr) { if (instr->nop) { assert(!instr->repeat); return instr->nop & 0x1; } return !!(instr->regs[1]->flags & IR3_REG_R); } static inline bool extract_SRC2_R(struct ir3_instruction *instr) { if (instr->nop) { assert(!instr->repeat); return (instr->nop >> 1) & 0x1; } /* src2 does not appear in all cat2, but SRC2_R does (for nop encoding) */ if (instr->regs_count > 2) return !!(instr->regs[2]->flags & IR3_REG_R); return 0; } static inline opc_t __instruction_case(struct encode_state *s, struct ir3_instruction *instr) { /* * Temporary hack.. the new world doesn't map opcodes directly to hw * encoding, so there are some cases where we need to fixup the opc * to match what the encoder expects. Eventually this will go away * once we completely transition away from the packed-struct encoding/ * decoding and split up things which are logically different * instructions */ if (instr->opc == OPC_B) { switch (instr->cat0.brtype) { case BRANCH_PLAIN: return OPC_BR; case BRANCH_OR: return OPC_BRAO; case BRANCH_AND: return OPC_BRAA; case BRANCH_CONST: return OPC_BRAC; case BRANCH_ANY: return OPC_BANY; case BRANCH_ALL: return OPC_BALL; case BRANCH_X: return OPC_BRAX; } } else if (instr->opc == OPC_MOV) { struct ir3_register *src = instr->regs[1]; if (src->flags & IR3_REG_IMMED) { return OPC_MOV_IMMED; } if (src->flags & IR3_REG_RELATIV) { if (src->flags & IR3_REG_CONST) { return OPC_MOV_RELCONST; } else { return OPC_MOV_RELGPR; } } else if (src->flags & IR3_REG_CONST) { return OPC_MOV_CONST; } else { return OPC_MOV_GPR; } } else if ((instr->block->shader->compiler->gpu_id > 600) && is_atomic(instr->opc) && (instr->flags & IR3_INSTR_G)) { return instr->opc - OPC_ATOMIC_ADD + OPC_ATOMIC_B_ADD; } else if (s->compiler->gpu_id >= 600) { if (instr->opc == OPC_RESINFO) { return OPC_RESINFO_B; } else if (instr->opc == OPC_LDIB) { return OPC_LDIB_B; } else if (instr->opc == OPC_STIB) { return OPC_STIB_B; } } return instr->opc; } static inline unsigned extract_ABSNEG(struct ir3_register *reg) { // TODO generate enums for this: if (reg->flags & (IR3_REG_FNEG | IR3_REG_SNEG | IR3_REG_BNOT)) { if (reg->flags & (IR3_REG_FABS | IR3_REG_SABS)) { return 3; // ABSNEG } else { return 1; // NEG } } else if (reg->flags & (IR3_REG_FABS | IR3_REG_SABS)) { return 2; // ABS } else { return 0; } } /** * This is a bit messy, to deal with the fact that the optional "s2en" * src is the first src, shifting everything else up by one. * * TODO revisit this once legacy 'packed struct' encoding is gone */ static inline struct ir3_register * extract_cat5_SRC(struct ir3_instruction *instr, unsigned n) { if (instr->flags & IR3_INSTR_S2EN) { n++; } if (n < instr->regs_count) return instr->regs[n]; return NULL; } static inline bool extract_cat5_FULL(struct ir3_instruction *instr) { struct ir3_register *reg = extract_cat5_SRC(instr, 1); /* some cat5 have zero src regs, in which case 'FULL' is false */ if (!reg) return false; return !(reg->flags & IR3_REG_HALF); } static inline cat5_desc_mode_t extract_cat5_DESC_MODE(struct ir3_instruction *instr) { assert(instr->flags & (IR3_INSTR_S2EN | IR3_INSTR_B)); if (instr->flags & IR3_INSTR_S2EN) { if (instr->flags & IR3_INSTR_B) { if (instr->flags & IR3_INSTR_A1EN) { return CAT5_BINDLESS_A1_UNIFORM; } else if (instr->flags & IR3_INSTR_NONUNIF) { return CAT5_BINDLESS_NONUNIFORM; } else { return CAT5_BINDLESS_UNIFORM; } } else { /* TODO: This should probably be CAT5_UNIFORM, at least on a6xx, * as this is what the blob does and it is presumably faster, but * first we should confirm it is actually nonuniform and figure * out when the whole descriptor mode mechanism was introduced. */ return CAT5_NONUNIFORM; } assert(!(instr->cat5.samp | instr->cat5.tex)); } else if (instr->flags & IR3_INSTR_B) { if (instr->flags & IR3_INSTR_A1EN) { return CAT5_BINDLESS_A1_IMM; } else { return CAT5_BINDLESS_IMM; } } return 0; } static inline unsigned extract_cat6_DESC_MODE(struct ir3_instruction *instr) { struct ir3_register *ssbo = instr->regs[1]; if (ssbo->flags & IR3_REG_IMMED) { return 0; // todo enum } else if (instr->flags & IR3_INSTR_NONUNIF) { return 2; // todo enum } else { return 1; // todo enum } } /** * This is a bit messy, for legacy (pre-bindless) atomic instructions, * the .g (global) variety have SSBO as first src and everything else * shifted up by one. * * TODO revisit this once legacy 'packed struct' encoding is gone */ static inline struct ir3_register * extract_cat6_SRC(struct ir3_instruction *instr, unsigned n) { if (instr->flags & IR3_INSTR_G) { n++; } assert(n < instr->regs_count); return instr->regs[n]; } typedef enum { REG_MULITSRC_IMMED, REG_MULTISRC_IMMED_FLUT_FULL, REG_MULTISRC_IMMED_FLUT_HALF, REG_MULTISRC_GPR, REG_MULTISRC_CONST, REG_MULTISRC_RELATIVE_GPR, REG_MULTISRC_RELATIVE_CONST, } reg_multisrc_t; static inline reg_multisrc_t __multisrc_case(struct encode_state *s, struct ir3_register *reg) { if (reg->flags & IR3_REG_IMMED) { assert(opc_cat(s->instr->opc) == 2); if (ir3_cat2_int(s->instr->opc)) { return REG_MULITSRC_IMMED; } else if (reg->flags & IR3_REG_HALF) { return REG_MULTISRC_IMMED_FLUT_HALF; } else { return REG_MULTISRC_IMMED_FLUT_FULL; } } else if (reg->flags & IR3_REG_RELATIV) { if (reg->flags & IR3_REG_CONST) { return REG_MULTISRC_RELATIVE_CONST; } else { return REG_MULTISRC_RELATIVE_GPR; } } else if (reg->flags & IR3_REG_CONST) { return REG_MULTISRC_CONST; } else { return REG_MULTISRC_GPR; } } typedef enum { REG_CAT3_SRC_GPR, REG_CAT3_SRC_CONST, REG_CAT3_SRC_RELATIVE_GPR, REG_CAT3_SRC_RELATIVE_CONST, } reg_cat3_src_t; static inline reg_cat3_src_t __cat3_src_case(struct encode_state *s, struct ir3_register *reg) { if (reg->flags & IR3_REG_RELATIV) { if (reg->flags & IR3_REG_CONST) { return REG_CAT3_SRC_RELATIVE_CONST; } else { return REG_CAT3_SRC_RELATIVE_GPR; } } else if (reg->flags & IR3_REG_CONST) { return REG_CAT3_SRC_CONST; } else { return REG_CAT3_SRC_GPR; } } #include "encode.h" void * isa_assemble(struct ir3_shader_variant *v) { uint64_t *ptr, *instrs; const struct ir3_info *info = &v->info; struct ir3 *shader = v->ir; ptr = instrs = rzalloc_size(v, info->size); foreach_block (block, &shader->block_list) { foreach_instr (instr, &block->instr_list) { struct encode_state s = { .compiler = shader->compiler, .instr = instr, }; *(instrs++) = encode__instruction(&s, NULL, instr); } } return ptr; }