aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/config/mips/mips.c31
-rw-r--r--gcc/config/mips/mips.h53
-rw-r--r--gcc/config/mips/mips.md84
3 files changed, 156 insertions, 12 deletions
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index 16315b4..19a00cc 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -3191,6 +3191,28 @@ override_options ()
else
mips_abicalls = MIPS_ABICALLS_NO;
+ /* -membedded-pic is a form of PIC code suitable for embedded
+ systems. All calls are made using PC relative addressing, and
+ all data is addressed using the $gp register. This requires gas,
+ which does most of the work, and GNU ld, which automatically
+ expands PC relative calls which are out of range into a longer
+ instruction sequence. All gcc really does differently is
+ generate a different sequence for a switch. */
+ if (TARGET_EMBEDDED_PIC)
+ {
+ flag_pic = 1;
+ if (TARGET_ABICALLS)
+ warning ("-membedded-pic and -mabicalls are incompatible");
+ if (g_switch_set)
+ warning ("-G and -membedded-pic are incompatible");
+ /* Setting mips_section_threshold is not required, because gas
+ will force everything to be GP addressable anyhow, but
+ setting it will cause gcc to make better estimates of the
+ number of instructions required to access a particular data
+ item. */
+ mips_section_threshold = 0x7fffffff;
+ }
+
/* -mrnames says to use the MIPS software convention for register
names instead of the hardware names (ie, $a0 instead of $4).
We do this by switching the names in mips_reg_names, which the
@@ -3387,6 +3409,7 @@ mips_debugger_offset (addr, offset)
'M' print high-order register of double-word register operand.
'C' print part of opcode for a branch condition.
'N' print part of opcode for a branch condition, inverted.
+ 'S' X is CODE_LABEL, print with prefix of "LS" (for embedded switch).
'(' Turn on .set noreorder
')' Turn on .set reorder
'[' Turn on .set noat
@@ -3569,6 +3592,14 @@ print_operand (file, op, letter)
abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%N");
}
+ else if (letter == 'S')
+ {
+ char buffer[100];
+
+ ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op));
+ assemble_name (file, buffer);
+ }
+
else if (code == REG)
{
register int regnum = REGNO (op);
diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h
index e7a954e..175649b 100644
--- a/gcc/config/mips/mips.h
+++ b/gcc/config/mips/mips.h
@@ -254,12 +254,12 @@ extern char *mktemp ();
#define MASK_HALF_PIC 0x00000800 /* Emit OSF-style pic refs to externs*/
#define MASK_LONG_CALLS 0x00001000 /* Always call through a register */
#define MASK_64BIT 0x00002000 /* Use 64 bit GP registers and insns */
-#define MASK_UNUSED1 0x00004000
-#define MASK_UNUSED2 0x00008000
-#define MASK_UNUSED3 0x00010000
-#define MASK_UNUSED4 0x00020000
-#define MASK_UNUSED5 0x00040000
-#define MASK_UNUSED6 0x00080000
+#define MASK_EMBEDDED_PIC 0x00004000 /* Generate embedded PIC code */
+#define MASK_UNUSED1 0x00008000
+#define MASK_UNUSED2 0x00010000
+#define MASK_UNUSED3 0x00020000
+#define MASK_UNUSED4 0x00040000
+#define MASK_UNUSED5 0x00080000
/* Dummy switches used only in spec's*/
#define MASK_MIPS_TFILE 0x00000000 /* flag for mips-tfile usage */
@@ -327,6 +327,10 @@ extern char *mktemp ();
/* always call through a register */
#define TARGET_LONG_CALLS (target_flags & MASK_LONG_CALLS)
+ /* generate embedded PIC code;
+ requires gas. */
+#define TARGET_EMBEDDED_PIC (target_flags & MASK_EMBEDDED_PIC)
+
/* Macro to define tables used to set the flags.
This is a list in braces of pairs in braces,
each pair being { "NAME", VALUE }
@@ -364,6 +368,8 @@ extern char *mktemp ();
{"no-half-pic", -MASK_HALF_PIC}, \
{"long-calls", MASK_LONG_CALLS}, \
{"no-long-calls", -MASK_LONG_CALLS}, \
+ {"embedded-pic", MASK_EMBEDDED_PIC}, \
+ {"no-embedded-pic", -MASK_EMBEDDED_PIC}, \
{"debug", MASK_DEBUG}, \
{"debuga", MASK_DEBUG_A}, \
{"debugb", MASK_DEBUG_B}, \
@@ -552,7 +558,8 @@ while (0)
%{ggdb:-g} %{ggdb0:-g0} %{ggdb1:-g1} %{ggdb2:-g2} %{ggdb3:-g3} \
%{gstabs:-g} %{gstabs0:-g0} %{gstabs1:-g1} %{gstabs2:-g2} %{gstabs3:-g3} \
%{gstabs+:-g} %{gstabs+0:-g0} %{gstabs+1:-g1} %{gstabs+2:-g2} %{gstabs+3:-g3} \
-%{gcoff:-g} %{gcoff0:-g0} %{gcoff1:-g1} %{gcoff2:-g2} %{gcoff3:-g3}"
+%{gcoff:-g} %{gcoff0:-g0} %{gcoff1:-g1} %{gcoff2:-g2} %{gcoff3:-g3} \
+%{membedded-pic}"
#else
/* not GAS */
@@ -568,7 +575,8 @@ while (0)
%{ggdb:-g} %{ggdb0:-g0} %{ggdb1:-g1} %{ggdb2:-g2} %{ggdb3:-g3} \
%{gstabs:-g} %{gstabs0:-g0} %{gstabs1:-g1} %{gstabs2:-g2} %{gstabs3:-g3} \
%{gstabs+:-g} %{gstabs+0:-g0} %{gstabs+1:-g1} %{gstabs+2:-g2} %{gstabs+3:-g3} \
-%{gcoff:-g} %{gcoff0:-g0} %{gcoff1:-g1} %{gcoff2:-g2} %{gcoff3:-g3}"
+%{gcoff:-g} %{gcoff0:-g0} %{gcoff1:-g1} %{gcoff2:-g2} %{gcoff3:-g3} \
+%{membedded-pic}"
#endif
#endif /* ASM_SPEC */
@@ -3375,12 +3383,33 @@ do { \
VALUE)
/* This is how to output an element of a case-vector that is relative.
- This is used for pc-relative code (e.g. when TARGET_ABICALLS). */
+ This is used for pc-relative code (e.g. when TARGET_ABICALLS or
+ TARGET_EMBEDDED_PIC). */
#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, VALUE, REL) \
- fprintf (STREAM, "\t%s\t$L%d\n", \
- TARGET_LONG64 ? ".gpdword" : ".gpword", \
- VALUE)
+do { \
+ if (TARGET_EMBEDDED_PIC) \
+ fprintf (STREAM, "\t%s\t$L%d-$LS%d\n", \
+ TARGET_LONG64 ? ".dword" : ".word", \
+ VALUE, REL); \
+ else \
+ fprintf (STREAM, "\t%s\t$L%d\n", \
+ TARGET_LONG64 ? ".gpdword" : ".gpword", \
+ VALUE); \
+} while (0)
+
+/* When generating embedded PIC code we want to put the jump table in
+ the .text section. In all other cases, we want to put the jump
+ table in the .rdata section. Unfortunately, we can't use
+ JUMP_TABLES_IN_TEXT_SECTION, because it is not conditional.
+ Instead, we use ASM_OUTPUT_CASE_LABEL to switch back to the .text
+ section if appropriate. */
+#define ASM_OUTPUT_CASE_LABEL(FILE, PREFIX, NUM, INSN) \
+do { \
+ if (TARGET_EMBEDDED_PIC) \
+ text_section (); \
+ ASM_OUTPUT_INTERNAL_LABEL (FILE, PREFIX, NUM); \
+} while (0)
/* This is how to output an assembler line
that says to advance the location counter
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 618fddb..be9e151 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -5410,6 +5410,90 @@ move\\t%0,%z4\\n\\
(set_attr "mode" "none")
(set_attr "length" "1")])
+;; Implement a switch statement when generating embedded PIC code.
+;; Switches are implemented by `tablejump' when not using -membedded-pic.
+
+(define_expand "casesi"
+ [(set (match_dup 5)
+ (minus:SI (match_operand:SI 0 "register_operand" "d")
+ (match_operand:SI 1 "arith_operand" "dI")))
+ (set (cc0)
+ (compare:CC (match_dup 5)
+ (match_operand:SI 2 "arith_operand" "")))
+ (set (pc)
+ (if_then_else (gtu (cc0)
+ (const_int 0))
+ (label_ref (match_operand 4 "" ""))
+ (pc)))
+ (parallel
+ [(set (pc)
+ (mem:SI (plus:SI (mult:SI (match_dup 5)
+ (const_int 4))
+ (label_ref (match_operand 3 "" "")))))
+ (clobber (match_scratch:SI 6 ""))
+ (clobber (reg:SI 31))])]
+ "TARGET_EMBEDDED_PIC"
+ "
+{
+ /* We need slightly different code for eight byte table entries. */
+ if (TARGET_LONG64)
+ abort ();
+
+ if (operands[0])
+ {
+ rtx reg = gen_reg_rtx (SImode);
+
+ /* The constraints should handle this, but they don't. */
+ operands[0] = force_reg (SImode, operands[0]);
+ if (! arith_operand (operands[1]))
+ operands[1] = force_reg (SImode, operands[1]);
+ if (! arith_operand (operands[2]))
+ operands[2] = force_reg (SImode, operands[2]);
+
+ /* If the index is too large, go to the default label. */
+ emit_insn (gen_subsi3 (reg, operands[0], operands[1]));
+ emit_insn (gen_cmpsi (reg, operands[2]));
+ emit_insn (gen_bgtu (operands[4]));
+
+ /* Do the PIC jump. */
+ emit_insn (gen_casesi_internal (reg, operands[3], gen_reg_rtx (SImode)));
+
+ DONE;
+ }
+}")
+
+;; An embedded PIC switch statement looks like this:
+;; bal $LS1
+;; sll $reg,$index,2
+;; $LS1:
+;; addu $reg,$reg,$31
+;; lw $reg,$L1-$LS1($reg)
+;; addu $reg,$reg,$31
+;; j $reg
+;; $L1:
+;; .word case1-$LS1
+;; .word case2-$LS1
+;; ...
+
+(define_insn "casesi_internal"
+ [(set (pc)
+ (mem:SI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "d")
+ (const_int 4))
+ (label_ref (match_operand 1 "" "")))))
+ (clobber (match_operand:SI 2 "register_operand" "d"))
+ (clobber (reg:SI 31))]
+ "TARGET_EMBEDDED_PIC"
+ "*
+{
+ output_asm_insn (\"%(bal\\t%S1\;sll\\t%0,2\\n%S1:\", operands);
+ output_asm_insn (\"addu\\t%0,%0,$31%)\", operands);
+ output_asm_insn (\"lw\\t%0,%1-%S1(%0)\;addu\\t%0,%0,$31\", operands);
+ return \"j\\t%0\";
+}"
+ [(set_attr "type" "jump")
+ (set_attr "mode" "none")
+ (set_attr "length" "6")])
+
;;
;; ....................