Ian Lance Taylor schrieb:
> Georg Lay <a...@gjlay.de> writes:
> 
>> Regs that are "naturally" dead because the function ends are not marked as 
>> dead,
>> and therefore some optimization opportunities pass by unnoticed, e.g. 
>> together
>> with recog.c::peep2_reg_dead_p() et. al.
> 
> I don't understand what you mean.  All registers other than the return
> register, stack pointer, and frame pointer die at the end of the
> function, and they should be marked accordingly.  Can you give an
> example?

Unfortunately, not all dead regs are marked as dead. The example is from a
private port for the C-Source

int f (int);

int and (int x)
{
    return f (x & 0x00018000);
}

After .ira, RTL looks like this:


(insn 9 8 21 2 peep2.c:5 (set (reg:SI 15 d15)
        (and:SI (reg:SI 4 d4 [ x ])
            (const_int 98304 [0x18000]))) 433 {*and3_zeroes-2.insert.ic} (nil))

(insn 21 9 10 2 peep2.c:5 (set (reg:SI 4 d4)
        (reg:SI 15 d15)) 2 {*movsi_insn} (nil))

(call_insn/j 10 21 11 2 peep2.c:5 (parallel [
            (set (reg:SI 2 d2)
                (call (mem:HI (symbol_ref:SI ("f") [flags 0x41]  <function_decl
0xb76b3280 f>) [0 S2 A16])
                    (const_int 0 [0x0])))
            (use (const_int 1 [0x1]))
        ]) 92 {call_value_insn} (nil)
    (expr_list:REG_DEP_TRUE (use (reg:SI 4 d4))
        (nil)))

;; End of basic block 2 -> ( 1)
;; lr  out       2 [d2] 26 [SP] 27 [a11]
;; live  out     2 [d2] 26 [SP] 27 [a11]
;; Succ edge  EXIT [100.0%]  (ab,sibcall)

(barrier 11 10 20)

The first insn, AND, is an early-clobber of output operand, D15. Functions
get/receive their first arg in D4, so reload generates a move.

Then the first insn gets split after reload and before peephole2:

(insn 22 8 23 2 peep2.c:5 (set (reg:SI 15 d15)
        (and:SI (reg:SI 4 d4 [ x ])
            (const_int -98305 [0xfffe7fff]))) 143 {*and3_zeroes.insert.{SI}.ic}
(nil))

(insn 23 22 21 2 peep2.c:5 (set (reg:SI 15 d15)
        (xor:SI (reg:SI 15 d15)
            (reg:SI 4 d4 [ x ]))) 39 {*xorsi3} (nil))

(insn 21 23 10 2 peep2.c:5 (set (reg:SI 4 d4)
        (reg:SI 15 d15)) 2 {*movsi_insn} (nil))

(call_insn/j 10 21 11 2 peep2.c:5 (parallel [
            (set (reg:SI 2 d2)
                (call (mem:HI (symbol_ref:SI ("f") [flags 0x41]  <function_decl
0xb76b3280 f>) [0 S2 A16])
                    (const_int 0 [0x0])))
            (use (const_int 1 [0x1]))
        ]) 92 {call_value_insn} (nil)
    (expr_list:REG_DEP_TRUE (use (reg:SI 4 d4))
        (nil)))
;; End of basic block 2 -> ( 1)
;; lr  out       2 [d2] 26 [SP] 27 [a11]
;; live  out     2 [d2] 26 [SP] 27 [a11]
;; Succ edge  EXIT [100.0%]  (ab,sibcall)

(barrier 11 10 20)


The second insn (XOR) and the third insn (SET) could be combined into one insn
because the xor-insn can handle three different regs. This is the peep2:

(define_peephole2
  [(set (match_operand:SI 0 "register_operand" "")
        (match_operator:SI 4 "tric_s10_operator"
                           [(match_operand:SI 1 "register_operand"  "")
                            (match_operand:SI 2 "reg_or_s10_operand" "")]))
   (set (match_operand:SI 3 "register_operand" "")
        (match_dup 0))]
  "peep2_reg_dead_p (2, operands[0])"
  ...

with XOR an element of "tric_s10_operator"
This peep2 fails because op0, in this case D15, is not marked as dead resp.
peep2_reg_dead_p does not report it as dead. D15 is a call-saved register.
The architecture automatically saves regs in CALL and restores them in RETURN,
so most functions have no prologue (except in cases SP has to be changed) and no
epilogue except a RETURN. D15 is advantageous in many instructions even though
it is call-saved.

I already tried to fix this by introducing a different return-pattern, i.e. a
PARALLEL of return and bunch of clobbers of unused regs. That fixes this problem
but has many other disadvantages compared to a simple return.

Georg Lay

Reply via email to