diff --git a/src/freedreno/ir3/ir3_shared_ra.c b/src/freedreno/ir3/ir3_shared_ra.c index 17d92bbbb32..9515eb250f0 100644 --- a/src/freedreno/ir3/ir3_shared_ra.c +++ b/src/freedreno/ir3/ir3_shared_ra.c @@ -667,9 +667,46 @@ free_space(struct ra_ctx *ctx, physreg_t start, unsigned size) } } +static physreg_t +try_allocate_src_subreg(struct ra_ctx *ctx, struct ir3_register *reg, + enum ir3_subreg_move subreg_move) +{ + assert(subreg_move != IR3_SUBREG_MOVE_NONE); + + /* Subreg moves always write a half register. */ + assert(reg_elem_size(reg) == 1); + + struct ir3_register *src = reg->instr->srcs[0]; + if (!ra_reg_is_src(src) || !(src->flags & IR3_REG_SHARED)) + return ~0; + + unsigned offset = subreg_move == IR3_SUBREG_MOVE_LOWER ? 0 : 1; + struct ra_interval *src_interval = ra_interval_get(ctx, src->def); + physreg_t src_physreg = ra_interval_get_physreg(src_interval) + offset; + unsigned file_size = reg_file_size(reg); + unsigned size = reg_size(reg); + + if (src_physreg + size <= file_size && + get_reg_specified(ctx, reg, src_physreg)) { + return src_physreg; + } + + return ~0; +} + static physreg_t get_reg(struct ra_ctx *ctx, struct ir3_register *reg, bool src) { + /* For subreg moves (see ir3_is_subreg_move), try to allocate half of their + * full src for their dst. If this succeeds, the instruction can be removed. + */ + enum ir3_subreg_move subreg_move = ir3_is_subreg_move(reg->instr); + if (subreg_move != IR3_SUBREG_MOVE_NONE) { + physreg_t src_reg = try_allocate_src_subreg(ctx, reg, subreg_move); + if (src_reg != (physreg_t)~0) + return src_reg; + } + if (reg->merge_set && reg->merge_set->preferred_reg != (physreg_t)~0) { physreg_t preferred_reg = reg->merge_set->preferred_reg + reg->merge_set_offset;