This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: porting GCC to a micro with a very limited addressing mode --- enforcing a 'register indirect memory operand' through a constraint in md
- From: Sergio Ruocco <sergio dot ruocco at gmail dot com>
- To: Michael Hope <michaelh at juju dot net dot nz>
- Cc: GCC <gcc at gcc dot gnu dot org>
- Date: Wed, 10 Feb 2010 10:45:24 +0100
- Subject: Re: porting GCC to a micro with a very limited addressing mode --- enforcing a 'register indirect memory operand' through a constraint in md
- References: <4B5D8FC1.60205@gmail.com> <20100125152424.GA6408@iram.es> <4B5DE110.7050205@gmail.com> <31d002d41001251131q12a592bcxdeaeb60409a3f4b7@mail.gmail.com> <4B618D65.5050101@gmail.com> <4B71BF38.5030705@gmail.com> <31d002d41002100022i55bb5e5dy823f395b8571900d@mail.gmail.com>
- Reply-to: sergio dot ruocco at gmail dot com
Thanks Micheal for your code, I will try it... however, I think the
problem that crashes my compiler may not lie in my LEGITIMATE/LEGITIMIZE
ADDRESS macros, which are equivalent to many others (yours included),
but in the GCC which - in some corner cases - does not like being told
that an register+offset is an invalid address.
While investigating this hypothesis, I found this tutorial
http://spindazzle.org/ggx/
which discusses a GCC port with the same constraints I am battling with,
but solved in a different way.
In this port, the register indirect addressing is enforced through a
custom constraint in the MD (details below).
What do you think of this approach?
Sergio
(from the patch to the MD)
-------------------------------------------------------------------------
;; Constraints
;; -------------------------------------------------------------------------
(define_constraint "W"
"A register indirect memory operand."
(and (match_code "mem")
(match_test "REG_P (XEXP (op, 0))
&& REGNO_OK_FOR_BASE_P (REGNO (XEXP (op, 0)))")))
which is then specified by the define_insn
(define_insn "*movsi"
[(set (match_operand:SI 0 "general_operand" "=r,r,W,m,r,r")
(match_operand:SI 1 "ggx_general_movsrc_operand" "r,i,r,r,W,m"))]
"register_operand (operands[0], SImode)
|| register_operand (operands[1], SImode)"
"@
mov %0, %1
ldi.l %0, %1
st.l %0, %1
sta.l %0, %1
ld.l %0, %1
lda.l %0, %1")
http://spindazzle.org/ggx/gcc/add-load-store.patch.txt
The GO_IF_LEGITIMATE_ADDRESS() macro is plain and simple:
/* A C compound statement with a conditional `goto LABEL;' executed
if X (an RTX) is a legitimate memory address on the target machine
for a memory operand of mode MODE. */
#define GO_IF_LEGITIMATE_ADDRESS(MODE,X,LABEL) \
do { \
if (REG_P (X) && REGNO_OK_FOR_BASE_P (REGNO (X))) \
goto LABEL; \
if (GET_CODE (X) == SYMBOL_REF \
|| GET_CODE (X) == LABEL_REF \
|| GET_CODE (X) == CONST) \
goto LABEL; \
} while (0)
http://spindazzle.org/ggx/gcc/add-ggx-config.patch.txt
Michael Hope wrote:
> Hi Sergio. Here's the interesting parts from my port. The code's a
> bit funny looking as I've edited it for this post.
>
> In <port>.h:
>
> #define BASE_REG_CLASS ADDR_REGS
> #define INDEX_REG_CLASS NO_REGS
>
> #ifdef REG_OK_STRICT
> # define <PORT>_REG_OK_STRICT 1
> #else
> # define <PORT>_REG_OK_STRICT 0
> #endif
>
> #define REGNO_OK_FOR_BASE_P(r) <port>_regno_ok_for_base_p(r,
> <PORT>_REG_OK_STRICT)
> #define REGNO_OK_FOR_INDEX_P(r) 0
>
> In <port>.c:
>
> static bool
> <port>_reg_ok(rtx reg, bool strict)
> {
> int regno = REGNO(reg);
>
> bool is_addr = <port>_is_addr_regno(regno);
> bool ok_strict = is_addr;
> bool special = regno == ARG_POINTER_REGNUM
> || regno == TREG_S
> ;
>
> if (strict)
> {
> return ok_strict || special;
> }
> else
> {
> return ok_strict || special
> || regno >= FIRST_PSEUDO_REGISTER
> ;
> }
> }
>
> bool
> <port>_legitimate_address (enum machine_mode mode ATTRIBUTE_UNUSED,
> rtx x,
> bool strict_checking)
> {
> /* (mem reg) */
> if (REG_P (x)
> && <port>_reg_ok (x, strict_checking)
> )
> {
> return 1;
> }
>
> return 0;
> }
>
> Note that this ISA only has indirect addressing and has no indirect +
> offset or indirect + register modes. GCC
> handles this just fine by splitting up any other type that fails
> legitimate_address into smaller components.
>
> -- Michael
>
> On 10 February 2010 09:02, Sergio Ruocco <sergio.ruocco@gmail.com> wrote:
>> Michael Hope wrote:
>>> Hi Sergio. Any luck so far?
>> Micheal, thanks for your inquiry. I made some progress, in fact.
>>
>> I got the GO_IF_LEGITIMATE_ADDRESS() macro to detect correctly REG+IMM
>> addresses, and then the LEGITIMIZE_ADDRESS() macro to force them to be
>> pre-computed in a register.
>>
>> However, now the compiler freaks out with an ICE.. :-/ I put some
>> details below. Thanks for any clue that you or others can give me.
>>
>> Cheers,
>>
>> Sergio
>>
>> ==========================================================================
>>
>>
>> This is a fragment of my LEGITIMIZE_ADDRESS():
>> -----------------------------------------------------------------
>>
>> rtx
>> legitimize_address(rtx X,rtx OLDX, enum machine_mode MODE)
>> {
>> rtx op1,op2,op,sum;
>> op=NULL;
>> ...
>> if(GET_CODE(X)==PLUS && !no_new_pseudos)
>> {
>> op1=XEXP(X,0);
>> op2=XEXP(X,1);
>> if(GET_CODE(op1) == CONST_INT && (GET_CODE(op2) == REG ||
>> GET_CODE(op2) == SUBREG)) // base displacement
>> {
>> sum = gen_rtx_PLUS (MODE, op1, op2);
>> op = force_reg(MODE, sum);
>> }
>> ...
>> -----------------------------------------------------------------
>>
>>
>> Now when compiling a simple program such as:
>>
>> void foobar(int par1, int par2, int parN)
>> {
>> int a,b;
>> a = 0x1234;
>> b = a;
>> }
>>
>> the instructions (n. 8,12,13) which compute the addresses in registers
>> seem to be generated correctly:
>>
>> -----------------------------------------------------------------
>> ;; Function foobar
>>
>> ;; Register dispositions:
>> 37 in 4 38 in 2 39 in 4 40 in 2 41 in 2
>>
>> ;; Hard regs used: 2 4 30
>>
>> (note 2 0 3 NOTE_INSN_DELETED)
>>
>> (note 3 2 6 0 NOTE_INSN_FUNCTION_BEG)
>>
>> ;; Start of basic block 1, registers live: 1 [A1] 29 [B13] 30 [B14]
>> (note 6 3 8 1 [bb 1] NOTE_INSN_BASIC_BLOCK)
>>
>> (insn 8 6 9 1 (set (reg/f:HI 4 A4 [37])
>> (plus:HI (reg/f:HI 30 B14)
>> (const_int -16 [0xfffffffffffffff0]))) 9 {addhi3} (nil)
>> (nil))
>>
>> (insn 9 8 10 1 (set (reg:HI 2 A2 [38])
>> (const_int 4660 [0x1234])) 5 {*constant_load} (nil)
>> (nil))
>>
>> (insn 10 9 12 1 (set (mem/i:HI (reg/f:HI 4 A4 [37]) [0 a+0 S2 A32])
>> (reg:HI 2 A2 [38])) 7 {*store_word} (nil)
>> (nil))
>>
>> (insn 12 10 13 1 (set (reg/f:HI 4 A4 [39])
>> (plus:HI (reg/f:HI 30 B14)
>> (const_int -14 [0xfffffffffffffff2]))) 9 {addhi3} (nil)
>> (nil))
>>
>> (insn 13 12 14 1 (set (reg/f:HI 2 A2 [40])
>> (plus:HI (reg/f:HI 30 B14)
>> (const_int -16 [0xfffffffffffffff0]))) 9 {addhi3} (nil)
>> (nil))
>>
>> (insn 14 13 15 1 (set (reg:HI 2 A2 [orig:41 a ] [41])
>> (mem/i:HI (reg/f:HI 2 A2 [40]) [0 a+0 S2 A32])) 4 {*load_word} (nil)
>> (nil))
>>
>> (insn 15 14 16 1 (set (mem/i:HI (reg/f:HI 4 A4 [39]) [0 b+0 S2 A16])
>> (reg:HI 2 A2 [orig:41 a ] [41])) 7 {*store_word} (nil)
>> (nil))
>> ;; End of basic block 1, registers live:
>> 1 [A1] 29 [B13] 30 [B14]
>>
>> (note 16 15 25 NOTE_INSN_FUNCTION_END)
>>
>> (note 25 16 0 NOTE_INSN_DELETED)
>> -----------------------------------------------------------------
>>
>> However, when I compile it
>>
>> $ hcc -da foobar8.c
>>
>> I get an ICE at the end of the compilation, and the assembly source is
>> not produced:
>>
>> [ lots of my debugging output removed ]
>>
>> legitimate_address2(non-strict, soft-reg allowed), X=
>> (reg/f:HI 29 B13)
>> legitimate_address2() yes: (X)==REG && non_strict_base_reg(REGNO(X))
>>
>> -----------------MOVHI--------------- [generating a MOV X, Y insn]
>> MOVHI: operands[0]
>> (mem:HI (reg/f:HI 29 B13) [0 S2 A8])
>> MOVHI: operands[1]
>> (reg:HI 31 B15)
>> MOVHI --- END
>>
>>
>> [then checking if -2(B13) is legitimate, it is not...]
>>
>> legitimate_address2(non-strict, soft-reg allowed), X=
>> (plus:HI (reg/f:HI 29 B13)
>> (const_int -2 [0xfffffffffffffffe]))
>> legitimate_address2(): FOUND register+offset --> FAIL!
>>
>> legitimate_address2(non-strict, soft-reg allowed), X=
>> (plus:HI (reg/f:HI 29 B13)
>> (const_int -2 [0xfffffffffffffffe]))
>> legitimate_address2(): FOUND register+offset --> FAIL!
>>
>> legitimate_address2(non-strict, soft-reg allowed), X=
>> (plus:HI (reg/f:HI 29 B13)
>> (const_int -2 [0xfffffffffffffffe]))
>> legitimate_address2(): FOUND register+offset --> FAIL!
>>
>> legitimate_address2(non-strict, soft-reg allowed), X=
>> (plus:HI (reg/f:HI 29 B13)
>> (const_int -2 [0xfffffffffffffffe]))
>> legitimate_address2(): FOUND register+offset --> FAIL!
>>
>>
>> [and after four check of the add above, gcc 4.0.2 freaks out with ]
>>
>> foobar8.c: In function ‘foobar’:
>> foobar8.c:7: internal compiler error: in change_address_1, at
>> emit-rtl.c:1800
>> Please submit a full bug report,
>>
>> with preprocessed source if appropriate.
>> See <URL:http://gcc.gnu.org/bugs.html> for instructions.
>>
>>
>> The failed assertion is in line 1800: some "addr" is not an address.
>>
>> 1784 change_address_1 (rtx memref, enum machine_mode mode, rtx addr,
>> int validate)
>> 1785 {
>> 1786 rtx new;
>> 1787
>> 1788 gcc_assert (MEM_P (memref));
>> 1789 if (mode == VOIDmode)
>> 1790 mode = GET_MODE (memref);
>> 1791 if (addr == 0)
>> 1792 addr = XEXP (memref, 0);
>> 1793 if (mode == GET_MODE (memref) && addr == XEXP (memref, 0)
>> 1794 && (!validate || memory_address_p (mode, addr)))
>> 1795 return memref;
>> 1796
>> 1797 if (validate)
>> 1798 {
>> 1799 if (reload_in_progress || reload_completed)
>> 1800 gcc_assert (memory_address_p (mode, addr));
>> 1801 else
>> 1802 addr = memory_address (mode, addr);
>> 1803 }
>> 1804
>> 1805 if (rtx_equal_p (addr, XEXP (memref, 0)) && mode == GET_MODE
>> (memref))
>> 1806 return memref;
>> 1807
>> 1808 new = gen_rtx_MEM (mode, addr);
>> 1809 MEM_COPY_ATTRIBUTES (new, memref);
>> 1810 return new;
>> 1811 }
>>
>>
>> Could it be the REG+OFF which the LEGITIMATE_ADDRESS() rejects above?
>>
>> But then why all the others before it get re-written by a call to
>> LEGITIMIZE_ ADDRESS() ?!
>>
>> What is calling change_address_1() at the end of the compilation phase?
>>
>> Thanks
>>
>> Sergio
>>
>> ==========================================================================
>>
>>
>>
>>
>>
>>
>> Sergio Ruocco wrote:
>>> Now my GO_IF_LEGITIMATE_ADDRESS refuses anything that is not a REG
>>> or a CONSTANT_ADDRESS:
>>>
>>> int legitimate_address1(enum machine_mode MODE,rtx X)
>>> {
>>> if(CONSTANT_ADDRESS_P(X))
>>> return 1;
>>> if(GET_CODE(X)==REG && is_base_reg(REGNO(X)))
>>> return 1;
>>>
>>> return 0; /* fails everything else */
>>>
>>> } /* this is the strict version, the non strict version is similar */
>>>
>>> but GCC (4.0.2, just in case the version is relevant) still aborts the
>>> compilation.
>>>
>>>
>>> Then I found this wiki note about forcing complex addresses into
>>> registers: http://gcc.gnu.org/wiki/WritingANewBackEnd
>>>
>>> ...
>>> rtx copy_addr_to_reg (rtx x)
>>> Equivalent to copy_to_mode_reg (Pmode, x). For example, this
>>> function can be used to compute a complex address X in a register for an
>>> instruction which supports only register indirect addressing. See also
>>> replace_equiv_address() below.
>>> ...
>>>
>>>
>>> Thus I changed in the .md file the movXX RTL expand macro to force any
>>> MEM expression into a register:
>>>
>>> (define_expand "movhi" /* my micro is 16 bit... */
>>> [(set (match_operand:HI 0 "nonimmediate_operand" "")
>>> (match_operand:HI 1 "general_operand" "")
>>> )]
>>> ""
>>> {
>>> if(!no_new_pseudos && GET_CODE(operands[0])==MEM) {
>>> if( /* addr in operands[0] == base reg + offset */ )
>>> operands[0] = copy_addr_to_reg ( operands[0] );
>>> }
>>> )
>>>
>>> The GCC still fails to generate the assembly code to do the arithmetic
>>> computation of the baseReg+offset-->tempReg, and then use (tempReg) as
>>> address.
>>>
>>> Note that with the current MD GCC is able to generate simple sums like
>>> R1 = R2 + R3 and R1 = R2 + IMM, thus the basic math to compute an
>>> address is there.
>>>
>>> Any suggestion on what I am doing wrong ?
>>>
>>> Sergio
>>>
>>>
>>> Michael Hope wrote:
>>>> Hi Sergio. My port has similar addressing modes - all memory must be
>>>> accessed by one of two registers and can only be accessed indirectly,
>>>> indirect with pre increment, and indirect with post increment. The
>>>> key is GO_IF_LEGITIMATE_ADDRESS and the legitimate address helper
>>>> function. Mine looks like this:
>>>>
>>>> /* Return 1 if the address is OK, otherwise 0.
>>>> Used by GO_IF_LEGITIMATE_ADDRESS. */
>>>>
>>>> bool
>>>> tomi_legitimate_address (enum machine_mode mode ATTRIBUTE_UNUSED,
>>>> rtx x,
>>>> bool strict_checking)
>>>> {
>>>> /* (mem reg) */
>>>> if (REG_P (x)
>>>> && tomi_reg_ok (x, strict_checking)
>>>> )
>>>> {
>>>> return 1;
>>>> }
>>>>
>>>> if (GET_CODE(x) == PRE_DEC)
>>>> {
>>>> ...
>>>> }
>>>>
>>>> if (GET_CODE(x) == POST_INC)
>>>> {
>>>> ...
>>>> }
>>>>
>>>> return 0;
>>>> }
>>>>
>>>> tomi_reg_ok returns true if x is any register when strict checking is
>>>> clear and true if x is one of my addressing registers when strict
>>>> checking is set.
>>>>
>>>> GCC will feed any memory accesses through this function to see if they
>>>> are directly supported, and if not it will break them up into
>>>> something smaller and try again.
>>>>
>>>> Hope that helps,
>>>>
>>>> -- Michael
>>>>
>>>>
>>>> 2010/1/26 Sergio Ruocco <sergio.ruocco@gmail.com>:
>>>>> Gabriel Paubert wrote:
>>>>>> On Mon, Jan 25, 2010 at 01:34:09PM +0100, Sergio Ruocco wrote:
>>>>>>> Hi everyone,
>>>>>>>
>>>>>>> I am porting GCC to a custom 16-bit microcontroller with very limited
>>>>>>> addressing modes. Basically, it can only load/store using a (general
>>>>>>> purpose) register as the address, without any offset:
>>>>>>>
>>>>>>> LOAD (R2) R1 ; load R1 from memory at address (R2)
>>>>>>> STORE R1 (R2) ; store R1 to memory at address (R2)
>>>>>>>
>>>>>>> As far as I can understand, this is more limited than the current
>>>>>>> architectures supported by GCC that I found in the current gcc/config/*.
>>>>>> The Itanium (ia64) has the same limited choice of addressing modes.
>>>>>>
>>>>>> Gabriel
>>>>> Thanks Gabriel.
>>>>>
>>>>> I dived into the ia64 md, but it is still unclear to me how the various
>>>>> parts (macros, define_expand and define_insn in MD etc.) work together
>>>>> to force the computation of a source/dest address plus offset into a
>>>>> register... can anyone help me with this ?
>>>>>
>>>>> Thanks,
>>>>>
>>>>> Sergio
>>>>>
>>>
>>