Commit 95726648 by Doug Evans

sparc.c (sp64_medium_pic_operand): New function.

	* sparc/sparc.c (sp64_medium_pic_operand): New function.
	(move_pic_label): Delete.
	(legitimize_pic_address): Simplify using some named patterns.
	(finalize_pic): Add preliminary sparc64 support.
	(emit_move_sequence): Reorganize.
	* sparc/sparc.md (pic_lo_sum_si,pic_sethi_si,get_pc_sp32,get_pc_sp64,
	move_pic_label_si,move_label_di,sethi_di_sp64): Make named patterns.
	(sethi_di_sp64_const,sethi_di_medium_pic): New anonymous patterns.
	(move_pic_label_si,move_label_di): Optimize for near labels.
	(tablejump): Use for TARGET_MEDANY.
	(casesi): Delete.

From-SVN: r11185
parent cd9784db
......@@ -341,6 +341,31 @@ symbolic_memory_operand (op, mode)
|| GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF);
}
/* Return 1 if the operand is an argument used in generating pic references
in either the medium/low or medium/anywhere code models of sparc64. */
int
sp64_medium_pic_operand (op, mode)
rtx op;
enum machine_mode mode;
{
/* Check for (const (minus (symbol_ref:GOT)
(const (minus (label) (pc))))). */
if (GET_CODE (op) != CONST)
return 0;
op = XEXP (op, 0);
if (GET_CODE (op) != MINUS)
return 0;
if (GET_CODE (XEXP (op, 0)) != SYMBOL_REF)
return 0;
/* ??? Ensure symbol is GOT. */
if (GET_CODE (XEXP (op, 1)) != CONST)
return 0;
if (GET_CODE (XEXP (XEXP (op, 1), 0)) != MINUS)
return 0;
return 1;
}
/* Return 1 if the operand is a data segment reference. This includes
the readonly data segment, or in other words anything but the text segment.
This is needed in the medium/anywhere code model on v9. These values
......@@ -356,7 +381,8 @@ data_segment_operand (op, mode)
case SYMBOL_REF :
return ! SYMBOL_REF_FLAG (op);
case PLUS :
/* Assume canonical format of symbol + constant. */
/* Assume canonical format of symbol + constant.
Fall through. */
case CONST :
return data_segment_operand (XEXP (op, 0));
default :
......@@ -379,7 +405,8 @@ text_segment_operand (op, mode)
case SYMBOL_REF :
return SYMBOL_REF_FLAG (op);
case PLUS :
/* Assume canonical format of symbol + constant. */
/* Assume canonical format of symbol + constant.
Fall through. */
case CONST :
return text_segment_operand (XEXP (op, 0));
default :
......@@ -453,17 +480,6 @@ move_operand (op, mode)
}
int
move_pic_label (op, mode)
rtx op;
enum machine_mode mode;
{
/* Special case for PIC. */
if (flag_pic && GET_CODE (op) == LABEL_REF)
return 1;
return 0;
}
int
splittable_symbolic_memory_operand (op, mode)
rtx op;
enum machine_mode mode;
......@@ -1150,16 +1166,9 @@ legitimize_pic_address (orig, mode, reg)
won't get confused into thinking that these two instructions
are loading in the true address of the symbol. If in the
future a PIC rtx exists, that should be used instead. */
emit_insn (gen_rtx (SET, VOIDmode, temp_reg,
gen_rtx (HIGH, Pmode,
gen_rtx (UNSPEC, Pmode,
gen_rtvec (1, orig),
0))));
emit_insn (gen_rtx (SET, VOIDmode, temp_reg,
gen_rtx (LO_SUM, Pmode, temp_reg,
gen_rtx (UNSPEC, Pmode,
gen_rtvec (1, orig),
0))));
emit_insn (gen_pic_sethi_si (temp_reg, orig));
emit_insn (gen_pic_lo_sum_si (temp_reg, temp_reg, orig));
address = temp_reg;
}
else
......@@ -1215,6 +1224,7 @@ legitimize_pic_address (orig, mode, reg)
return gen_rtx (PLUS, Pmode, base, offset);
}
else if (GET_CODE (orig) == LABEL_REF)
/* ??? Why do we do this? */
current_function_uses_pic_offset_table = 1;
return orig;
......@@ -1247,18 +1257,12 @@ finalize_pic ()
abort ();
flag_pic = 0;
l1 = gen_label_rtx ();
l2 = gen_label_rtx ();
/* ??? sparc64 pic currently under construction. */
start_sequence ();
emit_label (l1);
/* Note that we pun calls and jumps here! */
emit_jump_insn (gen_rtx (PARALLEL, VOIDmode,
gen_rtvec (2,
gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (LABEL_REF, VOIDmode, l2)),
gen_rtx (SET, VOIDmode, gen_rtx (REG, SImode, 15), gen_rtx (LABEL_REF, VOIDmode, l2)))));
emit_label (l2);
l1 = gen_label_rtx ();
/* Initialize every time through, since we can't easily
know this to be permanent. */
......@@ -1271,13 +1275,14 @@ finalize_pic ()
gen_rtx (LABEL_REF, VOIDmode, l1),
pc_rtx))));
if (Pmode == DImode)
emit_insn (gen_rtx (PARALLEL, VOIDmode,
gen_rtvec (2,
gen_rtx (SET, VOIDmode, pic_offset_table_rtx,
gen_rtx (HIGH, Pmode, pic_pc_rtx)),
gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, Pmode, 1)))));
else
if (! TARGET_ARCH64)
{
l2 = gen_label_rtx ();
emit_label (l1);
/* Note that we pun calls and jumps here! */
emit_jump_insn (gen_get_pc_sp32 (l2));
emit_label (l2);
emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx,
gen_rtx (HIGH, Pmode, pic_pc_rtx)));
......@@ -1288,10 +1293,39 @@ finalize_pic ()
emit_insn (gen_rtx (SET, VOIDmode,
pic_offset_table_rtx,
gen_rtx (PLUS, Pmode,
pic_offset_table_rtx, gen_rtx (REG, Pmode, 15))));
pic_offset_table_rtx,
gen_rtx (REG, Pmode, 15))));
/* emit_insn (gen_rtx (ASM_INPUT, VOIDmode, "!#PROLOGUE# 1")); */
LABEL_PRESERVE_P (l1) = 1;
LABEL_PRESERVE_P (l2) = 1;
}
else
{
/* ??? This definately isn't right for -mfullany. */
/* ??? And it doesn't quite seem right for the others either. */
emit_label (l1);
emit_insn (gen_get_pc_sp64 (gen_rtx (REG, Pmode, 1)));
/* Don't let the scheduler separate the previous insn from `l1'. */
emit_insn (gen_blockage ());
emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx,
gen_rtx (HIGH, Pmode, pic_pc_rtx)));
emit_insn (gen_rtx (SET, VOIDmode,
pic_offset_table_rtx,
gen_rtx (LO_SUM, Pmode,
pic_offset_table_rtx, pic_pc_rtx)));
emit_insn (gen_rtx (SET, VOIDmode,
pic_offset_table_rtx,
gen_rtx (PLUS, Pmode,
pic_offset_table_rtx, gen_rtx (REG, Pmode, 1))));
/* emit_insn (gen_rtx (ASM_INPUT, VOIDmode, "!#PROLOGUE# 1")); */
LABEL_PRESERVE_P (l1) = 1;
}
flag_pic = orig_flag_pic;
seq = gen_sequence ();
......@@ -1352,10 +1386,33 @@ emit_move_sequence (operands, mode)
}
}
/* Simplify the source if we need to. Must handle DImode HIGH operators
here because such a move needs a clobber added. */
if ((GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode))
|| (GET_CODE (operand1) == HIGH && GET_MODE (operand1) == DImode))
if (GET_CODE (operand1) == LABEL_REF
&& mode == SImode && flag_pic)
{
if (TARGET_ARCH64)
abort ();
emit_insn (gen_move_pic_label_si (operand0, XEXP (operand1, 0)));
return 1;
}
/* Non-pic LABEL_REF's in sparc64 are expensive to do the normal way,
so always use special code. */
else if (GET_CODE (operand1) == LABEL_REF
&& mode == DImode)
{
if (! TARGET_ARCH64)
abort ();
emit_insn (gen_move_label_di (operands[0], XEXP (operands[1], 0)));
return 1;
}
/* DImode HIGH values in sparc64 need a clobber added. */
else if (TARGET_ARCH64
&& GET_CODE (operand1) == HIGH && GET_MODE (operand1) == DImode)
{
emit_insn (gen_sethi_di_sp64 (operand0, XEXP (operand1, 0)));
return 1;
}
/* Simplify the source if we need to. */
else if (GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode))
{
if (flag_pic && symbolic_operand (operand1, mode))
{
......@@ -1387,29 +1444,7 @@ emit_move_sequence (operands, mode)
? operand0 : gen_reg_rtx (mode));
if (TARGET_ARCH64 && mode == DImode)
{
int high_operand = 0;
/* If the operand is already a HIGH, then remove the HIGH so
that we won't get duplicate HIGH operators in this insn.
Also, we must store the result into the original dest,
because that is where the following LO_SUM expects it. */
if (GET_CODE (operand1) == HIGH)
{
operand1 = XEXP (operand1, 0);
high_operand = 1;
}
emit_insn (gen_rtx (PARALLEL, VOIDmode,
gen_rtvec (2,
gen_rtx (SET, VOIDmode, temp,
gen_rtx (HIGH, mode, operand1)),
gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, DImode, 1)))));
/* If this was a high operand, then we are now finished. */
if (high_operand)
return 1;
}
emit_insn (gen_sethi_di_sp64 (temp, operand1));
else
emit_insn (gen_rtx (SET, VOIDmode, temp,
gen_rtx (HIGH, mode, operand1)));
......@@ -1418,21 +1453,6 @@ emit_move_sequence (operands, mode)
}
}
if (GET_CODE (operand1) == LABEL_REF && flag_pic)
{
/* The procedure for doing this involves using a call instruction to
get the pc into o7. We need to indicate this explicitly because
the tablejump pattern assumes that it can use this value also. */
emit_insn (gen_rtx (PARALLEL, VOIDmode,
gen_rtvec (2,
gen_rtx (SET, VOIDmode, operand0,
operand1),
gen_rtx (SET, VOIDmode,
gen_rtx (REG, mode, 15),
pc_rtx))));
return 1;
}
/* Now have insn-emit do whatever it normally does. */
return 0;
}
......
......@@ -1467,13 +1467,29 @@
;; is not an "arith_operand".
[(set_attr "length" "1")])
(define_insn "*sethi_si"
[(set (match_operand:SI 0 "register_operand" "=r")
(high:SI (match_operand 1 "" "")))]
"check_pic (1)"
"sethi %%hi(%a1),%0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*sethi_hi"
[(set (match_operand:HI 0 "register_operand" "=r")
(high:HI (match_operand 1 "" "")))]
"check_pic (1)"
"sethi %%hi(%a1),%0"
[(set_attr "type" "move")
(set_attr "length" "1")])
;; For PIC, symbol_refs are put inside unspec so that the optimizer will not
;; confuse them with real addresses.
(define_insn "*pic_lo_sum_si"
(define_insn "pic_lo_sum_si"
[(set (match_operand:SI 0 "register_operand" "=r")
(lo_sum:SI (match_operand:SI 1 "register_operand" "r")
(unspec:SI [(match_operand:SI 2 "immediate_operand" "in")] 0)))]
""
"flag_pic"
;; V9 needs "add" because of the code models. We still use "or" for v8
;; so we can compare the old compiler with the new.
"* return TARGET_ARCH64 ? \"add %1,%%lo(%a2),%0\" : \"or %1,%%lo(%a2),%0\";"
......@@ -1483,53 +1499,77 @@
;; For PIC, symbol_refs are put inside unspec so that the optimizer will not
;; confuse them with real addresses.
(define_insn "*pic_sethi_si"
(define_insn "pic_sethi_si"
[(set (match_operand:SI 0 "register_operand" "=r")
(high:SI (unspec:SI [(match_operand 1 "" "")] 0)))]
"check_pic (1)"
"flag_pic && check_pic (1)"
"sethi %%hi(%a1),%0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*sethi_si"
[(set (match_operand:SI 0 "register_operand" "=r")
(high:SI (match_operand 1 "" "")))]
"check_pic (1)"
"sethi %%hi(%a1),%0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "get_pc_sp32"
[(set (pc) (label_ref (match_operand 0 "" "")))
(set (reg:SI 15) (label_ref (match_dup 0)))]
"! TARGET_PTR64"
"call %l0%#"
[(set_attr "type" "uncond_branch")])
(define_insn "*sethi_hi"
[(set (match_operand:HI 0 "register_operand" "=r")
(high:HI (match_operand 1 "" "")))]
"check_pic (1)"
"sethi %%hi(%a1),%0"
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "get_pc_sp64"
[(set (match_operand:DI 0 "register_operand" "=r") (pc))]
"TARGET_PTR64"
"rd %%pc,%0"
[(set_attr "type" "move")])
;; Special pic pattern, for loading the address of a label into a register.
;; It clobbers o7 because the call puts the return address (i.e. pc value)
;; there.
;; there. The pic tablejump pattern also uses this.
(define_insn "*move_pic_label_si"
(define_insn "move_pic_label_si"
[(set (match_operand:SI 0 "register_operand" "=r")
(match_operand:SI 1 "move_pic_label" "i"))
(label_ref:SI (match_operand 1 "" "")))
(set (reg:SI 15) (pc))]
""
"\\n1:\;call 2f\;sethi %%hi(%l1-1b),%0\\n2:\\tor %0,%%lo(%l1-1b),%0\;add %0,%%o7,%0"
"flag_pic"
"*
{
if (get_attr_length (insn) == 2)
return \"\\n1:\;call 2f\;add %%o7,%%lo(%l1-1b),%0\\n2:\";
else
return \"\\n1:\;call 2f\;sethi %%hi(%l1-1b),%0\\n2:\\tor %0,%%lo(%l1-1b),%0\;add %0,%%o7,%0\";
}"
[(set_attr "type" "multi")
(set_attr "length" "4")])
;; v9 special pic pattern, for loading the address of a label into a register.
; 1024 = 4096 bytes / 4 bytes/insn
(set (attr "length") (if_then_else (ltu (minus (match_dup 1) (pc))
(const_int 1024))
(const_int 2)
(const_int 4)))])
;; Special sparc64 pattern for loading the address of a label into a register.
;; The pic and non-pic cases are the same since it's the most efficient way.
;;
;; ??? The non-pic case doesn't need to use %o7, we could use a scratch
;; instead. But the pic case doesn't need to use %o7 either. We handle them
;; both here so that when this is fixed, they can both be fixed together.
;; Don't forget that the pic jump table stuff uses %o7 (that will need to be
;; changed too).
(define_insn "*move_pic_label_di"
(define_insn "move_label_di"
[(set (match_operand:DI 0 "register_operand" "=r")
(match_operand:DI 1 "move_pic_label" "i"))
(label_ref:DI (match_operand 1 "" "")))
(set (reg:DI 15) (pc))]
"TARGET_ARCH64"
"\\n1:\;call 2f\;sethi %%hi(%l1-1b),%0\\n2:\\tor %0,%%lo(%l1-1b),%0\;add %0,%%o7,%0"
"*
{
if (get_attr_length (insn) == 2)
return \"\\n1:\;rd %%pc,%%o7\;add %%o7,%l1-1b,%0\";
else
return \"\\n1:\;rd %%pc,%%o7\;sethi %%hi(%l1-1b),%0\;add %0,%%lo(%l1-1b),%0\;sra %0,0,%0\;add %0,%%o7,%0\";
}"
[(set_attr "type" "multi")
(set_attr "length" "4")])
; 1024 = 4096 bytes / 4 bytes/insn
(set (attr "length") (if_then_else (ltu (minus (match_dup 1) (pc))
(const_int 1024))
(const_int 2)
(const_int 5)))])
(define_insn "*lo_sum_di_sp32"
[(set (match_operand:DI 0 "register_operand" "=r")
......@@ -1548,8 +1588,6 @@
;; is not an "arith_operand".
[(set_attr "length" "1")])
;; ??? Gas does not handle %lo(DI), so we use the same code for ! TARGET_ARCH64.
;; ??? The previous comment is obsolete.
;; ??? Optimizer does not handle "or %o1,%lo(0),%o1". How about add?
(define_insn "*lo_sum_di_sp64"
......@@ -1617,12 +1655,15 @@
;;; e.g. by using a toc like the romp and rs6000 ports do for addresses, reg
;;; 1 will then no longer need to be considered a fixed reg.
;;; Gas doesn't have any 64 bit constant support, so don't use %uhi and %ulo
;;; on constants. Symbols have to be handled by the linker, so we must use
;;; %uhi and %ulo for them, but gas will handle these correctly.
;;; ??? This comment is obsolete, gas handles them now.
(define_expand "sethi_di_sp64"
[(parallel
[(set (match_operand:DI 0 "register_operand" "")
(high:DI (match_operand 1 "general_operand" "")))
(clobber (reg:DI 1))])]
"TARGET_ARCH64"
"")
(define_insn "*sethi_di_sp64"
(define_insn "*sethi_di_sp64_const"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (match_operand 1 "const_double_operand" "")))
(clobber (reg:DI 1))]
......@@ -1661,9 +1702,9 @@
;; When TARGET_MEDLOW, assume that the upper 32 bits of symbol addresses are
;; always 0.
;; When TARGET_MEDANY, the upper 32 bits of function addresses are 0.
;; The data segment has a maximum size of 32 bits, but may be located anywhere.
;; MEDANY_BASE_REG contains the start address, currently %g4.
;; When TARGET_MEDANY, the text and data segments have a maximum size of 32
;; bits and may be located anywhere. MEDANY_BASE_REG contains the start
;; address of the data segment, currently %g4.
;; When TARGET_FULLANY, symbolic addresses are 64 bits.
(define_insn "*sethi_di_medlow"
......@@ -1676,6 +1717,14 @@
[(set_attr "type" "move")
(set_attr "length" "1")])
(define_insn "*sethi_di_medium_pic"
[(set (match_operand:DI 0 "register_operand" "=r")
(high:DI (match_operand 1 "sp64_medium_pic_operand" "")))]
"(TARGET_MEDLOW || TARGET_MEDANY) && check_pic (1)"
"sethi %%hi(%a1),%0"
[(set_attr "type" "move")
(set_attr "length" "1")])
;; WARNING: %0 gets %hi(%1)+%g4.
;; You cannot OR in %lo(%1), it must be added in.
......@@ -4782,7 +4831,7 @@
(define_expand "tablejump"
[(parallel [(set (pc) (match_operand 0 "register_operand" "r"))
(use (label_ref (match_operand 1 "" "")))])]
"! TARGET_MEDANY"
""
"
{
if (GET_MODE (operands[0]) != Pmode)
......@@ -4830,54 +4879,6 @@
"jmp %a0%#"
[(set_attr "type" "uncond_branch")])
(define_insn "*get_pc_sp32"
[(set (pc) (label_ref (match_operand 0 "" "")))
(set (reg:SI 15) (label_ref (match_dup 0)))]
"! TARGET_PTR64"
"call %l0%#"
[(set_attr "type" "uncond_branch")])
(define_insn "*get_pc_sp64"
[(set (pc) (label_ref (match_operand 0 "" "")))
(set (reg:DI 15) (label_ref (match_dup 0)))]
"TARGET_PTR64"
"call %l0%#"
[(set_attr "type" "uncond_branch")])
;; Implement a switch statement for the medium/anywhere code model.
;; This wouldn't be necessary if we could distinguish label refs of the jump
;; table from other label refs. The problem is that jump tables live in the
;; .rodata section and thus we need to add %g4 to get their address.
(define_expand "casesi"
[(set (match_dup 5)
(minus:SI (match_operand:SI 0 "register_operand" "")
(match_operand:SI 1 "nonmemory_operand" "")))
(set (reg:CC 0)
(compare:CC (match_dup 5)
(match_operand:SI 2 "nonmemory_operand" "")))
(set (pc)
(if_then_else (gtu (reg:CC 0)
(const_int 0))
(label_ref (match_operand 4 "" ""))
(pc)))
(parallel [(set (match_dup 6) (high:DI (label_ref (match_operand 3 "" ""))))
(clobber (reg:DI 1))])
(set (match_dup 6)
(lo_sum:DI (match_dup 6) (label_ref (match_dup 3))))
(set (match_dup 6) (plus:DI (match_dup 6) (reg:DI 4)))
(set (match_dup 7) (zero_extend:DI (match_dup 5)))
(set (match_dup 7) (ashift:DI (match_dup 7) (const_int 3)))
(set (match_dup 7) (mem:DI (plus:DI (match_dup 6) (match_dup 7))))
(set (pc) (match_dup 7))]
"TARGET_MEDANY"
"
{
operands[5] = gen_reg_rtx (SImode);
operands[6] = gen_reg_rtx (DImode);
operands[7] = gen_reg_rtx (DImode);
}")
;; This pattern recognizes the "instruction" that appears in
;; a function call that wants a structure value,
;; to inform the called function if compiled with Sun CC.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment