Restricted addressing modes in ColdFire FPU for DFmode
Peter Barada
pbarada@mail.wm.sps.mot.com
Fri Dec 7 10:13:00 GMT 2001
I'm working on adding the V4E ColdFire FPU instructions to gcc-2.95.3,
and I've been having a very hard time convincing gcc to deal with the
restricted addressing modes that the ColdFire FPU supports.
The fmove.d instruction supports only the following addressing modes:
predecrement, postincement, indirect, inderect with offset, and
pc-relative with offset(for loads). Note that neither register direct
nor absolute addressing is supported. I created to following RTL for
movdf:
(define_expand "movdf"
[(set (match_operand:DF 0 "nonimmediate_operand" "")
(match_operand:DF 1 "general_operand" ""))]
""
"
{
if (TARGET_FPU_V4E) {
/* The ColdFire FPU can't deal with absolute addresses or double
* constants, so push the constant to memory */
if (CONSTANT_P (operands[1])) {
operands[1] = force_const_mem (DFmode, operands[1]);
if (! memory_address_p (DFmode, XEXP (operands[1], 0))
&& ! reload_in_progress)
operands[1] = change_address (operands[1], DFmode,
XEXP (operands[1], 0));
}
/* Since the Coldfire can't deal with absolute addresses, force
* the address of the reference into a register and do it indirect */
if (!reload_in_progress && GET_CODE (operands[1]) == MEM
&& GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF)
operands[1] = gen_rtx (MEM, DFmode,
force_reg (Pmode, XEXP (operands[1], 0)));
}
}")
(define_insn "movdf_v4e"
[(set (match_operand:DF 0 "general_operand" "=f,<Q>S")
(match_operand:DF 1 "general_operand" "f<Q>S,f"))]
"TARGET_FPU_V4E"
"*
{
return \"fmove%.d %1,%0\";
}")
And that split up symbolic addressing quite well(as well as handled constants):
(insn 8 6 9 (set (reg:SI 30)
(symbol_ref/u:SI ("*.LC0"))) -1 (nil)
(expr_list:REG_EQUAL (symbol_ref/u:SI ("*.LC0"))
(nil)))
(insn 9 8 10 (set (reg/i:DF 16 %fp0)
(mem:DF (reg:SI 30) 0)) -1 (nil)
(nil))
but the combiner converted that back into:
(insn 9 8 10 (set (reg/i:DF 16 %fp0)
(mem:DF (symbol_ref/u:SI ("*.LC0")) 0)) 67 {movdf_v4e} (nil)
(expr_list:REG_EQUAL (const_double:DF (mem/u:DF (symbol_ref/u:SI ("*.LC0")) 0) 0 [0x0] 0 [0x0] 1073643520 [0x3ffe8000])
(nil)))
Which died with:
/tmp/z.c: In function `x':
/tmp/z.c:5: internal error--insn does not satisfy its constraints:
(insn 21 8 9 (set (reg:DF 17 %fp1)
(mem:DF (symbol_ref/u:SI ("*.LC0")) 0)) 67 {movdf_v4e} (nil)
(nil))
I looked over combine.c, and I didn't see anyway that a target can
prevent combination from happening. So in desperation I decided to
add a flag to the rtx struct:
/* 1 in an INSN or a SET if this rtx can *not* be combined with
either RTX next to it. Its necessary to limit the combiner from
creating RTX that the machine description worked very hard to
keep seperate */
unsigned cannot_combine : 1;
And its access macro:
#define RTX_CANNOT_COMBINE_P(RTX) ((RTX)->cannot_combine)
As well as modify the initializer of global_rtl to add the extra zero...
And then added the following statment to cant_combine_insn_p():
/* For particular chips with restrictive addressing modes, the
machine description may split up accesses and then mark the
instruction. As an example the ColdFire FPU can't deal with
absolute addressing, but the ColdFire integer unit can, so the
m68k.md forces the symbol_ref into a register and uses indirect
addressing, and marks the instruction as one that can't be
combined. */
if (RTX_CANNOT_COMBINE_P (insn))
return 1;
As well as changed the movdf pattern to:
(define_expand "movdf"
[(set (match_operand:DF 0 "nonimmediate_operand" "")
(match_operand:DF 1 "general_operand" ""))]
""
"
{
rtx insn;
if (TARGET_FPU_V4E) {
/* The ColdFire FPU can't deal with absolute addresses or double
* constants, so push it to memory */
if (CONSTANT_P (operands[1])) {
operands[1] = force_const_mem (DFmode, operands[1]);
if (! memory_address_p (DFmode, XEXP (operands[1], 0))
&& ! reload_in_progress)
operands[1] = change_address (operands[1], DFmode,
XEXP (operands[1], 0));
}
/* Since the Coldfire can't deal with absolute addresses, force
* the address of the reference into a register and do it indirect */
if (!reload_in_progress && GET_CODE (operands[1]) == MEM
&& GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF) {
operands[1] = gen_rtx (MEM, DFmode,
force_reg (Pmode, XEXP (operands[1], 0)));
/* Create the instruciton and set RTX_CANNOT_COMBINE_P to
prevent the combiner from putting this back together.
insn = emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1]));
RTX_CANNOT_COMBINE_P (insn) = 1;
DONE;
}
}
}")
Now the following code:
double x(void)
{
return 0.5;
}
Compiles into:
.file "z.c"
gcc2_compiled.:
.section .rodata
.align 2
.LC0:
.double 0r5.00000000000000000000e-1
.text
.globl x
.type x,@function
x:
link.w %a6,#0
lea .LC0,%a0
fmove.d (%a0),%fp0
unlk %a6
rts
Which looks correct.
My question to you all is whether or not this is a viable approach.
Does anyone have a suggestion for how better to solve this problem?
Thanks in advance for your guidance,
--
Peter Barada Peter.Barada@motorola.com
Wizard 781-852-2768 (direct)
WaveMark Solutions(wholly owned by Motorola) 781-270-0193 (fax)
More information about the Gcc
mailing list