I've been working on some patches to make insn_rtx_cost take account of the cost of SET_DESTs as well as SET_SRCs. But I'm slowly beginning to realise that I don't understand what rtx costs are supposed to represent.
AIUI the rules have historically been: 1) Registers have zero cost. 2) Constants have a cost relative to that of registers. By extension, constants have zero cost if they are as cheap as a register. 3) With an outer code of SET, actual operations have the cost of the associated instruction. E.g. the cost of a PLUS is the cost of an addition instruction. 4) With other outer codes, actual operations have the cost of the combined instruction, if available, or the cost of a separate instruction otherwise. E.g. the cost of a NEG inside an AND might be zero on targets that support BIC-like instructions, and COSTS_N_INSNS (1) on most others. Memories seem a bit more vague. I think most ports handle them like (2), but I'm not sure whether all RISCy ports do. I'd wrongly assumed that rtx costs were only used for rvalues. However, several places actually take the rtx cost of a SET. Specifically: * auto-inc-dec.c (attempt_change) * cfgloopanal.c (seq_cost) * loop-invariant.c (create_new_invariant) * postreload.c (move2add_use_add2_insn, move2add_use_add3_insn) (reload_cse_move2add) (What confused me is that these places still pass "SET" as the outer code. "INSN" would be more accurate.) By default, the cost of a SET is: COSTS_N_INSNS (1) + rtx_cost (SET_DEST (x), SET, speed) + rtx_cost (SET_SRC (x), SET, speed) But it seems like there's some double-counting for complex SET_SRCs here. The cost of the instruction is already included in the SET_SRC, so adding COSTS_N_INSNS (1) is overkill. COSTS_N_INSNS (1) does seem necessary for register, constant, subreg and (possibly) memory SET_SRCs though. Some targets' cost hooks do handle SETs. Others have a default case that assumes any unhandled operation is expensive, so even register sets end up getting this treatment. (I notice ARM is inconsistent: Thumb-1 handles SETs explicitly, but the default ARM and Thumb-2 costs assume that SET itself has a cost of COSTS_N_INSNS (4).) insn_rtx_cost instead uses: cost = set_src_cost (SET_SRC (set), speed); return cost > 0 ? cost : COSTS_N_INSNS (1); This ignores SET_DEST (the problem I'm trying to fix). It also means that constants that are slightly more expensive than a register -- somewhere in the range [0, COSTS_N_INSNS (1)] -- end up seeming cheaper than registers. So it doesn't look like we're consistent here, and that some rejigging might be needed. As others have said, it would be nice if costs could be extracted from the .md file, but that's more work than I have time for now. Is it worth changing the costs anyway? One approach I'm trying is to make sure that every target that doesn't explicitly handle SET does nothing with it. (Targets that do handle SET remain unchanged.) Then, if we see a SET whose SET_SRC is a register, constant, memory or subreg, we give it cost: COSTS_N_INSNS (1) + rtx_cost (SET_DEST (x), SET, speed) + rtx_cost (SET_SRC (x), SET, speed) as now. In other cases we give it a cost of: rtx_cost (SET_DEST (x), SET, speed) + rtx_cost (SET_SRC (x), SET, speed) But that hardly seems clean either. Perhaps we should instead make the SET_SRC always include the cost of the SET, even for registers, constants and the like. Thoughts? (The patches I've been working on also tell rtx_costs -- and the target hook -- whether the operand is an lvalue. That means that stores can have a different cost from loads.) Richard