| /* Description of target passes for AVR 8-bit microcontrollers. |
| Copyright (C) 2016-2025 Free Software Foundation, Inc. */ |
| |
| /* This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| /* A post reload optimization pass that fuses PLUS insns with CONST_INT |
| addend with a load or store insn to get POST_INC or PRE_DEC addressing. |
| It can also fuse two PLUSes to a single one, which may occur due to |
| splits from `avr_split_fake_addressing_move'. We do this in an own |
| pass because it can find more cases than peephole2, for example when |
| there are unrelated insns between the interesting ones. */ |
| |
| INSERT_PASS_BEFORE (pass_peephole2, 1, avr_pass_fuse_add); |
| |
| /* There are cases where avr-fuse-add doesn't find POST_INC cases because |
| the RTL code at that time is too long-winded, and moves registers back and |
| forth (which seems to be the same reason for why pass auto_inc_dec cannot |
| find POST_INC, either). Some of that long-windedness is cleaned up very |
| late in pass cprop_hardreg, which opens up new opportunities to find post |
| increments. An example is the following function from AVR-LibC's qsort: |
| |
| void swapfunc (char *a, char *b, int n) |
| { |
| do |
| { |
| char tmp = *a; |
| *a++ = *b; |
| *b++ = tmp; |
| } while (--n > 0); |
| } |
| |
| Hence, run avr-fuse-add twice; the second time after cprop_hardreg. */ |
| |
| INSERT_PASS_AFTER (pass_cprop_hardreg, 1, avr_pass_fuse_add); |
| |
| /* An analysis pass that runs prior to prologue / epilogue generation. |
| Computes cfun->machine->gasisr.maybe which is used in prologue and |
| epilogue generation provided -mgas-isr-prologues is on. */ |
| |
| INSERT_PASS_BEFORE (pass_thread_prologue_and_epilogue, 1, avr_pass_pre_proep); |
| |
| /* This avr-specific pass (re)computes insn notes, in particular REG_DEAD |
| notes which are used by `avr.cc::reg_unused_after' and branch offset |
| computations. These notes must be correct, i.e. there must be no |
| dangling REG_DEAD notes; otherwise wrong code might result, cf. PR64331. |
| |
| DF needs (correct) CFG, hence right before free_cfg is the last |
| opportunity to rectify notes. */ |
| |
| INSERT_PASS_BEFORE (pass_free_cfg, 1, avr_pass_recompute_notes); |
| |
| /* casesi uses a SImode switch index which is quite costly as most code will |
| work on HImode or QImode. The following pass runs right after .expand and |
| tries to fix such situations by operating on the original mode. This |
| reduces code size and register pressure. |
| |
| The assertion is that the code generated by casesi is unaltered and |
| a sign-extend or zero-extend from QImode or HImode precedes the casesi |
| insns without any insns in between. */ |
| |
| INSERT_PASS_AFTER (pass_expand, 1, avr_pass_casesi); |
| |
| /* Insn combine may come up with superfluous reg-reg moves, where the combine |
| people say that these are no problem since reg-alloc is supposed to optimize |
| them. The issue is that the lower-subreg pass sitting between combine and |
| reg-alloc may split such moves, coming up with a zoo of subregs which are |
| only handled poorly by the register allocator. */ |
| |
| INSERT_PASS_AFTER (pass_combine, 1, avr_pass_2moves); |
| |
| /* Some combine insns have nonzero_bits() in their condition, though insns |
| should not use such stuff in their condition. Therefore, we split such |
| insn into something without nonzero_bits() in their condition right after |
| insn combine. |
| |
| Since neither split_all_insns() nor split_all_insns_noflow() work at that |
| point (presumably since there are splits involving branches), we split |
| respective insns (and only such insns) by hand. Respective insns are |
| tagged with insn attribute nzb = "yes" so that they are easy to spot. */ |
| |
| INSERT_PASS_AFTER (pass_combine, 1, avr_pass_split_nzb); |
| |
| /* If-else decision trees generated for switch / case may produce sequences |
| like |
| |
| SREG = compare (reg, val); |
| if (SREG == 0) goto label1; |
| SREG = compare (reg, 1 + val); |
| if (SREG >= 0) goto label2; |
| |
| which can be optimized to |
| |
| SREG = compare (reg, val); |
| if (SREG == 0) goto label1; |
| if (SREG >= 0) goto label2; |
| |
| The optimal place for such a pass would be directly after expand, but |
| it's not possible for a jump insn to target more than one code label. |
| Hence, run a mini pass right before split2 which introduces REG_CC. */ |
| |
| INSERT_PASS_BEFORE (pass_split_after_reload, 1, avr_pass_ifelse); |
| |
| /* A post reload pass that tracks known values held in registers |
| and performs optimizations based on that knowledge. |
| It also splits non-memory insns that can be represented in |
| terms of byte operations. |
| |
| It runs between the two instances of the RTL peephole pass because |
| - The RTL peepholer may provide a scratch reg for *reload_in<mode>. |
| - The RTL peepholer may optimize insns involving lower registers. */ |
| |
| INSERT_PASS_AFTER (pass_peephole2, 1, avr_pass_fuse_move); |
| |
| /* Run an instance of post-reload split prior to avr-fuse-move. |
| Purpose is to split the `3op' alternative (which allows 3 operands) |
| of shift insns into a 3-operand shift with a byte offset, and |
| a 2-operand residual shift. This additional split pass runs after |
| the 1st RTL peephole pass but prior to avr-fuse-move. |
| The respective define_split patterns have a `n_avr_fuse_add_executed' |
| condition (amongst others) so that split passes that run before |
| the 1st RTL peephole pass won't split them. Shifts with a constant |
| offset that is a multiple of 8 are split by avr-fuse-move. */ |
| |
| INSERT_PASS_AFTER (pass_peephole2, 1, avr_pass_split_after_peephole2); |