| /* |
| * Copyright © 2018 Valve Corporation |
| * Copyright © 2018 Google |
| * |
| * 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 "aco_instruction_selection.h" |
| |
| #include "aco_builder.h" |
| #include "aco_ir.h" |
| #include "aco_interface.h" |
| |
| #include "common/ac_nir.h" |
| #include "common/sid.h" |
| |
| #include "util/fast_idiv_by_const.h" |
| #include "util/memstream.h" |
| |
| #include <array> |
| #include <functional> |
| #include <map> |
| #include <numeric> |
| #include <stack> |
| #include <utility> |
| #include <vector> |
| |
| namespace aco { |
| namespace { |
| |
| #define isel_err(...) _isel_err(ctx, __FILE__, __LINE__, __VA_ARGS__) |
| |
| static void |
| _isel_err(isel_context* ctx, const char* file, unsigned line, const nir_instr* instr, |
| const char* msg) |
| { |
| char* out; |
| size_t outsize; |
| struct u_memstream mem; |
| u_memstream_open(&mem, &out, &outsize); |
| FILE* const memf = u_memstream_get(&mem); |
| |
| fprintf(memf, "%s: ", msg); |
| nir_print_instr(instr, memf); |
| u_memstream_close(&mem); |
| |
| _aco_err(ctx->program, file, line, out); |
| free(out); |
| } |
| |
| struct if_context { |
| Temp cond; |
| |
| bool divergent_old; |
| bool exec_potentially_empty_discard_old; |
| bool exec_potentially_empty_break_old; |
| bool had_divergent_discard_old; |
| bool had_divergent_discard_then; |
| uint16_t exec_potentially_empty_break_depth_old; |
| |
| unsigned BB_if_idx; |
| unsigned invert_idx; |
| bool uniform_has_then_branch; |
| bool then_branch_divergent; |
| Block BB_invert; |
| Block BB_endif; |
| }; |
| |
| struct loop_context { |
| Block loop_exit; |
| |
| unsigned header_idx_old; |
| Block* exit_old; |
| bool divergent_cont_old; |
| bool divergent_branch_old; |
| bool divergent_if_old; |
| }; |
| |
| static bool visit_cf_list(struct isel_context* ctx, struct exec_list* list); |
| |
| static void |
| add_logical_edge(unsigned pred_idx, Block* succ) |
| { |
| succ->logical_preds.emplace_back(pred_idx); |
| } |
| |
| static void |
| add_linear_edge(unsigned pred_idx, Block* succ) |
| { |
| succ->linear_preds.emplace_back(pred_idx); |
| } |
| |
| static void |
| add_edge(unsigned pred_idx, Block* succ) |
| { |
| add_logical_edge(pred_idx, succ); |
| add_linear_edge(pred_idx, succ); |
| } |
| |
| static void |
| append_logical_start(Block* b) |
| { |
| Builder(NULL, b).pseudo(aco_opcode::p_logical_start); |
| } |
| |
| static void |
| append_logical_end(Block* b) |
| { |
| Builder(NULL, b).pseudo(aco_opcode::p_logical_end); |
| } |
| |
| Temp |
| get_ssa_temp(struct isel_context* ctx, nir_ssa_def* def) |
| { |
| uint32_t id = ctx->first_temp_id + def->index; |
| return Temp(id, ctx->program->temp_rc[id]); |
| } |
| |
| Temp |
| emit_mbcnt(isel_context* ctx, Temp dst, Operand mask = Operand(), Operand base = Operand::zero()) |
| { |
| Builder bld(ctx->program, ctx->block); |
| assert(mask.isUndefined() || mask.isTemp() || (mask.isFixed() && mask.physReg() == exec)); |
| assert(mask.isUndefined() || mask.bytes() == bld.lm.bytes()); |
| |
| if (ctx->program->wave_size == 32) { |
| Operand mask_lo = mask.isUndefined() ? Operand::c32(-1u) : mask; |
| return bld.vop3(aco_opcode::v_mbcnt_lo_u32_b32, Definition(dst), mask_lo, base); |
| } |
| |
| Operand mask_lo = Operand::c32(-1u); |
| Operand mask_hi = Operand::c32(-1u); |
| |
| if (mask.isTemp()) { |
| RegClass rc = RegClass(mask.regClass().type(), 1); |
| Builder::Result mask_split = |
| bld.pseudo(aco_opcode::p_split_vector, bld.def(rc), bld.def(rc), mask); |
| mask_lo = Operand(mask_split.def(0).getTemp()); |
| mask_hi = Operand(mask_split.def(1).getTemp()); |
| } else if (mask.physReg() == exec) { |
| mask_lo = Operand(exec_lo, s1); |
| mask_hi = Operand(exec_hi, s1); |
| } |
| |
| Temp mbcnt_lo = bld.vop3(aco_opcode::v_mbcnt_lo_u32_b32, bld.def(v1), mask_lo, base); |
| |
| if (ctx->program->gfx_level <= GFX7) |
| return bld.vop2(aco_opcode::v_mbcnt_hi_u32_b32, Definition(dst), mask_hi, mbcnt_lo); |
| else |
| return bld.vop3(aco_opcode::v_mbcnt_hi_u32_b32_e64, Definition(dst), mask_hi, mbcnt_lo); |
| } |
| |
| Temp |
| emit_wqm(Builder& bld, Temp src, Temp dst = Temp(0, s1), bool program_needs_wqm = false) |
| { |
| if (bld.program->stage != fragment_fs) { |
| if (!dst.id()) |
| return src; |
| else |
| return bld.copy(Definition(dst), src); |
| } else if (!dst.id()) { |
| dst = bld.tmp(src.regClass()); |
| } |
| |
| assert(src.size() == dst.size()); |
| bld.pseudo(aco_opcode::p_wqm, Definition(dst), src); |
| bld.program->needs_wqm |= program_needs_wqm; |
| return dst; |
| } |
| |
| static Temp |
| emit_bpermute(isel_context* ctx, Builder& bld, Temp index, Temp data) |
| { |
| if (index.regClass() == s1) |
| return bld.readlane(bld.def(s1), data, index); |
| |
| if (ctx->options->gfx_level <= GFX7) { |
| /* GFX6-7: there is no bpermute instruction */ |
| Operand index_op(index); |
| Operand input_data(data); |
| index_op.setLateKill(true); |
| input_data.setLateKill(true); |
| |
| return bld.pseudo(aco_opcode::p_bpermute, bld.def(v1), bld.def(bld.lm), bld.def(bld.lm, vcc), |
| index_op, input_data); |
| } else if (ctx->options->gfx_level >= GFX10 && ctx->program->wave_size == 64) { |
| |
| /* GFX10 wave64 mode: emulate full-wave bpermute */ |
| Temp index_is_lo = |
| bld.vopc(aco_opcode::v_cmp_ge_u32, bld.def(bld.lm), Operand::c32(31u), index); |
| Builder::Result index_is_lo_split = |
| bld.pseudo(aco_opcode::p_split_vector, bld.def(s1), bld.def(s1), index_is_lo); |
| Temp index_is_lo_n1 = bld.sop1(aco_opcode::s_not_b32, bld.def(s1), bld.def(s1, scc), |
| index_is_lo_split.def(1).getTemp()); |
| Operand same_half = bld.pseudo(aco_opcode::p_create_vector, bld.def(s2), |
| index_is_lo_split.def(0).getTemp(), index_is_lo_n1); |
| Operand index_x4 = bld.vop2(aco_opcode::v_lshlrev_b32, bld.def(v1), Operand::c32(2u), index); |
| Operand input_data(data); |
| |
| index_x4.setLateKill(true); |
| input_data.setLateKill(true); |
| same_half.setLateKill(true); |
| |
| /* We need one pair of shared VGPRs: |
| * Note, that these have twice the allocation granularity of normal VGPRs */ |
| ctx->program->config->num_shared_vgprs = 2 * ctx->program->dev.vgpr_alloc_granule; |
| |
| return bld.pseudo(aco_opcode::p_bpermute, bld.def(v1), bld.def(s2), bld.def(s1, scc), |
| index_x4, input_data, same_half); |
| } else { |
| /* GFX8-9 or GFX10 wave32: bpermute works normally */ |
| Temp index_x4 = bld.vop2(aco_opcode::v_lshlrev_b32, bld.def(v1), Operand::c32(2u), index); |
| return bld.ds(aco_opcode::ds_bpermute_b32, bld.def(v1), index_x4, data); |
| } |
| } |
| |
| static Temp |
| emit_masked_swizzle(isel_context* ctx, Builder& bld, Temp src, unsigned mask) |
| { |
| if (ctx->options->gfx_level >= GFX8) { |
| unsigned and_mask = mask & 0x1f; |
| unsigned or_mask = (mask >> 5) & 0x1f; |
| unsigned xor_mask = (mask >> 10) & 0x1f; |
| |
| uint16_t dpp_ctrl = 0xffff; |
| |
| if (and_mask == 0x1f && or_mask < 4 && xor_mask < 4) { |
| unsigned res[4] = {0, 1, 2, 3}; |
| for (unsigned i = 0; i < 4; i++) |
| res[i] = ((res[i] | or_mask) ^ xor_mask) & 0x3; |
| dpp_ctrl = dpp_quad_perm(res[0], res[1], res[2], res[3]); |
| } else if (and_mask == 0x1f && !or_mask && xor_mask == 8) { |
| dpp_ctrl = dpp_row_rr(8); |
| } else if (and_mask == 0x1f && !or_mask && xor_mask == 0xf) { |
| dpp_ctrl = dpp_row_mirror; |
| } else if (and_mask == 0x1f && !or_mask && xor_mask == 0x7) { |
| dpp_ctrl = dpp_row_half_mirror; |
| } else if (ctx->options->gfx_level >= GFX10 && (and_mask & 0x18) == 0x18 && or_mask < 8 && |
| xor_mask < 8) { |
| // DPP8 comes last, as it does not allow several modifiers like `abs` that are available with DPP16 |
| Builder::Result ret = bld.vop1_dpp8(aco_opcode::v_mov_b32, bld.def(v1), src); |
| for (unsigned i = 0; i < 8; i++) { |
| ret.instr->dpp8().lane_sel[i] = (((i & and_mask) | or_mask) ^ xor_mask) & 0x7; |
| } |
| return ret; |
| } |
| |
| if (dpp_ctrl != 0xffff) |
| return bld.vop1_dpp(aco_opcode::v_mov_b32, bld.def(v1), src, dpp_ctrl); |
| } |
| |
| return bld.ds(aco_opcode::ds_swizzle_b32, bld.def(v1), src, mask, 0, false); |
| } |
| |
| Temp |
| as_vgpr(Builder& bld, Temp val) |
| { |
| if (val.type() == RegType::sgpr) |
| return bld.copy(bld.def(RegType::vgpr, val.size()), val); |
| assert(val.type() == RegType::vgpr); |
| return val; |
| } |
| |
| Temp |
| as_vgpr(isel_context* ctx, Temp val) |
| { |
| Builder bld(ctx->program, ctx->block); |
| return as_vgpr(bld, val); |
| } |
| |
| // assumes a != 0xffffffff |
| void |
| emit_v_div_u32(isel_context* ctx, Temp dst, Temp a, uint32_t b) |
| { |
| assert(b != 0); |
| Builder bld(ctx->program, ctx->block); |
| |
| if (util_is_power_of_two_or_zero(b)) { |
| bld.vop2(aco_opcode::v_lshrrev_b32, Definition(dst), Operand::c32(util_logbase2(b)), a); |
| return; |
| } |
| |
| util_fast_udiv_info info = util_compute_fast_udiv_info(b, 32, 32); |
| |
| assert(info.multiplier <= 0xffffffff); |
| |
| bool pre_shift = info.pre_shift != 0; |
| bool increment = info.increment != 0; |
| bool multiply = true; |
| bool post_shift = info.post_shift != 0; |
| |
| if (!pre_shift && !increment && !multiply && !post_shift) { |
| bld.copy(Definition(dst), a); |
| return; |
| } |
| |
| Temp pre_shift_dst = a; |
| if (pre_shift) { |
| pre_shift_dst = (increment || multiply || post_shift) ? bld.tmp(v1) : dst; |
| bld.vop2(aco_opcode::v_lshrrev_b32, Definition(pre_shift_dst), Operand::c32(info.pre_shift), |
| a); |
| } |
| |
| Temp increment_dst = pre_shift_dst; |
| if (increment) { |
| increment_dst = (post_shift || multiply) ? bld.tmp(v1) : dst; |
| bld.vadd32(Definition(increment_dst), Operand::c32(info.increment), pre_shift_dst); |
| } |
| |
| Temp multiply_dst = increment_dst; |
| if (multiply) { |
| multiply_dst = post_shift ? bld.tmp(v1) : dst; |
| bld.vop3(aco_opcode::v_mul_hi_u32, Definition(multiply_dst), increment_dst, |
| bld.copy(bld.def(v1), Operand::c32(info.multiplier))); |
| } |
| |
| if (post_shift) { |
| bld.vop2(aco_opcode::v_lshrrev_b32, Definition(dst), Operand::c32(info.post_shift), |
| multiply_dst); |
| } |
| } |
| |
| void |
| emit_extract_vector(isel_context* ctx, Temp src, uint32_t idx, Temp dst) |
| { |
| Builder bld(ctx->program, ctx->block); |
| bld.pseudo(aco_opcode::p_extract_vector, Definition(dst), src, Operand::c32(idx)); |
| } |
| |
| Temp |
| emit_extract_vector(isel_context* ctx, Temp src, uint32_t idx, RegClass dst_rc) |
| { |
| /* no need to extract the whole vector */ |
| if (src.regClass() == dst_rc) { |
| assert(idx == 0); |
| return src; |
| } |
| |
| assert(src.bytes() > (idx * dst_rc.bytes())); |
| Builder bld(ctx->program, ctx->block); |
| auto it = ctx->allocated_vec.find(src.id()); |
| if (it != ctx->allocated_vec.end() && dst_rc.bytes() == it->second[idx].regClass().bytes()) { |
| if (it->second[idx].regClass() == dst_rc) { |
| return it->second[idx]; |
| } else { |
| assert(!dst_rc.is_subdword()); |
| assert(dst_rc.type() == RegType::vgpr && it->second[idx].type() == RegType::sgpr); |
| return bld.copy(bld.def(dst_rc), it->second[idx]); |
| } |
| } |
| |
| if (dst_rc.is_subdword()) |
| src = as_vgpr(ctx, src); |
| |
| if (src.bytes() == dst_rc.bytes()) { |
| assert(idx == 0); |
| return bld.copy(bld.def(dst_rc), src); |
| } else { |
| Temp dst = bld.tmp(dst_rc); |
| emit_extract_vector(ctx, src, idx, dst); |
| return dst; |
| } |
| } |
| |
| void |
| emit_split_vector(isel_context* ctx, Temp vec_src, unsigned num_components) |
| { |
| if (num_components == 1) |
| return; |
| if (ctx->allocated_vec.find(vec_src.id()) != ctx->allocated_vec.end()) |
| return; |
| RegClass rc; |
| if (num_components > vec_src.size()) { |
| if (vec_src.type() == RegType::sgpr) { |
| /* should still help get_alu_src() */ |
| emit_split_vector(ctx, vec_src, vec_src.size()); |
| return; |
| } |
| /* sub-dword split */ |
| rc = RegClass(RegType::vgpr, vec_src.bytes() / num_components).as_subdword(); |
| } else { |
| rc = RegClass(vec_src.type(), vec_src.size() / num_components); |
| } |
| aco_ptr<Pseudo_instruction> split{create_instruction<Pseudo_instruction>( |
| aco_opcode::p_split_vector, Format::PSEUDO, 1, num_components)}; |
| split->operands[0] = Operand(vec_src); |
| std::array<Temp, NIR_MAX_VEC_COMPONENTS> elems; |
| for (unsigned i = 0; i < num_components; i++) { |
| elems[i] = ctx->program->allocateTmp(rc); |
| split->definitions[i] = Definition(elems[i]); |
| } |
| ctx->block->instructions.emplace_back(std::move(split)); |
| ctx->allocated_vec.emplace(vec_src.id(), elems); |
| } |
| |
| /* This vector expansion uses a mask to determine which elements in the new vector |
| * come from the original vector. The other elements are undefined. */ |
| void |
| expand_vector(isel_context* ctx, Temp vec_src, Temp dst, unsigned num_components, unsigned mask, |
| bool zero_padding = false) |
| { |
| assert(vec_src.type() == RegType::vgpr); |
| Builder bld(ctx->program, ctx->block); |
| |
| if (dst.type() == RegType::sgpr && num_components > dst.size()) { |
| Temp tmp_dst = bld.tmp(RegClass::get(RegType::vgpr, 2 * num_components)); |
| expand_vector(ctx, vec_src, tmp_dst, num_components, mask, zero_padding); |
| bld.pseudo(aco_opcode::p_as_uniform, Definition(dst), tmp_dst); |
| ctx->allocated_vec[dst.id()] = ctx->allocated_vec[tmp_dst.id()]; |
| return; |
| } |
| |
| emit_split_vector(ctx, vec_src, util_bitcount(mask)); |
| |
| if (vec_src == dst) |
| return; |
| |
| if (num_components == 1) { |
| if (dst.type() == RegType::sgpr) |
| bld.pseudo(aco_opcode::p_as_uniform, Definition(dst), vec_src); |
| else |
| bld.copy(Definition(dst), vec_src); |
| return; |
| } |
| |
| unsigned component_bytes = dst.bytes() / num_components; |
| RegClass src_rc = RegClass::get(RegType::vgpr, component_bytes); |
| RegClass dst_rc = RegClass::get(dst.type(), component_bytes); |
| assert(dst.type() == RegType::vgpr || !src_rc.is_subdword()); |
| std::array<Temp, NIR_MAX_VEC_COMPONENTS> elems; |
| |
| Temp padding = Temp(0, dst_rc); |
| if (zero_padding) |
| padding = bld.copy(bld.def(dst_rc), Operand::zero(component_bytes)); |
| |
| aco_ptr<Pseudo_instruction> vec{create_instruction<Pseudo_instruction>( |
| aco_opcode::p_create_vector, Format::PSEUDO, num_components, 1)}; |
| vec->definitions[0] = Definition(dst); |
| unsigned k = 0; |
| for (unsigned i = 0; i < num_components; i++) { |
| if (mask & (1 << i)) { |
| Temp src = emit_extract_vector(ctx, vec_src, k++, src_rc); |
| if (dst.type() == RegType::sgpr) |
| src = bld.as_uniform(src); |
| vec->operands[i] = Operand(src); |
| elems[i] = src; |
| } else { |
| vec->operands[i] = Operand::zero(component_bytes); |
| elems[i] = padding; |
| } |
| } |
| ctx->block->instructions.emplace_back(std::move(vec)); |
| ctx->allocated_vec.emplace(dst.id(), elems); |
| } |
| |
| /* adjust misaligned small bit size loads */ |
| void |
| byte_align_scalar(isel_context* ctx, Temp vec, Operand offset, Temp dst) |
| { |
| Builder bld(ctx->program, ctx->block); |
| Operand shift; |
| Temp select = Temp(); |
| if (offset.isConstant()) { |
| assert(offset.constantValue() && offset.constantValue() < 4); |
| shift = Operand::c32(offset.constantValue() * 8); |
| } else { |
| /* bit_offset = 8 * (offset & 0x3) */ |
| Temp tmp = |
| bld.sop2(aco_opcode::s_and_b32, bld.def(s1), bld.def(s1, scc), offset, Operand::c32(3u)); |
| select = bld.tmp(s1); |
| shift = bld.sop2(aco_opcode::s_lshl_b32, bld.def(s1), bld.scc(Definition(select)), tmp, |
| Operand::c32(3u)); |
| } |
| |
| if (vec.size() == 1) { |
| bld.sop2(aco_opcode::s_lshr_b32, Definition(dst), bld.def(s1, scc), vec, shift); |
| } else if (vec.size() == 2) { |
| Temp tmp = dst.size() == 2 ? dst : bld.tmp(s2); |
| bld.sop2(aco_opcode::s_lshr_b64, Definition(tmp), bld.def(s1, scc), vec, shift); |
| if (tmp == dst) |
| emit_split_vector(ctx, dst, 2); |
| else |
| emit_extract_vector(ctx, tmp, 0, dst); |
| } else if (vec.size() == 3 || vec.size() == 4) { |
| Temp lo = bld.tmp(s2), hi; |
| if (vec.size() == 3) { |
| /* this can happen if we use VMEM for a uniform load */ |
| hi = bld.tmp(s1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(lo), Definition(hi), vec); |
| } else { |
| hi = bld.tmp(s2); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(lo), Definition(hi), vec); |
| hi = bld.pseudo(aco_opcode::p_extract_vector, bld.def(s1), hi, Operand::zero()); |
| } |
| if (select != Temp()) |
| hi = |
| bld.sop2(aco_opcode::s_cselect_b32, bld.def(s1), hi, Operand::zero(), bld.scc(select)); |
| lo = bld.sop2(aco_opcode::s_lshr_b64, bld.def(s2), bld.def(s1, scc), lo, shift); |
| Temp mid = bld.tmp(s1); |
| lo = bld.pseudo(aco_opcode::p_split_vector, bld.def(s1), Definition(mid), lo); |
| hi = bld.sop2(aco_opcode::s_lshl_b32, bld.def(s1), bld.def(s1, scc), hi, shift); |
| mid = bld.sop2(aco_opcode::s_or_b32, bld.def(s1), bld.def(s1, scc), hi, mid); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), lo, mid); |
| emit_split_vector(ctx, dst, 2); |
| } |
| } |
| |
| void |
| byte_align_vector(isel_context* ctx, Temp vec, Operand offset, Temp dst, unsigned component_size) |
| { |
| Builder bld(ctx->program, ctx->block); |
| if (offset.isTemp()) { |
| Temp tmp[4] = {vec, vec, vec, vec}; |
| |
| if (vec.size() == 4) { |
| tmp[0] = bld.tmp(v1), tmp[1] = bld.tmp(v1), tmp[2] = bld.tmp(v1), tmp[3] = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(tmp[0]), Definition(tmp[1]), |
| Definition(tmp[2]), Definition(tmp[3]), vec); |
| } else if (vec.size() == 3) { |
| tmp[0] = bld.tmp(v1), tmp[1] = bld.tmp(v1), tmp[2] = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(tmp[0]), Definition(tmp[1]), |
| Definition(tmp[2]), vec); |
| } else if (vec.size() == 2) { |
| tmp[0] = bld.tmp(v1), tmp[1] = bld.tmp(v1), tmp[2] = tmp[1]; |
| bld.pseudo(aco_opcode::p_split_vector, Definition(tmp[0]), Definition(tmp[1]), vec); |
| } |
| for (unsigned i = 0; i < dst.size(); i++) |
| tmp[i] = bld.vop3(aco_opcode::v_alignbyte_b32, bld.def(v1), tmp[i + 1], tmp[i], offset); |
| |
| vec = tmp[0]; |
| if (dst.size() == 2) |
| vec = bld.pseudo(aco_opcode::p_create_vector, bld.def(v2), tmp[0], tmp[1]); |
| |
| offset = Operand::zero(); |
| } |
| |
| unsigned num_components = vec.bytes() / component_size; |
| if (vec.regClass() == dst.regClass()) { |
| assert(offset.constantValue() == 0); |
| bld.copy(Definition(dst), vec); |
| emit_split_vector(ctx, dst, num_components); |
| return; |
| } |
| |
| emit_split_vector(ctx, vec, num_components); |
| std::array<Temp, NIR_MAX_VEC_COMPONENTS> elems; |
| RegClass rc = RegClass(RegType::vgpr, component_size).as_subdword(); |
| |
| assert(offset.constantValue() % component_size == 0); |
| unsigned skip = offset.constantValue() / component_size; |
| for (unsigned i = skip; i < num_components; i++) |
| elems[i - skip] = emit_extract_vector(ctx, vec, i, rc); |
| |
| if (dst.type() == RegType::vgpr) { |
| /* if dst is vgpr - split the src and create a shrunk version according to the mask. */ |
| num_components = dst.bytes() / component_size; |
| aco_ptr<Pseudo_instruction> create_vec{create_instruction<Pseudo_instruction>( |
| aco_opcode::p_create_vector, Format::PSEUDO, num_components, 1)}; |
| for (unsigned i = 0; i < num_components; i++) |
| create_vec->operands[i] = Operand(elems[i]); |
| create_vec->definitions[0] = Definition(dst); |
| bld.insert(std::move(create_vec)); |
| |
| } else if (skip) { |
| /* if dst is sgpr - split the src, but move the original to sgpr. */ |
| vec = bld.pseudo(aco_opcode::p_as_uniform, bld.def(RegClass(RegType::sgpr, vec.size())), vec); |
| byte_align_scalar(ctx, vec, offset, dst); |
| } else { |
| assert(dst.size() == vec.size()); |
| bld.pseudo(aco_opcode::p_as_uniform, Definition(dst), vec); |
| } |
| |
| ctx->allocated_vec.emplace(dst.id(), elems); |
| } |
| |
| Temp |
| get_ssa_temp_tex(struct isel_context* ctx, nir_ssa_def* def, bool is_16bit) |
| { |
| RegClass rc = RegClass::get(RegType::vgpr, (is_16bit ? 2 : 4) * def->num_components); |
| Temp tmp = get_ssa_temp(ctx, def); |
| if (tmp.bytes() != rc.bytes()) |
| return emit_extract_vector(ctx, tmp, 0, rc); |
| else |
| return tmp; |
| } |
| |
| Temp |
| bool_to_vector_condition(isel_context* ctx, Temp val, Temp dst = Temp(0, s2)) |
| { |
| Builder bld(ctx->program, ctx->block); |
| if (!dst.id()) |
| dst = bld.tmp(bld.lm); |
| |
| assert(val.regClass() == s1); |
| assert(dst.regClass() == bld.lm); |
| |
| return bld.sop2(Builder::s_cselect, Definition(dst), Operand::c32(-1), Operand::zero(), |
| bld.scc(val)); |
| } |
| |
| Temp |
| bool_to_scalar_condition(isel_context* ctx, Temp val, Temp dst = Temp(0, s1)) |
| { |
| Builder bld(ctx->program, ctx->block); |
| if (!dst.id()) |
| dst = bld.tmp(s1); |
| |
| assert(val.regClass() == bld.lm); |
| assert(dst.regClass() == s1); |
| |
| /* if we're currently in WQM mode, ensure that the source is also computed in WQM */ |
| bld.sop2(Builder::s_and, bld.def(bld.lm), bld.scc(Definition(dst)), val, Operand(exec, bld.lm)); |
| return dst; |
| } |
| |
| /** |
| * Copies the first src_bits of the input to the output Temp. Input bits at positions larger than |
| * src_bits and dst_bits are truncated. |
| * |
| * Sign extension may be applied using the sign_extend parameter. The position of the input sign |
| * bit is indicated by src_bits in this case. |
| * |
| * If dst.bytes() is larger than dst_bits/8, the value of the upper bits is undefined. |
| */ |
| Temp |
| convert_int(isel_context* ctx, Builder& bld, Temp src, unsigned src_bits, unsigned dst_bits, |
| bool sign_extend, Temp dst = Temp()) |
| { |
| assert(!(sign_extend && dst_bits < src_bits) && |
| "Shrinking integers is not supported for signed inputs"); |
| |
| if (!dst.id()) { |
| if (dst_bits % 32 == 0 || src.type() == RegType::sgpr) |
| dst = bld.tmp(src.type(), DIV_ROUND_UP(dst_bits, 32u)); |
| else |
| dst = bld.tmp(RegClass(RegType::vgpr, dst_bits / 8u).as_subdword()); |
| } |
| |
| assert(src.type() == RegType::sgpr || src_bits == src.bytes() * 8); |
| assert(dst.type() == RegType::sgpr || dst_bits == dst.bytes() * 8); |
| |
| if (dst.bytes() == src.bytes() && dst_bits < src_bits) { |
| /* Copy the raw value, leaving an undefined value in the upper bits for |
| * the caller to handle appropriately */ |
| return bld.copy(Definition(dst), src); |
| } else if (dst.bytes() < src.bytes()) { |
| return bld.pseudo(aco_opcode::p_extract_vector, Definition(dst), src, Operand::zero()); |
| } |
| |
| Temp tmp = dst; |
| if (dst_bits == 64) |
| tmp = src_bits == 32 ? src : bld.tmp(src.type(), 1); |
| |
| if (tmp == src) { |
| } else if (src.regClass() == s1) { |
| assert(src_bits < 32); |
| bld.pseudo(aco_opcode::p_extract, Definition(tmp), bld.def(s1, scc), src, Operand::zero(), |
| Operand::c32(src_bits), Operand::c32((unsigned)sign_extend)); |
| } else { |
| assert(src_bits < 32); |
| bld.pseudo(aco_opcode::p_extract, Definition(tmp), src, Operand::zero(), Operand::c32(src_bits), |
| Operand::c32((unsigned)sign_extend)); |
| } |
| |
| if (dst_bits == 64) { |
| if (sign_extend && dst.regClass() == s2) { |
| Temp high = |
| bld.sop2(aco_opcode::s_ashr_i32, bld.def(s1), bld.def(s1, scc), tmp, Operand::c32(31u)); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), tmp, high); |
| } else if (sign_extend && dst.regClass() == v2) { |
| Temp high = bld.vop2(aco_opcode::v_ashrrev_i32, bld.def(v1), Operand::c32(31u), tmp); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), tmp, high); |
| } else { |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), tmp, Operand::zero()); |
| } |
| } |
| |
| return dst; |
| } |
| |
| enum sgpr_extract_mode { |
| sgpr_extract_sext, |
| sgpr_extract_zext, |
| sgpr_extract_undef, |
| }; |
| |
| Temp |
| extract_8_16_bit_sgpr_element(isel_context* ctx, Temp dst, nir_alu_src* src, sgpr_extract_mode mode) |
| { |
| Temp vec = get_ssa_temp(ctx, src->src.ssa); |
| unsigned src_size = src->src.ssa->bit_size; |
| unsigned swizzle = src->swizzle[0]; |
| |
| if (vec.size() > 1) { |
| assert(src_size == 16); |
| vec = emit_extract_vector(ctx, vec, swizzle / 2, s1); |
| swizzle = swizzle & 1; |
| } |
| |
| Builder bld(ctx->program, ctx->block); |
| Temp tmp = dst.regClass() == s2 ? bld.tmp(s1) : dst; |
| |
| if (mode == sgpr_extract_undef && swizzle == 0) |
| bld.copy(Definition(tmp), vec); |
| else |
| bld.pseudo(aco_opcode::p_extract, Definition(tmp), bld.def(s1, scc), Operand(vec), |
| Operand::c32(swizzle), Operand::c32(src_size), |
| Operand::c32((mode == sgpr_extract_sext))); |
| |
| if (dst.regClass() == s2) |
| convert_int(ctx, bld, tmp, 32, 64, mode == sgpr_extract_sext, dst); |
| |
| return dst; |
| } |
| |
| Temp |
| get_alu_src(struct isel_context* ctx, nir_alu_src src, unsigned size = 1) |
| { |
| if (src.src.ssa->num_components == 1 && size == 1) |
| return get_ssa_temp(ctx, src.src.ssa); |
| |
| Temp vec = get_ssa_temp(ctx, src.src.ssa); |
| unsigned elem_size = src.src.ssa->bit_size / 8u; |
| bool identity_swizzle = true; |
| |
| for (unsigned i = 0; identity_swizzle && i < size; i++) { |
| if (src.swizzle[i] != i) |
| identity_swizzle = false; |
| } |
| if (identity_swizzle) |
| return emit_extract_vector(ctx, vec, 0, RegClass::get(vec.type(), elem_size * size)); |
| |
| assert(elem_size > 0); |
| assert(vec.bytes() % elem_size == 0); |
| |
| if (elem_size < 4 && vec.type() == RegType::sgpr && size == 1) { |
| assert(src.src.ssa->bit_size == 8 || src.src.ssa->bit_size == 16); |
| return extract_8_16_bit_sgpr_element(ctx, ctx->program->allocateTmp(s1), &src, |
| sgpr_extract_undef); |
| } |
| |
| bool as_uniform = elem_size < 4 && vec.type() == RegType::sgpr; |
| if (as_uniform) |
| vec = as_vgpr(ctx, vec); |
| |
| RegClass elem_rc = elem_size < 4 ? RegClass(vec.type(), elem_size).as_subdword() |
| : RegClass(vec.type(), elem_size / 4); |
| if (size == 1) { |
| return emit_extract_vector(ctx, vec, src.swizzle[0], elem_rc); |
| } else { |
| assert(size <= 4); |
| std::array<Temp, NIR_MAX_VEC_COMPONENTS> elems; |
| aco_ptr<Pseudo_instruction> vec_instr{create_instruction<Pseudo_instruction>( |
| aco_opcode::p_create_vector, Format::PSEUDO, size, 1)}; |
| for (unsigned i = 0; i < size; ++i) { |
| elems[i] = emit_extract_vector(ctx, vec, src.swizzle[i], elem_rc); |
| vec_instr->operands[i] = Operand{elems[i]}; |
| } |
| Temp dst = ctx->program->allocateTmp(RegClass(vec.type(), elem_size * size / 4)); |
| vec_instr->definitions[0] = Definition(dst); |
| ctx->block->instructions.emplace_back(std::move(vec_instr)); |
| ctx->allocated_vec.emplace(dst.id(), elems); |
| return vec.type() == RegType::sgpr ? Builder(ctx->program, ctx->block).as_uniform(dst) : dst; |
| } |
| } |
| |
| Temp |
| get_alu_src_vop3p(struct isel_context* ctx, nir_alu_src src) |
| { |
| /* returns v2b or v1 for vop3p usage. |
| * The source expects exactly 2 16bit components |
| * which are within the same dword |
| */ |
| assert(src.src.ssa->bit_size == 16); |
| assert(src.swizzle[0] >> 1 == src.swizzle[1] >> 1); |
| |
| Temp tmp = get_ssa_temp(ctx, src.src.ssa); |
| if (tmp.size() == 1) |
| return tmp; |
| |
| /* the size is larger than 1 dword: check the swizzle */ |
| unsigned dword = src.swizzle[0] >> 1; |
| |
| /* extract a full dword if possible */ |
| if (tmp.bytes() >= (dword + 1) * 4) { |
| /* if the source is splitted into components, use p_create_vector */ |
| auto it = ctx->allocated_vec.find(tmp.id()); |
| if (it != ctx->allocated_vec.end()) { |
| unsigned index = dword << 1; |
| Builder bld(ctx->program, ctx->block); |
| if (it->second[index].regClass() == v2b) |
| return bld.pseudo(aco_opcode::p_create_vector, bld.def(v1), it->second[index], |
| it->second[index + 1]); |
| } |
| return emit_extract_vector(ctx, tmp, dword, v1); |
| } else { |
| /* This must be a swizzled access to %a.zz where %a is v6b */ |
| assert(((src.swizzle[0] | src.swizzle[1]) & 1) == 0); |
| assert(tmp.regClass() == v6b && dword == 1); |
| return emit_extract_vector(ctx, tmp, dword * 2, v2b); |
| } |
| } |
| |
| uint32_t |
| get_alu_src_ub(isel_context* ctx, nir_alu_instr* instr, int src_idx) |
| { |
| nir_ssa_scalar scalar = |
| nir_ssa_scalar{instr->src[src_idx].src.ssa, instr->src[src_idx].swizzle[0]}; |
| return nir_unsigned_upper_bound(ctx->shader, ctx->range_ht, scalar, &ctx->ub_config); |
| } |
| |
| Temp |
| convert_pointer_to_64_bit(isel_context* ctx, Temp ptr, bool non_uniform = false) |
| { |
| if (ptr.size() == 2) |
| return ptr; |
| Builder bld(ctx->program, ctx->block); |
| if (ptr.type() == RegType::vgpr && !non_uniform) |
| ptr = bld.as_uniform(ptr); |
| return bld.pseudo(aco_opcode::p_create_vector, bld.def(RegClass(ptr.type(), 2)), ptr, |
| Operand::c32((unsigned)ctx->options->address32_hi)); |
| } |
| |
| void |
| emit_sop2_instruction(isel_context* ctx, nir_alu_instr* instr, aco_opcode op, Temp dst, |
| bool writes_scc, uint8_t uses_ub = 0) |
| { |
| aco_ptr<SOP2_instruction> sop2{ |
| create_instruction<SOP2_instruction>(op, Format::SOP2, 2, writes_scc ? 2 : 1)}; |
| sop2->operands[0] = Operand(get_alu_src(ctx, instr->src[0])); |
| sop2->operands[1] = Operand(get_alu_src(ctx, instr->src[1])); |
| sop2->definitions[0] = Definition(dst); |
| if (instr->no_unsigned_wrap) |
| sop2->definitions[0].setNUW(true); |
| if (writes_scc) |
| sop2->definitions[1] = Definition(ctx->program->allocateId(s1), scc, s1); |
| |
| for (int i = 0; i < 2; i++) { |
| if (uses_ub & (1 << i)) { |
| uint32_t src_ub = get_alu_src_ub(ctx, instr, i); |
| if (src_ub <= 0xffff) |
| sop2->operands[i].set16bit(true); |
| else if (src_ub <= 0xffffff) |
| sop2->operands[i].set24bit(true); |
| } |
| } |
| |
| ctx->block->instructions.emplace_back(std::move(sop2)); |
| } |
| |
| void |
| emit_vop2_instruction(isel_context* ctx, nir_alu_instr* instr, aco_opcode opc, Temp dst, |
| bool commutative, bool swap_srcs = false, bool flush_denorms = false, |
| bool nuw = false, uint8_t uses_ub = 0) |
| { |
| Builder bld(ctx->program, ctx->block); |
| bld.is_precise = instr->exact; |
| |
| Temp src0 = get_alu_src(ctx, instr->src[swap_srcs ? 1 : 0]); |
| Temp src1 = get_alu_src(ctx, instr->src[swap_srcs ? 0 : 1]); |
| if (src1.type() == RegType::sgpr) { |
| if (commutative && src0.type() == RegType::vgpr) { |
| Temp t = src0; |
| src0 = src1; |
| src1 = t; |
| } else { |
| src1 = as_vgpr(ctx, src1); |
| } |
| } |
| |
| Operand op[2] = {Operand(src0), Operand(src1)}; |
| |
| for (int i = 0; i < 2; i++) { |
| if (uses_ub & (1 << i)) { |
| uint32_t src_ub = get_alu_src_ub(ctx, instr, swap_srcs ? !i : i); |
| if (src_ub <= 0xffff) |
| op[i].set16bit(true); |
| else if (src_ub <= 0xffffff) |
| op[i].set24bit(true); |
| } |
| } |
| |
| if (flush_denorms && ctx->program->gfx_level < GFX9) { |
| assert(dst.size() == 1); |
| Temp tmp = bld.vop2(opc, bld.def(v1), op[0], op[1]); |
| bld.vop2(aco_opcode::v_mul_f32, Definition(dst), Operand::c32(0x3f800000u), tmp); |
| } else { |
| if (nuw) { |
| bld.nuw().vop2(opc, Definition(dst), op[0], op[1]); |
| } else { |
| bld.vop2(opc, Definition(dst), op[0], op[1]); |
| } |
| } |
| } |
| |
| void |
| emit_vop2_instruction_logic64(isel_context* ctx, nir_alu_instr* instr, aco_opcode op, Temp dst) |
| { |
| Builder bld(ctx->program, ctx->block); |
| bld.is_precise = instr->exact; |
| |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| |
| if (src1.type() == RegType::sgpr) { |
| assert(src0.type() == RegType::vgpr); |
| std::swap(src0, src1); |
| } |
| |
| Temp src00 = bld.tmp(src0.type(), 1); |
| Temp src01 = bld.tmp(src0.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src00), Definition(src01), src0); |
| Temp src10 = bld.tmp(v1); |
| Temp src11 = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src10), Definition(src11), src1); |
| Temp lo = bld.vop2(op, bld.def(v1), src00, src10); |
| Temp hi = bld.vop2(op, bld.def(v1), src01, src11); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), lo, hi); |
| } |
| |
| void |
| emit_vop3a_instruction(isel_context* ctx, nir_alu_instr* instr, aco_opcode op, Temp dst, |
| bool flush_denorms = false, unsigned num_sources = 2, bool swap_srcs = false) |
| { |
| assert(num_sources == 2 || num_sources == 3); |
| Temp src[3] = {Temp(0, v1), Temp(0, v1), Temp(0, v1)}; |
| bool has_sgpr = false; |
| for (unsigned i = 0; i < num_sources; i++) { |
| src[i] = get_alu_src(ctx, instr->src[swap_srcs ? 1 - i : i]); |
| if (has_sgpr) |
| src[i] = as_vgpr(ctx, src[i]); |
| else |
| has_sgpr = src[i].type() == RegType::sgpr; |
| } |
| |
| Builder bld(ctx->program, ctx->block); |
| bld.is_precise = instr->exact; |
| if (flush_denorms && ctx->program->gfx_level < GFX9) { |
| Temp tmp; |
| if (num_sources == 3) |
| tmp = bld.vop3(op, bld.def(dst.regClass()), src[0], src[1], src[2]); |
| else |
| tmp = bld.vop3(op, bld.def(dst.regClass()), src[0], src[1]); |
| if (dst.size() == 1) |
| bld.vop2(aco_opcode::v_mul_f32, Definition(dst), Operand::c32(0x3f800000u), tmp); |
| else |
| bld.vop3(aco_opcode::v_mul_f64, Definition(dst), Operand::c64(0x3FF0000000000000), tmp); |
| } else if (num_sources == 3) { |
| bld.vop3(op, Definition(dst), src[0], src[1], src[2]); |
| } else { |
| bld.vop3(op, Definition(dst), src[0], src[1]); |
| } |
| } |
| |
| Builder::Result |
| emit_vop3p_instruction(isel_context* ctx, nir_alu_instr* instr, aco_opcode op, Temp dst, |
| bool swap_srcs = false) |
| { |
| Temp src0 = get_alu_src_vop3p(ctx, instr->src[swap_srcs]); |
| Temp src1 = get_alu_src_vop3p(ctx, instr->src[!swap_srcs]); |
| if (src0.type() == RegType::sgpr && src1.type() == RegType::sgpr) |
| src1 = as_vgpr(ctx, src1); |
| assert(instr->dest.dest.ssa.num_components == 2); |
| |
| /* swizzle to opsel: all swizzles are either 0 (x) or 1 (y) */ |
| unsigned opsel_lo = |
| (instr->src[!swap_srcs].swizzle[0] & 1) << 1 | (instr->src[swap_srcs].swizzle[0] & 1); |
| unsigned opsel_hi = |
| (instr->src[!swap_srcs].swizzle[1] & 1) << 1 | (instr->src[swap_srcs].swizzle[1] & 1); |
| |
| Builder bld(ctx->program, ctx->block); |
| bld.is_precise = instr->exact; |
| Builder::Result res = bld.vop3p(op, Definition(dst), src0, src1, opsel_lo, opsel_hi); |
| return res; |
| } |
| |
| void |
| emit_idot_instruction(isel_context* ctx, nir_alu_instr* instr, aco_opcode op, Temp dst, bool clamp, |
| unsigned neg_lo = 0) |
| { |
| Temp src[3] = {Temp(0, v1), Temp(0, v1), Temp(0, v1)}; |
| bool has_sgpr = false; |
| for (unsigned i = 0; i < 3; i++) { |
| src[i] = get_alu_src(ctx, instr->src[i]); |
| if (has_sgpr) |
| src[i] = as_vgpr(ctx, src[i]); |
| else |
| has_sgpr = src[i].type() == RegType::sgpr; |
| } |
| |
| Builder bld(ctx->program, ctx->block); |
| bld.is_precise = instr->exact; |
| VOP3P_instruction& vop3p = |
| bld.vop3p(op, Definition(dst), src[0], src[1], src[2], 0x0, 0x7).instr->vop3p(); |
| vop3p.clamp = clamp; |
| u_foreach_bit (i, neg_lo) |
| vop3p.neg_lo[i] = true; |
| } |
| |
| void |
| emit_vop1_instruction(isel_context* ctx, nir_alu_instr* instr, aco_opcode op, Temp dst) |
| { |
| Builder bld(ctx->program, ctx->block); |
| bld.is_precise = instr->exact; |
| if (dst.type() == RegType::sgpr) |
| bld.pseudo(aco_opcode::p_as_uniform, Definition(dst), |
| bld.vop1(op, bld.def(RegType::vgpr, dst.size()), get_alu_src(ctx, instr->src[0]))); |
| else |
| bld.vop1(op, Definition(dst), get_alu_src(ctx, instr->src[0])); |
| } |
| |
| void |
| emit_vopc_instruction(isel_context* ctx, nir_alu_instr* instr, aco_opcode op, Temp dst) |
| { |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| assert(src0.size() == src1.size()); |
| |
| aco_ptr<Instruction> vopc; |
| if (src1.type() == RegType::sgpr) { |
| if (src0.type() == RegType::vgpr) { |
| /* to swap the operands, we might also have to change the opcode */ |
| switch (op) { |
| case aco_opcode::v_cmp_lt_f16: op = aco_opcode::v_cmp_gt_f16; break; |
| case aco_opcode::v_cmp_ge_f16: op = aco_opcode::v_cmp_le_f16; break; |
| case aco_opcode::v_cmp_lt_i16: op = aco_opcode::v_cmp_gt_i16; break; |
| case aco_opcode::v_cmp_ge_i16: op = aco_opcode::v_cmp_le_i16; break; |
| case aco_opcode::v_cmp_lt_u16: op = aco_opcode::v_cmp_gt_u16; break; |
| case aco_opcode::v_cmp_ge_u16: op = aco_opcode::v_cmp_le_u16; break; |
| case aco_opcode::v_cmp_lt_f32: op = aco_opcode::v_cmp_gt_f32; break; |
| case aco_opcode::v_cmp_ge_f32: op = aco_opcode::v_cmp_le_f32; break; |
| case aco_opcode::v_cmp_lt_i32: op = aco_opcode::v_cmp_gt_i32; break; |
| case aco_opcode::v_cmp_ge_i32: op = aco_opcode::v_cmp_le_i32; break; |
| case aco_opcode::v_cmp_lt_u32: op = aco_opcode::v_cmp_gt_u32; break; |
| case aco_opcode::v_cmp_ge_u32: op = aco_opcode::v_cmp_le_u32; break; |
| case aco_opcode::v_cmp_lt_f64: op = aco_opcode::v_cmp_gt_f64; break; |
| case aco_opcode::v_cmp_ge_f64: op = aco_opcode::v_cmp_le_f64; break; |
| case aco_opcode::v_cmp_lt_i64: op = aco_opcode::v_cmp_gt_i64; break; |
| case aco_opcode::v_cmp_ge_i64: op = aco_opcode::v_cmp_le_i64; break; |
| case aco_opcode::v_cmp_lt_u64: op = aco_opcode::v_cmp_gt_u64; break; |
| case aco_opcode::v_cmp_ge_u64: op = aco_opcode::v_cmp_le_u64; break; |
| default: /* eq and ne are commutative */ break; |
| } |
| Temp t = src0; |
| src0 = src1; |
| src1 = t; |
| } else { |
| src1 = as_vgpr(ctx, src1); |
| } |
| } |
| |
| Builder bld(ctx->program, ctx->block); |
| bld.vopc(op, Definition(dst), src0, src1); |
| } |
| |
| void |
| emit_sopc_instruction(isel_context* ctx, nir_alu_instr* instr, aco_opcode op, Temp dst) |
| { |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| Builder bld(ctx->program, ctx->block); |
| |
| assert(dst.regClass() == bld.lm); |
| assert(src0.type() == RegType::sgpr); |
| assert(src1.type() == RegType::sgpr); |
| assert(src0.regClass() == src1.regClass()); |
| |
| /* Emit the SALU comparison instruction */ |
| Temp cmp = bld.sopc(op, bld.scc(bld.def(s1)), src0, src1); |
| /* Turn the result into a per-lane bool */ |
| bool_to_vector_condition(ctx, cmp, dst); |
| } |
| |
| void |
| emit_comparison(isel_context* ctx, nir_alu_instr* instr, Temp dst, aco_opcode v16_op, |
| aco_opcode v32_op, aco_opcode v64_op, aco_opcode s32_op = aco_opcode::num_opcodes, |
| aco_opcode s64_op = aco_opcode::num_opcodes) |
| { |
| aco_opcode s_op = instr->src[0].src.ssa->bit_size == 64 ? s64_op |
| : instr->src[0].src.ssa->bit_size == 32 ? s32_op |
| : aco_opcode::num_opcodes; |
| aco_opcode v_op = instr->src[0].src.ssa->bit_size == 64 ? v64_op |
| : instr->src[0].src.ssa->bit_size == 32 ? v32_op |
| : v16_op; |
| bool use_valu = s_op == aco_opcode::num_opcodes || nir_dest_is_divergent(instr->dest.dest) || |
| get_ssa_temp(ctx, instr->src[0].src.ssa).type() == RegType::vgpr || |
| get_ssa_temp(ctx, instr->src[1].src.ssa).type() == RegType::vgpr; |
| aco_opcode op = use_valu ? v_op : s_op; |
| assert(op != aco_opcode::num_opcodes); |
| assert(dst.regClass() == ctx->program->lane_mask); |
| |
| if (use_valu) |
| emit_vopc_instruction(ctx, instr, op, dst); |
| else |
| emit_sopc_instruction(ctx, instr, op, dst); |
| } |
| |
| void |
| emit_boolean_logic(isel_context* ctx, nir_alu_instr* instr, Builder::WaveSpecificOpcode op, |
| Temp dst) |
| { |
| Builder bld(ctx->program, ctx->block); |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| |
| assert(dst.regClass() == bld.lm); |
| assert(src0.regClass() == bld.lm); |
| assert(src1.regClass() == bld.lm); |
| |
| bld.sop2(op, Definition(dst), bld.def(s1, scc), src0, src1); |
| } |
| |
| void |
| emit_bcsel(isel_context* ctx, nir_alu_instr* instr, Temp dst) |
| { |
| Builder bld(ctx->program, ctx->block); |
| Temp cond = get_alu_src(ctx, instr->src[0]); |
| Temp then = get_alu_src(ctx, instr->src[1]); |
| Temp els = get_alu_src(ctx, instr->src[2]); |
| |
| assert(cond.regClass() == bld.lm); |
| |
| if (dst.type() == RegType::vgpr) { |
| aco_ptr<Instruction> bcsel; |
| if (dst.size() == 1) { |
| then = as_vgpr(ctx, then); |
| els = as_vgpr(ctx, els); |
| |
| bld.vop2(aco_opcode::v_cndmask_b32, Definition(dst), els, then, cond); |
| } else if (dst.size() == 2) { |
| Temp then_lo = bld.tmp(v1), then_hi = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(then_lo), Definition(then_hi), then); |
| Temp else_lo = bld.tmp(v1), else_hi = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(else_lo), Definition(else_hi), els); |
| |
| Temp dst0 = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), else_lo, then_lo, cond); |
| Temp dst1 = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), else_hi, then_hi, cond); |
| |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), dst0, dst1); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| return; |
| } |
| |
| if (instr->dest.dest.ssa.bit_size == 1) { |
| assert(dst.regClass() == bld.lm); |
| assert(then.regClass() == bld.lm); |
| assert(els.regClass() == bld.lm); |
| } |
| |
| if (!nir_src_is_divergent(instr->src[0].src)) { /* uniform condition and values in sgpr */ |
| if (dst.regClass() == s1 || dst.regClass() == s2) { |
| assert((then.regClass() == s1 || then.regClass() == s2) && |
| els.regClass() == then.regClass()); |
| assert(dst.size() == then.size()); |
| aco_opcode op = |
| dst.regClass() == s1 ? aco_opcode::s_cselect_b32 : aco_opcode::s_cselect_b64; |
| bld.sop2(op, Definition(dst), then, els, bld.scc(bool_to_scalar_condition(ctx, cond))); |
| } else { |
| isel_err(&instr->instr, "Unimplemented uniform bcsel bit size"); |
| } |
| return; |
| } |
| |
| /* divergent boolean bcsel |
| * this implements bcsel on bools: dst = s0 ? s1 : s2 |
| * are going to be: dst = (s0 & s1) | (~s0 & s2) */ |
| assert(instr->dest.dest.ssa.bit_size == 1); |
| |
| if (cond.id() != then.id()) |
| then = bld.sop2(Builder::s_and, bld.def(bld.lm), bld.def(s1, scc), cond, then); |
| |
| if (cond.id() == els.id()) |
| bld.copy(Definition(dst), then); |
| else |
| bld.sop2(Builder::s_or, Definition(dst), bld.def(s1, scc), then, |
| bld.sop2(Builder::s_andn2, bld.def(bld.lm), bld.def(s1, scc), els, cond)); |
| } |
| |
| void |
| emit_scaled_op(isel_context* ctx, Builder& bld, Definition dst, Temp val, aco_opcode op, |
| uint32_t undo) |
| { |
| /* multiply by 16777216 to handle denormals */ |
| Temp is_denormal = bld.vopc(aco_opcode::v_cmp_class_f32, bld.def(bld.lm), as_vgpr(ctx, val), |
| bld.copy(bld.def(v1), Operand::c32((1u << 7) | (1u << 4)))); |
| Temp scaled = bld.vop2(aco_opcode::v_mul_f32, bld.def(v1), Operand::c32(0x4b800000u), val); |
| scaled = bld.vop1(op, bld.def(v1), scaled); |
| scaled = bld.vop2(aco_opcode::v_mul_f32, bld.def(v1), Operand::c32(undo), scaled); |
| |
| Temp not_scaled = bld.vop1(op, bld.def(v1), val); |
| |
| bld.vop2(aco_opcode::v_cndmask_b32, dst, not_scaled, scaled, is_denormal); |
| } |
| |
| void |
| emit_rcp(isel_context* ctx, Builder& bld, Definition dst, Temp val) |
| { |
| if (ctx->block->fp_mode.denorm32 == 0) { |
| bld.vop1(aco_opcode::v_rcp_f32, dst, val); |
| return; |
| } |
| |
| emit_scaled_op(ctx, bld, dst, val, aco_opcode::v_rcp_f32, 0x4b800000u); |
| } |
| |
| void |
| emit_rsq(isel_context* ctx, Builder& bld, Definition dst, Temp val) |
| { |
| if (ctx->block->fp_mode.denorm32 == 0) { |
| bld.vop1(aco_opcode::v_rsq_f32, dst, val); |
| return; |
| } |
| |
| emit_scaled_op(ctx, bld, dst, val, aco_opcode::v_rsq_f32, 0x45800000u); |
| } |
| |
| void |
| emit_sqrt(isel_context* ctx, Builder& bld, Definition dst, Temp val) |
| { |
| if (ctx->block->fp_mode.denorm32 == 0) { |
| bld.vop1(aco_opcode::v_sqrt_f32, dst, val); |
| return; |
| } |
| |
| emit_scaled_op(ctx, bld, dst, val, aco_opcode::v_sqrt_f32, 0x39800000u); |
| } |
| |
| void |
| emit_log2(isel_context* ctx, Builder& bld, Definition dst, Temp val) |
| { |
| if (ctx->block->fp_mode.denorm32 == 0) { |
| bld.vop1(aco_opcode::v_log_f32, dst, val); |
| return; |
| } |
| |
| emit_scaled_op(ctx, bld, dst, val, aco_opcode::v_log_f32, 0xc1c00000u); |
| } |
| |
| Temp |
| emit_trunc_f64(isel_context* ctx, Builder& bld, Definition dst, Temp val) |
| { |
| if (ctx->options->gfx_level >= GFX7) |
| return bld.vop1(aco_opcode::v_trunc_f64, Definition(dst), val); |
| |
| /* GFX6 doesn't support V_TRUNC_F64, lower it. */ |
| /* TODO: create more efficient code! */ |
| if (val.type() == RegType::sgpr) |
| val = as_vgpr(ctx, val); |
| |
| /* Split the input value. */ |
| Temp val_lo = bld.tmp(v1), val_hi = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(val_lo), Definition(val_hi), val); |
| |
| /* Extract the exponent and compute the unbiased value. */ |
| Temp exponent = |
| bld.vop3(aco_opcode::v_bfe_u32, bld.def(v1), val_hi, Operand::c32(20u), Operand::c32(11u)); |
| exponent = bld.vsub32(bld.def(v1), exponent, Operand::c32(1023u)); |
| |
| /* Extract the fractional part. */ |
| Temp fract_mask = bld.pseudo(aco_opcode::p_create_vector, bld.def(v2), Operand::c32(-1u), |
| Operand::c32(0x000fffffu)); |
| fract_mask = bld.vop3(aco_opcode::v_lshr_b64, bld.def(v2), fract_mask, exponent); |
| |
| Temp fract_mask_lo = bld.tmp(v1), fract_mask_hi = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(fract_mask_lo), Definition(fract_mask_hi), |
| fract_mask); |
| |
| Temp fract_lo = bld.tmp(v1), fract_hi = bld.tmp(v1); |
| Temp tmp = bld.vop1(aco_opcode::v_not_b32, bld.def(v1), fract_mask_lo); |
| fract_lo = bld.vop2(aco_opcode::v_and_b32, bld.def(v1), val_lo, tmp); |
| tmp = bld.vop1(aco_opcode::v_not_b32, bld.def(v1), fract_mask_hi); |
| fract_hi = bld.vop2(aco_opcode::v_and_b32, bld.def(v1), val_hi, tmp); |
| |
| /* Get the sign bit. */ |
| Temp sign = bld.vop2(aco_opcode::v_and_b32, bld.def(v1), Operand::c32(0x80000000u), val_hi); |
| |
| /* Decide the operation to apply depending on the unbiased exponent. */ |
| Temp exp_lt0 = |
| bld.vopc_e64(aco_opcode::v_cmp_lt_i32, bld.def(bld.lm), exponent, Operand::zero()); |
| Temp dst_lo = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), fract_lo, |
| bld.copy(bld.def(v1), Operand::zero()), exp_lt0); |
| Temp dst_hi = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), fract_hi, sign, exp_lt0); |
| Temp exp_gt51 = bld.vopc_e64(aco_opcode::v_cmp_gt_i32, bld.def(s2), exponent, Operand::c32(51u)); |
| dst_lo = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), dst_lo, val_lo, exp_gt51); |
| dst_hi = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), dst_hi, val_hi, exp_gt51); |
| |
| return bld.pseudo(aco_opcode::p_create_vector, Definition(dst), dst_lo, dst_hi); |
| } |
| |
| Temp |
| emit_floor_f64(isel_context* ctx, Builder& bld, Definition dst, Temp val) |
| { |
| if (ctx->options->gfx_level >= GFX7) |
| return bld.vop1(aco_opcode::v_floor_f64, Definition(dst), val); |
| |
| /* GFX6 doesn't support V_FLOOR_F64, lower it (note that it's actually |
| * lowered at NIR level for precision reasons). */ |
| Temp src0 = as_vgpr(ctx, val); |
| |
| Temp mask = bld.copy(bld.def(s1), Operand::c32(3u)); /* isnan */ |
| Temp min_val = bld.pseudo(aco_opcode::p_create_vector, bld.def(s2), Operand::c32(-1u), |
| Operand::c32(0x3fefffffu)); |
| |
| Temp isnan = bld.vopc_e64(aco_opcode::v_cmp_class_f64, bld.def(bld.lm), src0, mask); |
| Temp fract = bld.vop1(aco_opcode::v_fract_f64, bld.def(v2), src0); |
| Temp min = bld.vop3(aco_opcode::v_min_f64, bld.def(v2), fract, min_val); |
| |
| Temp then_lo = bld.tmp(v1), then_hi = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(then_lo), Definition(then_hi), src0); |
| Temp else_lo = bld.tmp(v1), else_hi = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(else_lo), Definition(else_hi), min); |
| |
| Temp dst0 = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), else_lo, then_lo, isnan); |
| Temp dst1 = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), else_hi, then_hi, isnan); |
| |
| Temp v = bld.pseudo(aco_opcode::p_create_vector, bld.def(v2), dst0, dst1); |
| |
| Instruction* add = bld.vop3(aco_opcode::v_add_f64, Definition(dst), src0, v); |
| add->vop3().neg[1] = true; |
| |
| return add->definitions[0].getTemp(); |
| } |
| |
| Temp |
| uadd32_sat(Builder& bld, Definition dst, Temp src0, Temp src1) |
| { |
| if (bld.program->gfx_level < GFX8) { |
| Builder::Result add = bld.vadd32(bld.def(v1), src0, src1, true); |
| return bld.vop2_e64(aco_opcode::v_cndmask_b32, dst, add.def(0).getTemp(), Operand::c32(-1), |
| add.def(1).getTemp()); |
| } |
| |
| Builder::Result add(NULL); |
| if (bld.program->gfx_level >= GFX9) { |
| add = bld.vop2_e64(aco_opcode::v_add_u32, dst, src0, src1); |
| } else { |
| add = bld.vop2_e64(aco_opcode::v_add_co_u32, dst, bld.def(bld.lm), src0, src1); |
| } |
| add.instr->vop3().clamp = 1; |
| return dst.getTemp(); |
| } |
| |
| Temp |
| usub32_sat(Builder& bld, Definition dst, Temp src0, Temp src1) |
| { |
| if (bld.program->gfx_level < GFX8) { |
| Builder::Result sub = bld.vsub32(bld.def(v1), src0, src1, true); |
| return bld.vop2_e64(aco_opcode::v_cndmask_b32, dst, sub.def(0).getTemp(), Operand::c32(0u), |
| sub.def(1).getTemp()); |
| } |
| |
| Builder::Result sub(NULL); |
| if (bld.program->gfx_level >= GFX9) { |
| sub = bld.vop2_e64(aco_opcode::v_sub_u32, dst, src0, src1); |
| } else { |
| sub = bld.vop2_e64(aco_opcode::v_sub_co_u32, dst, bld.def(bld.lm), src0, src1); |
| } |
| sub.instr->vop3().clamp = 1; |
| return dst.getTemp(); |
| } |
| |
| void |
| visit_alu_instr(isel_context* ctx, nir_alu_instr* instr) |
| { |
| if (!instr->dest.dest.is_ssa) { |
| isel_err(&instr->instr, "nir alu dst not in ssa"); |
| abort(); |
| } |
| Builder bld(ctx->program, ctx->block); |
| bld.is_precise = instr->exact; |
| Temp dst = get_ssa_temp(ctx, &instr->dest.dest.ssa); |
| switch (instr->op) { |
| case nir_op_vec2: |
| case nir_op_vec3: |
| case nir_op_vec4: |
| case nir_op_vec5: |
| case nir_op_vec8: |
| case nir_op_vec16: { |
| std::array<Temp, NIR_MAX_VEC_COMPONENTS> elems; |
| unsigned num = instr->dest.dest.ssa.num_components; |
| for (unsigned i = 0; i < num; ++i) |
| elems[i] = get_alu_src(ctx, instr->src[i]); |
| |
| if (instr->dest.dest.ssa.bit_size >= 32 || dst.type() == RegType::vgpr) { |
| aco_ptr<Pseudo_instruction> vec{create_instruction<Pseudo_instruction>( |
| aco_opcode::p_create_vector, Format::PSEUDO, instr->dest.dest.ssa.num_components, 1)}; |
| RegClass elem_rc = RegClass::get(RegType::vgpr, instr->dest.dest.ssa.bit_size / 8u); |
| for (unsigned i = 0; i < num; ++i) { |
| if (elems[i].type() == RegType::sgpr && elem_rc.is_subdword()) |
| elems[i] = emit_extract_vector(ctx, elems[i], 0, elem_rc); |
| vec->operands[i] = Operand{elems[i]}; |
| } |
| vec->definitions[0] = Definition(dst); |
| ctx->block->instructions.emplace_back(std::move(vec)); |
| ctx->allocated_vec.emplace(dst.id(), elems); |
| } else { |
| bool use_s_pack = ctx->program->gfx_level >= GFX9; |
| Temp mask = bld.copy(bld.def(s1), Operand::c32((1u << instr->dest.dest.ssa.bit_size) - 1)); |
| |
| std::array<Temp, NIR_MAX_VEC_COMPONENTS> packed; |
| uint32_t const_vals[NIR_MAX_VEC_COMPONENTS] = {}; |
| for (unsigned i = 0; i < num; i++) { |
| unsigned packed_size = use_s_pack ? 16 : 32; |
| unsigned idx = i * instr->dest.dest.ssa.bit_size / packed_size; |
| unsigned offset = i * instr->dest.dest.ssa.bit_size % packed_size; |
| if (nir_src_is_const(instr->src[i].src)) { |
| const_vals[idx] |= nir_src_as_uint(instr->src[i].src) << offset; |
| continue; |
| } |
| if (nir_src_is_undef(instr->src[i].src)) |
| continue; |
| |
| if (offset != packed_size - instr->dest.dest.ssa.bit_size) |
| elems[i] = |
| bld.sop2(aco_opcode::s_and_b32, bld.def(s1), bld.def(s1, scc), elems[i], mask); |
| |
| if (offset) |
| elems[i] = bld.sop2(aco_opcode::s_lshl_b32, bld.def(s1), bld.def(s1, scc), elems[i], |
| Operand::c32(offset)); |
| |
| if (packed[idx].id()) |
| packed[idx] = bld.sop2(aco_opcode::s_or_b32, bld.def(s1), bld.def(s1, scc), elems[i], |
| packed[idx]); |
| else |
| packed[idx] = elems[i]; |
| } |
| |
| if (use_s_pack) { |
| for (unsigned i = 0; i < dst.size(); i++) { |
| bool same = !!packed[i * 2].id() == !!packed[i * 2 + 1].id(); |
| |
| if (packed[i * 2].id() && packed[i * 2 + 1].id()) |
| packed[i] = bld.sop2(aco_opcode::s_pack_ll_b32_b16, bld.def(s1), packed[i * 2], |
| packed[i * 2 + 1]); |
| else if (packed[i * 2 + 1].id()) |
| packed[i] = bld.sop2(aco_opcode::s_pack_ll_b32_b16, bld.def(s1), |
| Operand::c32(const_vals[i * 2]), packed[i * 2 + 1]); |
| else if (packed[i * 2].id()) |
| packed[i] = bld.sop2(aco_opcode::s_pack_ll_b32_b16, bld.def(s1), packed[i * 2], |
| Operand::c32(const_vals[i * 2 + 1])); |
| |
| if (same) |
| const_vals[i] = const_vals[i * 2] | (const_vals[i * 2 + 1] << 16); |
| else |
| const_vals[i] = 0; |
| } |
| } |
| |
| for (unsigned i = 0; i < dst.size(); i++) { |
| if (const_vals[i] && packed[i].id()) |
| packed[i] = bld.sop2(aco_opcode::s_or_b32, bld.def(s1), bld.def(s1, scc), |
| Operand::c32(const_vals[i]), packed[i]); |
| else if (!packed[i].id()) |
| packed[i] = bld.copy(bld.def(s1), Operand::c32(const_vals[i])); |
| } |
| |
| if (dst.size() == 1) |
| bld.copy(Definition(dst), packed[0]); |
| else if (dst.size() == 2) |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), packed[0], packed[1]); |
| else |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), packed[0], packed[1], |
| packed[2]); |
| } |
| break; |
| } |
| case nir_op_mov: { |
| Temp src = get_alu_src(ctx, instr->src[0]); |
| if (src.type() == RegType::vgpr && dst.type() == RegType::sgpr) { |
| /* use size() instead of bytes() for 8/16-bit */ |
| assert(src.size() == dst.size() && "wrong src or dst register class for nir_op_mov"); |
| bld.pseudo(aco_opcode::p_as_uniform, Definition(dst), src); |
| } else { |
| assert(src.bytes() == dst.bytes() && "wrong src or dst register class for nir_op_mov"); |
| bld.copy(Definition(dst), src); |
| } |
| break; |
| } |
| case nir_op_inot: { |
| Temp src = get_alu_src(ctx, instr->src[0]); |
| if (dst.regClass() == v1 || dst.regClass() == v2b || dst.regClass() == v1b) { |
| emit_vop1_instruction(ctx, instr, aco_opcode::v_not_b32, dst); |
| } else if (dst.regClass() == v2) { |
| Temp lo = bld.tmp(v1), hi = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(lo), Definition(hi), src); |
| lo = bld.vop1(aco_opcode::v_not_b32, bld.def(v1), lo); |
| hi = bld.vop1(aco_opcode::v_not_b32, bld.def(v1), hi); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), lo, hi); |
| } else if (dst.type() == RegType::sgpr) { |
| aco_opcode opcode = dst.size() == 1 ? aco_opcode::s_not_b32 : aco_opcode::s_not_b64; |
| bld.sop1(opcode, Definition(dst), bld.def(s1, scc), src); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_iabs: { |
| if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| Temp src = get_alu_src_vop3p(ctx, instr->src[0]); |
| |
| unsigned opsel_lo = (instr->src[0].swizzle[0] & 1) << 1; |
| unsigned opsel_hi = ((instr->src[0].swizzle[1] & 1) << 1) | 1; |
| |
| Temp sub = bld.vop3p(aco_opcode::v_pk_sub_u16, Definition(bld.tmp(v1)), Operand::zero(), |
| src, opsel_lo, opsel_hi); |
| bld.vop3p(aco_opcode::v_pk_max_i16, Definition(dst), sub, src, opsel_lo, opsel_hi); |
| break; |
| } |
| Temp src = get_alu_src(ctx, instr->src[0]); |
| if (dst.regClass() == s1) { |
| bld.sop1(aco_opcode::s_abs_i32, Definition(dst), bld.def(s1, scc), src); |
| } else if (dst.regClass() == v1) { |
| bld.vop2(aco_opcode::v_max_i32, Definition(dst), src, |
| bld.vsub32(bld.def(v1), Operand::zero(), src)); |
| } else if (dst.regClass() == v2b && ctx->program->gfx_level >= GFX10) { |
| bld.vop3( |
| aco_opcode::v_max_i16_e64, Definition(dst), src, |
| bld.vop3(aco_opcode::v_sub_u16_e64, Definition(bld.tmp(v2b)), Operand::zero(2), src)); |
| } else if (dst.regClass() == v2b) { |
| src = as_vgpr(ctx, src); |
| bld.vop2(aco_opcode::v_max_i16, Definition(dst), src, |
| bld.vop2(aco_opcode::v_sub_u16, Definition(bld.tmp(v2b)), Operand::zero(2), src)); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_isign: { |
| Temp src = get_alu_src(ctx, instr->src[0]); |
| if (dst.regClass() == s1) { |
| Temp tmp = |
| bld.sop2(aco_opcode::s_max_i32, bld.def(s1), bld.def(s1, scc), src, Operand::c32(-1)); |
| bld.sop2(aco_opcode::s_min_i32, Definition(dst), bld.def(s1, scc), tmp, Operand::c32(1u)); |
| } else if (dst.regClass() == s2) { |
| Temp neg = |
| bld.sop2(aco_opcode::s_ashr_i64, bld.def(s2), bld.def(s1, scc), src, Operand::c32(63u)); |
| Temp neqz; |
| if (ctx->program->gfx_level >= GFX8) |
| neqz = bld.sopc(aco_opcode::s_cmp_lg_u64, bld.def(s1, scc), src, Operand::zero()); |
| else |
| neqz = |
| bld.sop2(aco_opcode::s_or_b64, bld.def(s2), bld.def(s1, scc), src, Operand::zero()) |
| .def(1) |
| .getTemp(); |
| /* SCC gets zero-extended to 64 bit */ |
| bld.sop2(aco_opcode::s_or_b64, Definition(dst), bld.def(s1, scc), neg, bld.scc(neqz)); |
| } else if (dst.regClass() == v1) { |
| bld.vop3(aco_opcode::v_med3_i32, Definition(dst), Operand::c32(-1), src, Operand::c32(1u)); |
| } else if (dst.regClass() == v2b && ctx->program->gfx_level >= GFX9) { |
| bld.vop3(aco_opcode::v_med3_i16, Definition(dst), Operand::c16(-1), src, Operand::c16(1u)); |
| } else if (dst.regClass() == v2b) { |
| src = as_vgpr(ctx, src); |
| bld.vop2(aco_opcode::v_max_i16, Definition(dst), Operand::c16(-1), |
| bld.vop2(aco_opcode::v_min_i16, Definition(bld.tmp(v1)), Operand::c16(1u), src)); |
| } else if (dst.regClass() == v2) { |
| Temp upper = emit_extract_vector(ctx, src, 1, v1); |
| Temp neg = bld.vop2(aco_opcode::v_ashrrev_i32, bld.def(v1), Operand::c32(31u), upper); |
| Temp gtz = bld.vopc(aco_opcode::v_cmp_ge_i64, bld.def(bld.lm), Operand::zero(), src); |
| Temp lower = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), Operand::c32(1u), neg, gtz); |
| upper = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), Operand::zero(), neg, gtz); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), lower, upper); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_imax: { |
| if (dst.regClass() == v2b && ctx->program->gfx_level >= GFX10) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_max_i16_e64, dst); |
| } else if (dst.regClass() == v2b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_max_i16, dst, true); |
| } else if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_max_i16, dst); |
| } else if (dst.regClass() == v1) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_max_i32, dst, true); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_max_i32, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_umax: { |
| if (dst.regClass() == v2b && ctx->program->gfx_level >= GFX10) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_max_u16_e64, dst); |
| } else if (dst.regClass() == v2b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_max_u16, dst, true); |
| } else if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_max_u16, dst); |
| } else if (dst.regClass() == v1) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_max_u32, dst, true); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_max_u32, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_imin: { |
| if (dst.regClass() == v2b && ctx->program->gfx_level >= GFX10) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_min_i16_e64, dst); |
| } else if (dst.regClass() == v2b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_min_i16, dst, true); |
| } else if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_min_i16, dst); |
| } else if (dst.regClass() == v1) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_min_i32, dst, true); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_min_i32, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_umin: { |
| if (dst.regClass() == v2b && ctx->program->gfx_level >= GFX10) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_min_u16_e64, dst); |
| } else if (dst.regClass() == v2b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_min_u16, dst, true); |
| } else if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_min_u16, dst); |
| } else if (dst.regClass() == v1) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_min_u32, dst, true); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_min_u32, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_ior: { |
| if (instr->dest.dest.ssa.bit_size == 1) { |
| emit_boolean_logic(ctx, instr, Builder::s_or, dst); |
| } else if (dst.regClass() == v1 || dst.regClass() == v2b || dst.regClass() == v1b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_or_b32, dst, true); |
| } else if (dst.regClass() == v2) { |
| emit_vop2_instruction_logic64(ctx, instr, aco_opcode::v_or_b32, dst); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_or_b32, dst, true); |
| } else if (dst.regClass() == s2) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_or_b64, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_iand: { |
| if (instr->dest.dest.ssa.bit_size == 1) { |
| emit_boolean_logic(ctx, instr, Builder::s_and, dst); |
| } else if (dst.regClass() == v1 || dst.regClass() == v2b || dst.regClass() == v1b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_and_b32, dst, true); |
| } else if (dst.regClass() == v2) { |
| emit_vop2_instruction_logic64(ctx, instr, aco_opcode::v_and_b32, dst); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_and_b32, dst, true); |
| } else if (dst.regClass() == s2) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_and_b64, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_ixor: { |
| if (instr->dest.dest.ssa.bit_size == 1) { |
| emit_boolean_logic(ctx, instr, Builder::s_xor, dst); |
| } else if (dst.regClass() == v1 || dst.regClass() == v2b || dst.regClass() == v1b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_xor_b32, dst, true); |
| } else if (dst.regClass() == v2) { |
| emit_vop2_instruction_logic64(ctx, instr, aco_opcode::v_xor_b32, dst); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_xor_b32, dst, true); |
| } else if (dst.regClass() == s2) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_xor_b64, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_ushr: { |
| if (dst.regClass() == v2b && ctx->program->gfx_level >= GFX10) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_lshrrev_b16_e64, dst, false, 2, true); |
| } else if (dst.regClass() == v2b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_lshrrev_b16, dst, false, true); |
| } else if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_lshrrev_b16, dst, true); |
| } else if (dst.regClass() == v1) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_lshrrev_b32, dst, false, true); |
| } else if (dst.regClass() == v2 && ctx->program->gfx_level >= GFX8) { |
| bld.vop3(aco_opcode::v_lshrrev_b64, Definition(dst), get_alu_src(ctx, instr->src[1]), |
| get_alu_src(ctx, instr->src[0])); |
| } else if (dst.regClass() == v2) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_lshr_b64, dst); |
| } else if (dst.regClass() == s2) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_lshr_b64, dst, true); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_lshr_b32, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_ishl: { |
| if (dst.regClass() == v2b && ctx->program->gfx_level >= GFX10) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_lshlrev_b16_e64, dst, false, 2, true); |
| } else if (dst.regClass() == v2b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_lshlrev_b16, dst, false, true); |
| } else if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_lshlrev_b16, dst, true); |
| } else if (dst.regClass() == v1) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_lshlrev_b32, dst, false, true, false, |
| false, 2); |
| } else if (dst.regClass() == v2 && ctx->program->gfx_level >= GFX8) { |
| bld.vop3(aco_opcode::v_lshlrev_b64, Definition(dst), get_alu_src(ctx, instr->src[1]), |
| get_alu_src(ctx, instr->src[0])); |
| } else if (dst.regClass() == v2) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_lshl_b64, dst); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_lshl_b32, dst, true, 1); |
| } else if (dst.regClass() == s2) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_lshl_b64, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_ishr: { |
| if (dst.regClass() == v2b && ctx->program->gfx_level >= GFX10) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_ashrrev_i16_e64, dst, false, 2, true); |
| } else if (dst.regClass() == v2b) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_ashrrev_i16, dst, false, true); |
| } else if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_ashrrev_i16, dst, true); |
| } else if (dst.regClass() == v1) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_ashrrev_i32, dst, false, true); |
| } else if (dst.regClass() == v2 && ctx->program->gfx_level >= GFX8) { |
| bld.vop3(aco_opcode::v_ashrrev_i64, Definition(dst), get_alu_src(ctx, instr->src[1]), |
| get_alu_src(ctx, instr->src[0])); |
| } else if (dst.regClass() == v2) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_ashr_i64, dst); |
| } else if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_ashr_i32, dst, true); |
| } else if (dst.regClass() == s2) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_ashr_i64, dst, true); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_find_lsb: { |
| Temp src = get_alu_src(ctx, instr->src[0]); |
| if (src.regClass() == s1) { |
| bld.sop1(aco_opcode::s_ff1_i32_b32, Definition(dst), src); |
| } else if (src.regClass() == v1) { |
| emit_vop1_instruction(ctx, instr, aco_opcode::v_ffbl_b32, dst); |
| } else if (src.regClass() == s2) { |
| bld.sop1(aco_opcode::s_ff1_i32_b64, Definition(dst), src); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_ufind_msb: |
| case nir_op_ifind_msb: { |
| Temp src = get_alu_src(ctx, instr->src[0]); |
| if (src.regClass() == s1 || src.regClass() == s2) { |
| aco_opcode op = src.regClass() == s2 |
| ? (instr->op == nir_op_ufind_msb ? aco_opcode::s_flbit_i32_b64 |
| : aco_opcode::s_flbit_i32_i64) |
| : (instr->op == nir_op_ufind_msb ? aco_opcode::s_flbit_i32_b32 |
| : aco_opcode::s_flbit_i32); |
| Temp msb_rev = bld.sop1(op, bld.def(s1), src); |
| |
| Builder::Result sub = bld.sop2(aco_opcode::s_sub_u32, bld.def(s1), bld.def(s1, scc), |
| Operand::c32(src.size() * 32u - 1u), msb_rev); |
| Temp msb = sub.def(0).getTemp(); |
| Temp carry = sub.def(1).getTemp(); |
| |
| bld.sop2(aco_opcode::s_cselect_b32, Definition(dst), Operand::c32(-1), msb, |
| bld.scc(carry)); |
| } else if (src.regClass() == v1) { |
| aco_opcode op = |
| instr->op == nir_op_ufind_msb ? aco_opcode::v_ffbh_u32 : aco_opcode::v_ffbh_i32; |
| Temp msb_rev = bld.tmp(v1); |
| emit_vop1_instruction(ctx, instr, op, msb_rev); |
| Temp msb = bld.tmp(v1); |
| Temp carry = |
| bld.vsub32(Definition(msb), Operand::c32(31u), Operand(msb_rev), true).def(1).getTemp(); |
| bld.vop2(aco_opcode::v_cndmask_b32, Definition(dst), msb, msb_rev, carry); |
| } else if (src.regClass() == v2) { |
| aco_opcode op = |
| instr->op == nir_op_ufind_msb ? aco_opcode::v_ffbh_u32 : aco_opcode::v_ffbh_i32; |
| |
| Temp lo = bld.tmp(v1), hi = bld.tmp(v1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(lo), Definition(hi), src); |
| |
| lo = uadd32_sat(bld, bld.def(v1), bld.copy(bld.def(s1), Operand::c32(32u)), |
| bld.vop1(op, bld.def(v1), lo)); |
| hi = bld.vop1(op, bld.def(v1), hi); |
| Temp found_hi = bld.vopc(aco_opcode::v_cmp_lg_u32, bld.def(bld.lm), Operand::c32(-1), hi); |
| |
| Temp msb_rev = bld.vop2(aco_opcode::v_cndmask_b32, bld.def(v1), lo, hi, found_hi); |
| |
| Temp msb = bld.tmp(v1); |
| Temp carry = |
| bld.vsub32(Definition(msb), Operand::c32(63u), Operand(msb_rev), true).def(1).getTemp(); |
| bld.vop2(aco_opcode::v_cndmask_b32, Definition(dst), msb, msb_rev, carry); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_ufind_msb_rev: |
| case nir_op_ifind_msb_rev: { |
| Temp src = get_alu_src(ctx, instr->src[0]); |
| if (src.regClass() == s1) { |
| aco_opcode op = instr->op == nir_op_ufind_msb_rev ? aco_opcode::s_flbit_i32_b32 |
| : aco_opcode::s_flbit_i32; |
| bld.sop1(op, Definition(dst), src); |
| } else if (src.regClass() == v1) { |
| aco_opcode op = |
| instr->op == nir_op_ufind_msb_rev ? aco_opcode::v_ffbh_u32 : aco_opcode::v_ffbh_i32; |
| emit_vop1_instruction(ctx, instr, op, dst); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_bitfield_reverse: { |
| if (dst.regClass() == s1) { |
| bld.sop1(aco_opcode::s_brev_b32, Definition(dst), get_alu_src(ctx, instr->src[0])); |
| } else if (dst.regClass() == v1) { |
| bld.vop1(aco_opcode::v_bfrev_b32, Definition(dst), get_alu_src(ctx, instr->src[0])); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_iadd: { |
| if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_add_u32, dst, true); |
| break; |
| } else if (dst.bytes() <= 2 && ctx->program->gfx_level >= GFX10) { |
| emit_vop3a_instruction(ctx, instr, aco_opcode::v_add_u16_e64, dst); |
| break; |
| } else if (dst.bytes() <= 2 && ctx->program->gfx_level >= GFX8) { |
| emit_vop2_instruction(ctx, instr, aco_opcode::v_add_u16, dst, true); |
| break; |
| } else if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_add_u16, dst); |
| break; |
| } |
| |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| if (dst.type() == RegType::vgpr && dst.bytes() <= 4) { |
| bld.vadd32(Definition(dst), Operand(src0), Operand(src1)); |
| break; |
| } |
| |
| assert(src0.size() == 2 && src1.size() == 2); |
| Temp src00 = bld.tmp(src0.type(), 1); |
| Temp src01 = bld.tmp(dst.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src00), Definition(src01), src0); |
| Temp src10 = bld.tmp(src1.type(), 1); |
| Temp src11 = bld.tmp(dst.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src10), Definition(src11), src1); |
| |
| if (dst.regClass() == s2) { |
| Temp carry = bld.tmp(s1); |
| Temp dst0 = |
| bld.sop2(aco_opcode::s_add_u32, bld.def(s1), bld.scc(Definition(carry)), src00, src10); |
| Temp dst1 = bld.sop2(aco_opcode::s_addc_u32, bld.def(s1), bld.def(s1, scc), src01, src11, |
| bld.scc(carry)); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), dst0, dst1); |
| } else if (dst.regClass() == v2) { |
| Temp dst0 = bld.tmp(v1); |
| Temp carry = bld.vadd32(Definition(dst0), src00, src10, true).def(1).getTemp(); |
| Temp dst1 = bld.vadd32(bld.def(v1), src01, src11, false, carry); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), dst0, dst1); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_uadd_sat: { |
| if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| Instruction* add_instr = |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_add_u16, dst); |
| add_instr->vop3p().clamp = 1; |
| break; |
| } |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| if (dst.regClass() == s1) { |
| Temp tmp = bld.tmp(s1), carry = bld.tmp(s1); |
| bld.sop2(aco_opcode::s_add_u32, Definition(tmp), bld.scc(Definition(carry)), src0, src1); |
| bld.sop2(aco_opcode::s_cselect_b32, Definition(dst), Operand::c32(-1), tmp, |
| bld.scc(carry)); |
| break; |
| } else if (dst.regClass() == v2b) { |
| Instruction* add_instr; |
| if (ctx->program->gfx_level >= GFX10) { |
| add_instr = bld.vop3(aco_opcode::v_add_u16_e64, Definition(dst), src0, src1).instr; |
| } else { |
| if (src1.type() == RegType::sgpr) |
| std::swap(src0, src1); |
| add_instr = |
| bld.vop2_e64(aco_opcode::v_add_u16, Definition(dst), src0, as_vgpr(ctx, src1)).instr; |
| } |
| add_instr->vop3().clamp = 1; |
| break; |
| } else if (dst.regClass() == v1) { |
| uadd32_sat(bld, Definition(dst), src0, src1); |
| break; |
| } |
| |
| assert(src0.size() == 2 && src1.size() == 2); |
| |
| Temp src00 = bld.tmp(src0.type(), 1); |
| Temp src01 = bld.tmp(src0.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src00), Definition(src01), src0); |
| Temp src10 = bld.tmp(src1.type(), 1); |
| Temp src11 = bld.tmp(src1.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src10), Definition(src11), src1); |
| |
| if (dst.regClass() == s2) { |
| Temp carry0 = bld.tmp(s1); |
| Temp carry1 = bld.tmp(s1); |
| |
| Temp no_sat0 = |
| bld.sop2(aco_opcode::s_add_u32, bld.def(s1), bld.scc(Definition(carry0)), src00, src10); |
| Temp no_sat1 = bld.sop2(aco_opcode::s_addc_u32, bld.def(s1), bld.scc(Definition(carry1)), |
| src01, src11, bld.scc(carry0)); |
| |
| Temp no_sat = bld.pseudo(aco_opcode::p_create_vector, bld.def(s2), no_sat0, no_sat1); |
| |
| bld.sop2(aco_opcode::s_cselect_b64, Definition(dst), Operand::c64(-1), no_sat, |
| bld.scc(carry1)); |
| } else if (dst.regClass() == v2) { |
| Temp no_sat0 = bld.tmp(v1); |
| Temp dst0 = bld.tmp(v1); |
| Temp dst1 = bld.tmp(v1); |
| |
| Temp carry0 = bld.vadd32(Definition(no_sat0), src00, src10, true).def(1).getTemp(); |
| Temp carry1; |
| |
| if (ctx->program->gfx_level >= GFX8) { |
| carry1 = bld.tmp(bld.lm); |
| bld.vop2_e64(aco_opcode::v_addc_co_u32, Definition(dst1), Definition(carry1), |
| as_vgpr(ctx, src01), as_vgpr(ctx, src11), carry0) |
| .instr->vop3() |
| .clamp = 1; |
| } else { |
| Temp no_sat1 = bld.tmp(v1); |
| carry1 = bld.vadd32(Definition(no_sat1), src01, src11, true, carry0).def(1).getTemp(); |
| bld.vop2_e64(aco_opcode::v_cndmask_b32, Definition(dst1), no_sat1, Operand::c32(-1), |
| carry1); |
| } |
| |
| bld.vop2_e64(aco_opcode::v_cndmask_b32, Definition(dst0), no_sat0, Operand::c32(-1), |
| carry1); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), dst0, dst1); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_iadd_sat: { |
| if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| Instruction* add_instr = |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_add_i16, dst); |
| add_instr->vop3p().clamp = 1; |
| break; |
| } |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| if (dst.regClass() == s1) { |
| Temp cond = bld.sopc(aco_opcode::s_cmp_lt_i32, bld.def(s1, scc), src1, Operand::zero()); |
| Temp bound = bld.sop2(aco_opcode::s_add_u32, bld.def(s1), bld.scc(bld.def(s1, scc)), |
| Operand::c32(INT32_MAX), cond); |
| Temp overflow = bld.tmp(s1); |
| Temp add = |
| bld.sop2(aco_opcode::s_add_i32, bld.def(s1), bld.scc(Definition(overflow)), src0, src1); |
| bld.sop2(aco_opcode::s_cselect_b32, Definition(dst), bound, add, bld.scc(overflow)); |
| break; |
| } |
| |
| src1 = as_vgpr(ctx, src1); |
| |
| if (dst.regClass() == v2b) { |
| Instruction* add_instr = |
| bld.vop3(aco_opcode::v_add_i16, Definition(dst), src0, src1).instr; |
| add_instr->vop3().clamp = 1; |
| } else if (dst.regClass() == v1) { |
| Instruction* add_instr = |
| bld.vop3(aco_opcode::v_add_i32, Definition(dst), src0, src1).instr; |
| add_instr->vop3().clamp = 1; |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_uadd_carry: { |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| if (dst.regClass() == s1) { |
| bld.sop2(aco_opcode::s_add_u32, bld.def(s1), bld.scc(Definition(dst)), src0, src1); |
| break; |
| } |
| if (dst.regClass() == v1) { |
| Temp carry = bld.vadd32(bld.def(v1), src0, src1, true).def(1).getTemp(); |
| bld.vop2_e64(aco_opcode::v_cndmask_b32, Definition(dst), Operand::zero(), Operand::c32(1u), |
| carry); |
| break; |
| } |
| |
| Temp src00 = bld.tmp(src0.type(), 1); |
| Temp src01 = bld.tmp(dst.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src00), Definition(src01), src0); |
| Temp src10 = bld.tmp(src1.type(), 1); |
| Temp src11 = bld.tmp(dst.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src10), Definition(src11), src1); |
| if (dst.regClass() == s2) { |
| Temp carry = bld.tmp(s1); |
| bld.sop2(aco_opcode::s_add_u32, bld.def(s1), bld.scc(Definition(carry)), src00, src10); |
| carry = bld.sop2(aco_opcode::s_addc_u32, bld.def(s1), bld.scc(bld.def(s1)), src01, src11, |
| bld.scc(carry)) |
| .def(1) |
| .getTemp(); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), carry, Operand::zero()); |
| } else if (dst.regClass() == v2) { |
| Temp carry = bld.vadd32(bld.def(v1), src00, src10, true).def(1).getTemp(); |
| carry = bld.vadd32(bld.def(v1), src01, src11, true, carry).def(1).getTemp(); |
| carry = bld.vop2_e64(aco_opcode::v_cndmask_b32, bld.def(v1), Operand::zero(), |
| Operand::c32(1u), carry); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), carry, Operand::zero()); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_isub: { |
| if (dst.regClass() == s1) { |
| emit_sop2_instruction(ctx, instr, aco_opcode::s_sub_i32, dst, true); |
| break; |
| } else if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| emit_vop3p_instruction(ctx, instr, aco_opcode::v_pk_sub_u16, dst); |
| break; |
| } |
| |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| if (dst.regClass() == v1) { |
| bld.vsub32(Definition(dst), src0, src1); |
| break; |
| } else if (dst.bytes() <= 2) { |
| if (ctx->program->gfx_level >= GFX10) |
| bld.vop3(aco_opcode::v_sub_u16_e64, Definition(dst), src0, src1); |
| else if (src1.type() == RegType::sgpr) |
| bld.vop2(aco_opcode::v_subrev_u16, Definition(dst), src1, as_vgpr(ctx, src0)); |
| else if (ctx->program->gfx_level >= GFX8) |
| bld.vop2(aco_opcode::v_sub_u16, Definition(dst), src0, as_vgpr(ctx, src1)); |
| else |
| bld.vsub32(Definition(dst), src0, src1); |
| break; |
| } |
| |
| Temp src00 = bld.tmp(src0.type(), 1); |
| Temp src01 = bld.tmp(dst.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src00), Definition(src01), src0); |
| Temp src10 = bld.tmp(src1.type(), 1); |
| Temp src11 = bld.tmp(dst.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src10), Definition(src11), src1); |
| if (dst.regClass() == s2) { |
| Temp borrow = bld.tmp(s1); |
| Temp dst0 = |
| bld.sop2(aco_opcode::s_sub_u32, bld.def(s1), bld.scc(Definition(borrow)), src00, src10); |
| Temp dst1 = bld.sop2(aco_opcode::s_subb_u32, bld.def(s1), bld.def(s1, scc), src01, src11, |
| bld.scc(borrow)); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), dst0, dst1); |
| } else if (dst.regClass() == v2) { |
| Temp lower = bld.tmp(v1); |
| Temp borrow = bld.vsub32(Definition(lower), src00, src10, true).def(1).getTemp(); |
| Temp upper = bld.vsub32(bld.def(v1), src01, src11, false, borrow); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), lower, upper); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_usub_borrow: { |
| Temp src0 = get_alu_src(ctx, instr->src[0]); |
| Temp src1 = get_alu_src(ctx, instr->src[1]); |
| if (dst.regClass() == s1) { |
| bld.sop2(aco_opcode::s_sub_u32, bld.def(s1), bld.scc(Definition(dst)), src0, src1); |
| break; |
| } else if (dst.regClass() == v1) { |
| Temp borrow = bld.vsub32(bld.def(v1), src0, src1, true).def(1).getTemp(); |
| bld.vop2_e64(aco_opcode::v_cndmask_b32, Definition(dst), Operand::zero(), Operand::c32(1u), |
| borrow); |
| break; |
| } |
| |
| Temp src00 = bld.tmp(src0.type(), 1); |
| Temp src01 = bld.tmp(dst.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src00), Definition(src01), src0); |
| Temp src10 = bld.tmp(src1.type(), 1); |
| Temp src11 = bld.tmp(dst.type(), 1); |
| bld.pseudo(aco_opcode::p_split_vector, Definition(src10), Definition(src11), src1); |
| if (dst.regClass() == s2) { |
| Temp borrow = bld.tmp(s1); |
| bld.sop2(aco_opcode::s_sub_u32, bld.def(s1), bld.scc(Definition(borrow)), src00, src10); |
| borrow = bld.sop2(aco_opcode::s_subb_u32, bld.def(s1), bld.scc(bld.def(s1)), src01, src11, |
| bld.scc(borrow)) |
| .def(1) |
| .getTemp(); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), borrow, Operand::zero()); |
| } else if (dst.regClass() == v2) { |
| Temp borrow = bld.vsub32(bld.def(v1), src00, src10, true).def(1).getTemp(); |
| borrow = bld.vsub32(bld.def(v1), src01, src11, true, Operand(borrow)).def(1).getTemp(); |
| borrow = bld.vop2_e64(aco_opcode::v_cndmask_b32, bld.def(v1), Operand::zero(), |
| Operand::c32(1u), borrow); |
| bld.pseudo(aco_opcode::p_create_vector, Definition(dst), borrow, Operand::zero()); |
| } else { |
| isel_err(&instr->instr, "Unimplemented NIR instr bit size"); |
| } |
| break; |
| } |
| case nir_op_usub_sat: { |
| if (dst.regClass() == v1 && instr->dest.dest.ssa.bit_size == 16) { |
| Instruction* sub_instr = |