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.ruo...@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.ruo...@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 >>>>> >>> >>