Commit 8cc9a5a5 by Edgar E. Iglesias Committed by Michael Eager

Add support for TLS in MicroBlaze

 * configure.ac: Add MicroBlaze TLS support detection.
 * configure: Regenerate.
 * config/microblaze/microblaze-protos.h: (microblaze_cannot_force_const_mem,
   microblaze_tls_referenced_p, symbol_mentioned_p,
   label_mentioned_p): Add prototypes.
 * config/microblaze/microblaze.c (microblaze_address_type): Add ADDRESS_TLS
   and tls_reloc address types.
   (microblaze_address_info): Add tls_reloc.
   (TARGET_HAVE_TLS): Define.
   (get_tls_get_addr, microblaze_tls_symbol_p, microblaze_tls_operand_p_1,
    microblaze_tls_referenced_p, microblaze_cannot_force_const_mem,
    symbol_mentioned_p, label_mentioned_p, tls_mentioned_p, load_tls_operand,
    microblaze_call_tls_get_addr, microblaze_legitimize_tls_address): New functions.
   (microblaze_classify_unspec): Handle UNSPEC_TLS.
   (get_base_reg): Use microblaze_tls_symbol_p.
   (microblaze_classify_address): Handle TLS.
   (microblaze_legitimate_pic_operand): Use symbol_mentioned_p, label_mentioned_p
    and microblaze_tls_referenced_p.
   (microblaze_legitimize_address): Handle TLS.
   (microblaze_address_insns): Handle ADDRESS_TLS.
   (pic_address_needs_scratch): Handle TLS.
   (print_operand_address): Handle TLS.
   (microblaze_expand_prologue): Check TLS_NEEDS_GOT.
   (microblaze_expand_move): Handle TLS.
   (microblaze_legitimate_constant_p): Check microblaze_cannot_force_const_mem
    and microblaze_tls_symbol_p.
   (TARGET_CANNOT_FORCE_CONST_MEM): Define.
 * config/microblaze/microblaze.h (TLS_NEEDS_GOT): Define
   (PIC_OFFSET_TABLE_REGNUM): Set.
 * config/microblaze/linux.h (TLS_NEEDS_GOT): Define.
 * config/microblaze/microblaze.md (UNSPEC_TLS): Define.
   (addsi3, movsi_internal2, movdf_internal): Update constraints
 * config/microblaze/predicates.md (arith_plus_operand): Define
   (move_operand): Redefine as move_src_operand, check microblaze_tls_referenced_p.



Co-Authored-By: David Holsgrove <david.holsgrove@xilinx.com>

From-SVN: r196659
parent a7137ee1
2013-03-14 Edgar E. Iglesias <edgar.iglesias@xilinx.com>
David Holsgrove <david.holsgrove@xilinx.com>
* configure.ac: Add MicroBlaze TLS support detection.
* configure: Regenerate.
* config/microblaze/microblaze-protos.h: (microblaze_cannot_force_const_mem,
microblaze_tls_referenced_p, symbol_mentioned_p, label_mentioned_p):
Add prototypes.
* config/microblaze/microblaze.c (microblaze_address_type): Add ADDRESS_TLS
and tls_reloc address types.
(microblaze_address_info): Add tls_reloc.
(TARGET_HAVE_TLS): Define.
(get_tls_get_addr, microblaze_tls_symbol_p, microblaze_tls_operand_p_1,
microblaze_tls_referenced_p, microblaze_cannot_force_const_mem,
symbol_mentioned_p, label_mentioned_p, tls_mentioned_p, load_tls_operand,
microblaze_call_tls_get_addr, microblaze_legitimize_tls_address): New functions.
(microblaze_classify_unspec): Handle UNSPEC_TLS.
(get_base_reg): Use microblaze_tls_symbol_p.
(microblaze_classify_address): Handle TLS.
(microblaze_legitimate_pic_operand): Use symbol_mentioned_p, label_mentioned_p
and microblaze_tls_referenced_p.
(microblaze_legitimize_address): Handle TLS.
(microblaze_address_insns): Handle ADDRESS_TLS.
(pic_address_needs_scratch): Handle TLS.
(print_operand_address): Handle TLS.
(microblaze_expand_prologue): Check TLS_NEEDS_GOT.
(microblaze_expand_move): Handle TLS.
(microblaze_legitimate_constant_p): Check microblaze_cannot_force_const_mem
and microblaze_tls_symbol_p.
(TARGET_CANNOT_FORCE_CONST_MEM): Define.
* config/microblaze/microblaze.h (TLS_NEEDS_GOT): Define
(PIC_OFFSET_TABLE_REGNUM): Set.
* config/microblaze/linux.h (TLS_NEEDS_GOT): Define.
* config/microblaze/microblaze.md (UNSPEC_TLS): Define.
(addsi3, movsi_internal2, movdf_internal): Update constraints
* config/microblaze/predicates.md (arith_plus_operand): Define
(move_operand): Redefine as move_src_operand, check microblaze_tls_referenced_p.
2013-03-14 Ian Bolton <ian.bolton@arm.com> 2013-03-14 Ian Bolton <ian.bolton@arm.com>
* config/aarch64/aarch64.md: (*and<mode>3nr_compare0): Use CC_NZ. * config/aarch64/aarch64.md: (*and<mode>3nr_compare0): Use CC_NZ.
......
...@@ -22,6 +22,9 @@ ...@@ -22,6 +22,9 @@
#undef TARGET_SUPPORTS_PIC #undef TARGET_SUPPORTS_PIC
#define TARGET_SUPPORTS_PIC 1 #define TARGET_SUPPORTS_PIC 1
#undef TLS_NEEDS_GOT
#define TLS_NEEDS_GOT 1
#define DYNAMIC_LINKER "/lib/ld.so.1" #define DYNAMIC_LINKER "/lib/ld.so.1"
#undef SUBTARGET_EXTRA_SPECS #undef SUBTARGET_EXTRA_SPECS
#define SUBTARGET_EXTRA_SPECS \ #define SUBTARGET_EXTRA_SPECS \
......
...@@ -50,6 +50,10 @@ extern void microblaze_declare_object (FILE *, const char *, const char *, ...@@ -50,6 +50,10 @@ extern void microblaze_declare_object (FILE *, const char *, const char *,
const char *, int); const char *, int);
extern void microblaze_asm_output_ident (const char *); extern void microblaze_asm_output_ident (const char *);
extern int microblaze_legitimate_pic_operand (rtx); extern int microblaze_legitimate_pic_operand (rtx);
extern bool microblaze_tls_referenced_p (rtx);
extern int symbol_mentioned_p (rtx);
extern int label_mentioned_p (rtx);
extern bool microblaze_cannot_force_const_mem (enum machine_mode, rtx);
#endif /* RTX_CODE */ #endif /* RTX_CODE */
/* Declare functions in microblaze-c.c. */ /* Declare functions in microblaze-c.c. */
......
...@@ -84,7 +84,8 @@ enum microblaze_address_type ...@@ -84,7 +84,8 @@ enum microblaze_address_type
ADDRESS_CONST_INT, ADDRESS_CONST_INT,
ADDRESS_SYMBOLIC, ADDRESS_SYMBOLIC,
ADDRESS_GOTOFF, ADDRESS_GOTOFF,
ADDRESS_PLT ADDRESS_PLT,
ADDRESS_TLS
}; };
/* Classifies symbols /* Classifies symbols
...@@ -98,6 +99,15 @@ enum microblaze_symbol_type ...@@ -98,6 +99,15 @@ enum microblaze_symbol_type
SYMBOL_TYPE_GENERAL SYMBOL_TYPE_GENERAL
}; };
/* TLS Address Type. */
enum tls_reloc {
TLS_GD,
TLS_LDM,
TLS_DTPREL,
TLS_IE,
TLS_LE
};
/* Classification of a MicroBlaze address. */ /* Classification of a MicroBlaze address. */
struct microblaze_address_info struct microblaze_address_info
{ {
...@@ -108,6 +118,7 @@ struct microblaze_address_info ...@@ -108,6 +118,7 @@ struct microblaze_address_info
rtx offset; /* Contains valid values on ADDRESS_CONST_INT and ADDRESS_REG. */ rtx offset; /* Contains valid values on ADDRESS_CONST_INT and ADDRESS_REG. */
rtx symbol; /* Contains valid values on ADDRESS_SYMBOLIC. */ rtx symbol; /* Contains valid values on ADDRESS_SYMBOLIC. */
enum microblaze_symbol_type symbol_type; enum microblaze_symbol_type symbol_type;
enum tls_reloc tls_type;
}; };
/* Structure to be filled in by compute_frame_size with register /* Structure to be filled in by compute_frame_size with register
...@@ -215,6 +226,11 @@ static int microblaze_interrupt_function_p (tree); ...@@ -215,6 +226,11 @@ static int microblaze_interrupt_function_p (tree);
section *sdata2_section; section *sdata2_section;
#ifdef HAVE_AS_TLS
#undef TARGET_HAVE_TLS
#define TARGET_HAVE_TLS true
#endif
/* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */ /* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */
static bool static bool
microblaze_const_double_ok (rtx op, enum machine_mode mode) microblaze_const_double_ok (rtx op, enum machine_mode mode)
...@@ -287,6 +303,9 @@ simple_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) ...@@ -287,6 +303,9 @@ simple_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
plus0 = XEXP (addr, 0); plus0 = XEXP (addr, 0);
plus1 = XEXP (addr, 1); plus1 = XEXP (addr, 1);
if (GET_CODE (plus0) != REG)
return 0;
if (GET_CODE (plus0) == REG && GET_CODE (plus1) == CONST_INT if (GET_CODE (plus0) == REG && GET_CODE (plus1) == CONST_INT
&& SMALL_INT (plus1)) && SMALL_INT (plus1))
{ {
...@@ -386,6 +405,225 @@ microblaze_valid_base_register_p (rtx x, ...@@ -386,6 +405,225 @@ microblaze_valid_base_register_p (rtx x,
&& microblaze_regno_ok_for_base_p (REGNO (x), strict)); && microblaze_regno_ok_for_base_p (REGNO (x), strict));
} }
/* Build the SYMBOL_REF for __tls_get_addr. */
static GTY(()) rtx tls_get_addr_libfunc;
static rtx
get_tls_get_addr (void)
{
if (!tls_get_addr_libfunc)
tls_get_addr_libfunc = init_one_libfunc ("__tls_get_addr");
return tls_get_addr_libfunc;
}
/* Return TRUE if X is a thread-local symbol. */
bool
microblaze_tls_symbol_p (rtx x)
{
if (!TARGET_HAVE_TLS)
return false;
if (GET_CODE (x) != SYMBOL_REF)
return false;
return SYMBOL_REF_TLS_MODEL (x) != 0;
}
static int
microblaze_tls_operand_p_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
{
if (GET_CODE (*x) == SYMBOL_REF)
return SYMBOL_REF_TLS_MODEL (*x) != 0;
/* Don't recurse into UNSPEC_TLS looking for TLS symbols; these are
TLS offsets, not real symbol references. */
if (GET_CODE (*x) == UNSPEC && XINT (*x, 1) == UNSPEC_TLS)
return -1;
return 0;
}
/* Return TRUE if X contains any TLS symbol references. */
bool
microblaze_tls_referenced_p (rtx x)
{
if (!TARGET_HAVE_TLS)
return false;
return for_each_rtx (&x, microblaze_tls_operand_p_1, NULL);
}
bool
microblaze_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
{
return microblaze_tls_referenced_p(x);
}
/* Return TRUE if X references a SYMBOL_REF. */
int
symbol_mentioned_p (rtx x)
{
const char * fmt;
int i;
if (GET_CODE (x) == SYMBOL_REF)
return 1;
/* UNSPEC entries for a symbol include the SYMBOL_REF, but they
are constant offsets, not symbols. */
if (GET_CODE (x) == UNSPEC)
return 0;
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (symbol_mentioned_p (XVECEXP (x, i, j)))
return 1;
}
else if (fmt[i] == 'e' && symbol_mentioned_p (XEXP (x, i)))
return 1;
}
return 0;
}
/* Return TRUE if X references a LABEL_REF. */
int
label_mentioned_p (rtx x)
{
const char * fmt;
int i;
if (GET_CODE (x) == LABEL_REF)
return 1;
/* UNSPEC entries for a symbol include a LABEL_REF for the referencing
instruction, but they are constant offsets, not symbols. */
if (GET_CODE (x) == UNSPEC)
return 0;
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'E')
{
int j;
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (label_mentioned_p (XVECEXP (x, i, j)))
return 1;
}
else if (fmt[i] == 'e' && label_mentioned_p (XEXP (x, i)))
return 1;
}
return 0;
}
int
tls_mentioned_p (rtx x)
{
switch (GET_CODE (x))
{
case CONST:
return tls_mentioned_p (XEXP (x, 0));
case UNSPEC:
if (XINT (x, 1) == UNSPEC_TLS)
return 1;
default:
return 0;
}
}
static rtx
load_tls_operand (rtx x, rtx reg)
{
rtx tmp;
if (reg == NULL_RTX)
reg = gen_reg_rtx (Pmode);
tmp = gen_rtx_CONST (Pmode, x);
emit_insn (gen_rtx_SET (VOIDmode, reg,
gen_rtx_PLUS (Pmode, pic_offset_table_rtx, tmp)));
return reg;
}
static rtx
microblaze_call_tls_get_addr (rtx x, rtx reg, rtx *valuep, int reloc)
{
rtx insns, tls_entry;
df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
start_sequence ();
tls_entry = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (reloc)),
UNSPEC_TLS);
reg = load_tls_operand (tls_entry, reg);
*valuep = emit_library_call_value (get_tls_get_addr (), NULL_RTX,
LCT_PURE, /* LCT_CONST? */
Pmode, 1, reg, Pmode);
insns = get_insns ();
end_sequence ();
return insns;
}
rtx
microblaze_legitimize_tls_address(rtx x, rtx reg)
{
rtx dest, insns, ret, eqv, addend;
enum tls_model model;
model = SYMBOL_REF_TLS_MODEL (x);
switch (model)
{
case TLS_MODEL_LOCAL_DYNAMIC:
case TLS_MODEL_GLOBAL_DYNAMIC:
case TLS_MODEL_INITIAL_EXEC:
insns = microblaze_call_tls_get_addr (x, reg, &ret, TLS_GD);
dest = gen_reg_rtx (Pmode);
emit_libcall_block (insns, dest, ret, x);
break;
case TLS_MODEL_LOCAL_EXEC:
insns = microblaze_call_tls_get_addr (x, reg, &ret, TLS_LDM);
/* Attach a unique REG_EQUIV, to allow the RTL optimizers to
share the LDM result with other LD model accesses. */
eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx), UNSPEC_TLS);
dest = gen_reg_rtx (Pmode);
emit_libcall_block (insns, dest, ret, eqv);
/* Load the addend. */
addend = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (TLS_DTPREL)),
UNSPEC_TLS);
addend = force_reg (SImode, gen_rtx_CONST (SImode, addend));
dest = gen_rtx_PLUS (Pmode, dest, addend);
break;
default:
gcc_unreachable ();
}
return dest;
}
static bool static bool
microblaze_classify_unspec (struct microblaze_address_info *info, rtx x) microblaze_classify_unspec (struct microblaze_address_info *info, rtx x)
{ {
...@@ -401,6 +639,11 @@ microblaze_classify_unspec (struct microblaze_address_info *info, rtx x) ...@@ -401,6 +639,11 @@ microblaze_classify_unspec (struct microblaze_address_info *info, rtx x)
{ {
info->type = ADDRESS_PLT; info->type = ADDRESS_PLT;
} }
else if (XINT (x, 1) == UNSPEC_TLS)
{
info->type = ADDRESS_TLS;
info->tls_type = tls_reloc INTVAL(XVECEXP(x, 0, 1));
}
else else
{ {
return false; return false;
...@@ -431,7 +674,12 @@ static int ...@@ -431,7 +674,12 @@ static int
get_base_reg (rtx x) get_base_reg (rtx x)
{ {
tree decl; tree decl;
int base_reg = (flag_pic ? MB_ABI_PIC_ADDR_REGNUM : MB_ABI_BASE_REGNUM); int base_reg;
if (!flag_pic || microblaze_tls_symbol_p(x))
base_reg = MB_ABI_BASE_REGNUM;
else if (flag_pic)
base_reg = MB_ABI_PIC_ADDR_REGNUM;
if (TARGET_XLGPOPT if (TARGET_XLGPOPT
&& GET_CODE (x) == SYMBOL_REF && GET_CODE (x) == SYMBOL_REF
...@@ -509,28 +757,61 @@ microblaze_classify_address (struct microblaze_address_info *info, rtx x, ...@@ -509,28 +757,61 @@ microblaze_classify_address (struct microblaze_address_info *info, rtx x,
} }
else if (GET_CODE (xplus1) == UNSPEC) else if (GET_CODE (xplus1) == UNSPEC)
{ {
/* Need offsettable address. */
if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
return false;
return microblaze_classify_unspec (info, xplus1); return microblaze_classify_unspec (info, xplus1);
} }
else if ((GET_CODE (xplus1) == SYMBOL_REF || else if ((GET_CODE (xplus1) == SYMBOL_REF ||
GET_CODE (xplus1) == LABEL_REF) && flag_pic == 2) GET_CODE (xplus1) == LABEL_REF))
{
return false;
}
else if (GET_CODE (xplus1) == SYMBOL_REF ||
GET_CODE (xplus1) == LABEL_REF ||
GET_CODE (xplus1) == CONST)
{ {
if (GET_CODE (XEXP (xplus1, 0)) == UNSPEC) if (flag_pic == 2 || microblaze_tls_symbol_p(xplus1))
return microblaze_classify_unspec (info, XEXP (xplus1, 0)); return false;
else if (flag_pic == 2)
{
return false;
}
info->type = ADDRESS_SYMBOLIC; info->type = ADDRESS_SYMBOLIC;
info->symbol = xplus1; info->symbol = xplus1;
info->symbol_type = SYMBOL_TYPE_GENERAL; info->symbol_type = SYMBOL_TYPE_GENERAL;
return true; return true;
} }
else if (GET_CODE (xplus1) == CONST)
{
rtx xconst0 = XEXP(xplus1, 0);
/* base + unspec. */
if (GET_CODE (xconst0) == UNSPEC)
{
/* Need offsettable address. */
if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
return false;
return microblaze_classify_unspec(info, xconst0);
}
/* for (plus x const_int) just look at x. */
if (GET_CODE (xconst0) == PLUS
&& GET_CODE (XEXP (xconst0, 1)) == CONST_INT
&& SMALL_INT (XEXP (xconst0, 1)))
{
/* This is ok as info->symbol is set to xplus1 the full
const-expression below. */
xconst0 = XEXP (xconst0, 0);
}
if (GET_CODE (xconst0) == SYMBOL_REF
|| GET_CODE (xconst0) == LABEL_REF)
{
if (flag_pic == 2 || microblaze_tls_symbol_p(xconst0))
return false;
info->type = ADDRESS_SYMBOLIC;
info->symbol = xplus1;
info->symbol_type = SYMBOL_TYPE_GENERAL;
return true;
}
/* Not base + symbol || base + UNSPEC. */
return false;
}
else if (GET_CODE (xplus1) == REG else if (GET_CODE (xplus1) == REG
&& microblaze_valid_index_register_p (xplus1, mode, && microblaze_valid_index_register_p (xplus1, mode,
strict) strict)
...@@ -562,13 +843,20 @@ microblaze_classify_address (struct microblaze_address_info *info, rtx x, ...@@ -562,13 +843,20 @@ microblaze_classify_address (struct microblaze_address_info *info, rtx x,
if (GET_CODE (x) == CONST) if (GET_CODE (x) == CONST)
{ {
return !(flag_pic && pic_address_needs_scratch (x)); if (GET_CODE (XEXP (x, 0)) == UNSPEC)
} {
else if (flag_pic == 2) info->regA = gen_rtx_raw_REG (mode,
{ get_base_reg (XVECEXP (XEXP (x,0), 0, 0)));
return false; return microblaze_classify_unspec (info, XEXP (x, 0));
}
return !(flag_pic && pic_address_needs_scratch (x));
} }
if (flag_pic == 2)
return false;
else if (microblaze_tls_symbol_p(x))
return false;
return true; return true;
} }
...@@ -616,11 +904,10 @@ microblaze_valid_pic_const (rtx x) ...@@ -616,11 +904,10 @@ microblaze_valid_pic_const (rtx x)
int int
microblaze_legitimate_pic_operand (rtx x) microblaze_legitimate_pic_operand (rtx x)
{ {
struct microblaze_address_info addr; if (flag_pic == 2 && (symbol_mentioned_p(x) || label_mentioned_p(x)))
if (pic_address_needs_scratch (x))
return 0; return 0;
if (!microblaze_valid_pic_const(x))
if (microblaze_tls_referenced_p(x))
return 0; return 0;
return 1; return 1;
...@@ -706,7 +993,7 @@ microblaze_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, ...@@ -706,7 +993,7 @@ microblaze_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
return result; return result;
} }
if (code0 == REG && REG_OK_FOR_BASE_P (xplus0) && flag_pic == 2) if (code0 == REG && REG_OK_FOR_BASE_P (xplus0))
{ {
if (reload_in_progress) if (reload_in_progress)
df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
...@@ -717,26 +1004,58 @@ microblaze_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, ...@@ -717,26 +1004,58 @@ microblaze_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
} }
if (code1 == SYMBOL_REF) if (code1 == SYMBOL_REF)
{ {
result = if (microblaze_tls_symbol_p(xplus1))
gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xplus1), UNSPEC_GOTOFF); {
result = gen_rtx_CONST (Pmode, result); rtx tls_ref, reg;
result = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, result); reg = gen_reg_rtx (Pmode);
result = gen_const_mem (Pmode, result);
result = gen_rtx_PLUS (Pmode, xplus0, result); tls_ref = microblaze_legitimize_tls_address (xplus1,
return result; NULL_RTX);
emit_move_insn (reg, tls_ref);
result = gen_rtx_PLUS (Pmode, xplus0, reg);
return result;
}
else if (flag_pic == 2)
{
rtx pic_ref, reg;
reg = gen_reg_rtx (Pmode);
pic_ref = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xplus1),
UNSPEC_GOTOFF);
pic_ref = gen_rtx_CONST (Pmode, pic_ref);
pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, pic_ref);
pic_ref = gen_const_mem (Pmode, pic_ref);
emit_move_insn (reg, pic_ref);
result = gen_rtx_PLUS (Pmode, xplus0, reg);
return result;
}
} }
} }
} }
if (GET_CODE (xinsn) == SYMBOL_REF) if (GET_CODE (xinsn) == SYMBOL_REF)
{ {
if (reload_in_progress) rtx reg;
df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); if (microblaze_tls_symbol_p(xinsn))
result = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xinsn), UNSPEC_GOTOFF); {
result = gen_rtx_CONST (Pmode, result); reg = microblaze_legitimize_tls_address (xinsn, NULL_RTX);
result = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, result); }
result = gen_const_mem (Pmode, result); else
return result; {
rtx pic_ref;
if (reload_in_progress)
df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
pic_ref = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, xinsn), UNSPEC_GOTOFF);
pic_ref = gen_rtx_CONST (Pmode, pic_ref);
pic_ref = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, pic_ref);
pic_ref = gen_const_mem (Pmode, pic_ref);
reg = pic_ref;
}
return reg;
} }
return x; return x;
...@@ -1061,10 +1380,22 @@ microblaze_address_insns (rtx x, enum machine_mode mode) ...@@ -1061,10 +1380,22 @@ microblaze_address_insns (rtx x, enum machine_mode mode)
else else
return 2; return 2;
case ADDRESS_REG_INDEX: case ADDRESS_REG_INDEX:
case ADDRESS_SYMBOLIC:
return 1; return 1;
case ADDRESS_SYMBOLIC:
case ADDRESS_GOTOFF: case ADDRESS_GOTOFF:
return 2; return 2;
case ADDRESS_TLS:
switch (addr.tls_type)
{
case TLS_GD:
return 2;
case TLS_LDM:
return 2;
case TLS_DTPREL:
return 1;
default :
abort();
}
default: default:
break; break;
} }
...@@ -1088,13 +1419,18 @@ microblaze_address_cost (rtx addr, enum machine_mode mode ATTRIBUTE_UNUSED, ...@@ -1088,13 +1419,18 @@ microblaze_address_cost (rtx addr, enum machine_mode mode ATTRIBUTE_UNUSED,
int int
pic_address_needs_scratch (rtx x) pic_address_needs_scratch (rtx x)
{ {
/* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */ if (GET_CODE (x) == CONST && GET_CODE (XEXP (x,0)) == PLUS)
if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS {
&& GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF rtx p0, p1;
&& GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
&& (flag_pic == 2 || !SMALL_INT (XEXP (XEXP (x, 0), 1))))
return 1;
p0 = XEXP (XEXP (x, 0), 0);
p1 = XEXP (XEXP (x, 0), 1);
if ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
&& (GET_CODE (p1) == CONST_INT)
&& (flag_pic == 2 || microblaze_tls_symbol_p (p0) || !SMALL_INT (p1)))
return 1;
}
return 0; return 0;
} }
...@@ -1918,6 +2254,7 @@ print_operand (FILE * file, rtx op, int letter) ...@@ -1918,6 +2254,7 @@ print_operand (FILE * file, rtx op, int letter)
case ADDRESS_CONST_INT: case ADDRESS_CONST_INT:
case ADDRESS_SYMBOLIC: case ADDRESS_SYMBOLIC:
case ADDRESS_GOTOFF: case ADDRESS_GOTOFF:
case ADDRESS_TLS:
fputs ("i", file); fputs ("i", file);
break; break;
case ADDRESS_REG_INDEX: case ADDRESS_REG_INDEX:
...@@ -2034,10 +2371,19 @@ print_operand (FILE * file, rtx op, int letter) ...@@ -2034,10 +2371,19 @@ print_operand (FILE * file, rtx op, int letter)
else if (letter == 't') else if (letter == 't')
fputs (code == EQ ? "t" : "f", file); fputs (code == EQ ? "t" : "f", file);
else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG) else if (code == CONST
&& ((GET_CODE (XEXP (op, 0)) == REG)
|| (GET_CODE (XEXP (op, 0)) == UNSPEC)))
{ {
print_operand (file, XEXP (op, 0), letter); print_operand (file, XEXP (op, 0), letter);
} }
else if (code == CONST
&& (GET_CODE (XEXP (op, 0)) == PLUS)
&& (GET_CODE (XEXP (XEXP (op, 0), 0)) == REG)
&& (GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST))
{
print_operand_address (file, XEXP (op, 0));
}
else if (letter == 'm') else if (letter == 'm')
fprintf (file, HOST_WIDE_INT_PRINT_DEC, (1L << INTVAL (op))); fprintf (file, HOST_WIDE_INT_PRINT_DEC, (1L << INTVAL (op)));
else else
...@@ -2098,6 +2444,7 @@ print_operand_address (FILE * file, rtx addr) ...@@ -2098,6 +2444,7 @@ print_operand_address (FILE * file, rtx addr)
case ADDRESS_SYMBOLIC: case ADDRESS_SYMBOLIC:
case ADDRESS_GOTOFF: case ADDRESS_GOTOFF:
case ADDRESS_PLT: case ADDRESS_PLT:
case ADDRESS_TLS:
if (info.regA) if (info.regA)
fprintf (file, "%s,", reg_names[REGNO (info.regA)]); fprintf (file, "%s,", reg_names[REGNO (info.regA)]);
output_addr_const (file, info.symbol); output_addr_const (file, info.symbol);
...@@ -2109,6 +2456,24 @@ print_operand_address (FILE * file, rtx addr) ...@@ -2109,6 +2456,24 @@ print_operand_address (FILE * file, rtx addr)
{ {
fputs ("@PLT", file); fputs ("@PLT", file);
} }
else if (type == ADDRESS_TLS)
{
switch (info.tls_type)
{
case TLS_GD:
fputs ("@TLSGD", file);
break;
case TLS_LDM:
fputs ("@TLSLDM", file);
break;
case TLS_DTPREL:
fputs ("@TLSDTPREL", file);
break;
default :
abort();
break;
}
}
break; break;
case ADDRESS_INVALID: case ADDRESS_INVALID:
fatal_insn ("invalid address", addr); fatal_insn ("invalid address", addr);
...@@ -2471,7 +2836,8 @@ microblaze_expand_prologue (void) ...@@ -2471,7 +2836,8 @@ microblaze_expand_prologue (void)
} }
} }
if (flag_pic == 2 && df_regs_ever_live_p (MB_ABI_PIC_ADDR_REGNUM)) if ((flag_pic == 2 || TLS_NEEDS_GOT )
&& df_regs_ever_live_p (MB_ABI_PIC_ADDR_REGNUM))
{ {
SET_REGNO (pic_offset_table_rtx, MB_ABI_PIC_ADDR_REGNUM); SET_REGNO (pic_offset_table_rtx, MB_ABI_PIC_ADDR_REGNUM);
emit_insn (gen_set_got (pic_offset_table_rtx)); /* setting GOT. */ emit_insn (gen_set_got (pic_offset_table_rtx)); /* setting GOT. */
...@@ -2701,83 +3067,62 @@ expand_pic_symbol_ref (enum machine_mode mode ATTRIBUTE_UNUSED, rtx op) ...@@ -2701,83 +3067,62 @@ expand_pic_symbol_ref (enum machine_mode mode ATTRIBUTE_UNUSED, rtx op)
bool bool
microblaze_expand_move (enum machine_mode mode, rtx operands[]) microblaze_expand_move (enum machine_mode mode, rtx operands[])
{ {
rtx op0, op1;
op0 = operands[0];
op1 = operands[1];
if (!register_operand (op0, SImode)
&& !register_operand (op1, SImode)
&& (GET_CODE (op1) != CONST_INT || INTVAL (op1) != 0))
{
rtx temp = force_reg (SImode, op1);
emit_move_insn (op0, temp);
return true;
}
/* If operands[1] is a constant address invalid for pic, then we need to /* If operands[1] is a constant address invalid for pic, then we need to
handle it just like LEGITIMIZE_ADDRESS does. */ handle it just like LEGITIMIZE_ADDRESS does. */
if (flag_pic) if (GET_CODE (op1) == SYMBOL_REF || GET_CODE (op1) == LABEL_REF)
{ {
if (GET_CODE (operands[0]) == MEM) rtx result;
if (microblaze_tls_symbol_p(op1))
{ {
rtx addr = XEXP (operands[0], 0); result = microblaze_legitimize_tls_address (op1, NULL_RTX);
if (GET_CODE (addr) == SYMBOL_REF) emit_move_insn (op0, result);
{ return true;
rtx ptr_reg, result;
if (reload_in_progress)
df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
addr = expand_pic_symbol_ref (mode, addr);
ptr_reg = gen_reg_rtx (Pmode);
emit_move_insn (ptr_reg, addr);
result = gen_rtx_MEM (mode, ptr_reg);
operands[0] = result;
}
} }
if (GET_CODE (operands[1]) == SYMBOL_REF else if (flag_pic)
|| GET_CODE (operands[1]) == LABEL_REF)
{ {
rtx result;
if (reload_in_progress) if (reload_in_progress)
df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
result = expand_pic_symbol_ref (mode, operands[1]); result = expand_pic_symbol_ref (mode, op1);
if (GET_CODE (operands[0]) != REG) emit_move_insn (op0, result);
{
rtx ptr_reg = gen_reg_rtx (Pmode);
emit_move_insn (ptr_reg, result);
emit_move_insn (operands[0], ptr_reg);
}
else
{
emit_move_insn (operands[0], result);
}
return true; return true;
} }
else if (GET_CODE (operands[1]) == MEM && }
GET_CODE (XEXP (operands[1], 0)) == SYMBOL_REF) /* Handle Case of (const (plus symbol const_int)). */
{ if (GET_CODE (op1) == CONST && GET_CODE (XEXP (op1,0)) == PLUS)
rtx result; {
rtx ptr_reg; rtx p0, p1;
if (reload_in_progress)
df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
result = expand_pic_symbol_ref (mode, XEXP (operands[1], 0));
ptr_reg = gen_reg_rtx (Pmode); p0 = XEXP (XEXP (op1, 0), 0);
p1 = XEXP (XEXP (op1, 0), 1);
emit_move_insn (ptr_reg, result); if ((GET_CODE (p1) == CONST_INT)
result = gen_rtx_MEM (mode, ptr_reg); && ((GET_CODE (p0) == UNSPEC)
emit_move_insn (operands[0], result); || ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
return true; && (flag_pic == 2 || microblaze_tls_symbol_p (p0)
} || !SMALL_INT (p1)))))
else if (pic_address_needs_scratch (operands[1]))
{ {
rtx temp = force_reg (SImode, XEXP (XEXP (operands[1], 0), 0)); rtx temp = force_reg (SImode, p0);
rtx temp2 = XEXP (XEXP (operands[1], 0), 1); rtx temp2 = p1;
if (reload_in_progress) if (flag_pic && reload_in_progress)
df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true); df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
emit_move_insn (operands[0], gen_rtx_PLUS (SImode, temp, temp2)); emit_move_insn (op0, gen_rtx_PLUS (SImode, temp, temp2));
return true; return true;
} }
} }
if ((reload_in_progress | reload_completed) == 0
&& !register_operand (operands[0], SImode)
&& !register_operand (operands[1], SImode)
&& (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0))
{
rtx temp = force_reg (SImode, operands[1]);
emit_move_insn (operands[0], temp);
return true;
}
return false; return false;
} }
...@@ -3048,10 +3393,40 @@ microblaze_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link, ...@@ -3048,10 +3393,40 @@ microblaze_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
At present, GAS doesn't understand li.[sd], so don't allow it At present, GAS doesn't understand li.[sd], so don't allow it
to be generated at present. */ to be generated at present. */
static bool static bool
microblaze_legitimate_constant_p (enum machine_mode mode, rtx x) microblaze_legitimate_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
{ {
return GET_CODE (x) != CONST_DOUBLE || microblaze_const_double_ok (x, mode);
if (microblaze_cannot_force_const_mem(mode, x))
return false;
if (GET_CODE (x) == CONST_DOUBLE)
{
return microblaze_const_double_ok (x, GET_MODE (x));
}
/* Handle Case of (const (plus unspec const_int)). */
if (GET_CODE (x) == CONST && GET_CODE (XEXP (x,0)) == PLUS)
{
rtx p0, p1;
p0 = XEXP (XEXP (x, 0), 0);
p1 = XEXP (XEXP (x, 0), 1);
if (GET_CODE(p1) == CONST_INT)
{
/* Const offset from UNSPEC is not supported. */
if ((GET_CODE (p0) == UNSPEC))
return false;
if ((GET_CODE (p0) == SYMBOL_REF || GET_CODE (p0) == LABEL_REF)
&& (microblaze_tls_symbol_p (p0) || !SMALL_INT (p1)))
return false;
}
}
return true;
} }
#undef TARGET_ENCODE_SECTION_INFO #undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO microblaze_encode_section_info #define TARGET_ENCODE_SECTION_INFO microblaze_encode_section_info
...@@ -3068,6 +3443,9 @@ microblaze_legitimate_constant_p (enum machine_mode mode, rtx x) ...@@ -3068,6 +3443,9 @@ microblaze_legitimate_constant_p (enum machine_mode mode, rtx x)
#undef TARGET_RTX_COSTS #undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS microblaze_rtx_costs #define TARGET_RTX_COSTS microblaze_rtx_costs
#undef TARGET_CANNOT_FORCE_CONST_MEM
#define TARGET_CANNOT_FORCE_CONST_MEM microblaze_cannot_force_const_mem
#undef TARGET_ADDRESS_COST #undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST microblaze_address_cost #define TARGET_ADDRESS_COST microblaze_address_cost
......
...@@ -65,6 +65,9 @@ extern enum pipeline_type microblaze_pipe; ...@@ -65,6 +65,9 @@ extern enum pipeline_type microblaze_pipe;
/* The default is to support PIC. */ /* The default is to support PIC. */
#define TARGET_SUPPORTS_PIC 1 #define TARGET_SUPPORTS_PIC 1
/* The default is to not need GOT for TLS. */
#define TLS_NEEDS_GOT 0
/* What is the default setting for -mcpu= . We set it to v4.00.a even though /* What is the default setting for -mcpu= . We set it to v4.00.a even though
we are actually ahead. This is safest version that has generate code we are actually ahead. This is safest version that has generate code
compatible for the original ISA */ compatible for the original ISA */
...@@ -326,9 +329,7 @@ extern char microblaze_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER]; ...@@ -326,9 +329,7 @@ extern char microblaze_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
#define NO_FUNCTION_CSE 1 #define NO_FUNCTION_CSE 1
#define PIC_OFFSET_TABLE_REGNUM \ #define PIC_OFFSET_TABLE_REGNUM (GP_REG_FIRST + MB_ABI_PIC_ADDR_REGNUM)
(flag_pic ? (GP_REG_FIRST + MB_ABI_PIC_ADDR_REGNUM) : \
INVALID_REGNUM)
enum reg_class enum reg_class
{ {
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
(UNSPEC_PLT 103) ;; jump table (UNSPEC_PLT 103) ;; jump table
(UNSPEC_CMP 104) ;; signed compare (UNSPEC_CMP 104) ;; signed compare
(UNSPEC_CMPU 105) ;; unsigned compare (UNSPEC_CMPU 105) ;; unsigned compare
(UNSPEC_TLS 106) ;; jump table
]) ])
...@@ -459,7 +460,7 @@ ...@@ -459,7 +460,7 @@
(define_insn "addsi3" (define_insn "addsi3"
[(set (match_operand:SI 0 "register_operand" "=d,d,d") [(set (match_operand:SI 0 "register_operand" "=d,d,d")
(plus:SI (match_operand:SI 1 "reg_or_0_operand" "%dJ,dJ,dJ") (plus:SI (match_operand:SI 1 "reg_or_0_operand" "%dJ,dJ,dJ")
(match_operand:SI 2 "arith_operand" "d,I,i")))] (match_operand:SI 2 "arith_plus_operand" "d,I,i")))]
"" ""
"@ "@
addk\t%0,%z1,%2 addk\t%0,%z1,%2
...@@ -892,8 +893,8 @@ ...@@ -892,8 +893,8 @@
(define_insn "*movdi_internal" (define_insn "*movdi_internal"
[(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,R,m") [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,R,o")
(match_operand:DI 1 "general_operand" " d,i,J,R,m,d,d"))] (match_operand:DI 1 "general_operand" " d,i,J,R,o,d,d"))]
"" ""
{ {
switch (which_alternative) switch (which_alternative)
...@@ -999,13 +1000,9 @@ ...@@ -999,13 +1000,9 @@
(set_attr "length" "4")]) (set_attr "length" "4")])
(define_insn "*movsi_internal2" (define_insn "*movsi_internal2"
[(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d, d,d,R, T") [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d, d,d,R,m")
(match_operand:SI 1 "move_operand" " d,I,Mnis,R,m,dJ,dJ"))] (match_operand:SI 1 "move_src_operand" " d,I,Mnis,R,m,dJ,dJ"))]
"(register_operand (operands[0], SImode) ""
|| register_operand (operands[1], SImode)
|| (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))
&& (flag_pic != 2 || (GET_CODE (operands[1]) != SYMBOL_REF
&& GET_CODE (operands[1]) != LABEL_REF))"
"@ "@
addk\t%0,%1,r0 addk\t%0,%1,r0
addik\t%0,r0,%1\t# %X1 addik\t%0,r0,%1\t# %X1
...@@ -1196,7 +1193,7 @@ ...@@ -1196,7 +1193,7 @@
;; Applies to both TARGET_SOFT_FLOAT and TARGET_HARD_FLOAT ;; Applies to both TARGET_SOFT_FLOAT and TARGET_HARD_FLOAT
;; ;;
(define_insn "*movdf_internal" (define_insn "*movdf_internal"
[(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,d,d,To") [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,d,d,o")
(match_operand:DF 1 "general_operand" "dG,o,F,T,d"))] (match_operand:DF 1 "general_operand" "dG,o,F,T,d"))]
"" ""
{ {
......
...@@ -30,6 +30,52 @@ ...@@ -30,6 +30,52 @@
(and (match_code "const_int,const_double") (and (match_code "const_int,const_double")
(match_test "LARGE_INT (op)")))) (match_test "LARGE_INT (op)"))))
(define_predicate "arith_plus_operand"
(match_operand 0 "general_operand")
{
switch (GET_CODE (op))
{
default:
return 0;
case CONST_INT:
case REG:
return 1;
case SYMBOL_REF:
case LABEL_REF:
if (flag_pic || microblaze_tls_referenced_p(op))
return 0;
return 1;
case CONST:
{
rtx const0;
const0 = XEXP (op, 0);
switch (GET_CODE(const0))
{
default:
return 0;
case UNSPEC :
return 1;
case PLUS :
{
rtx p0, p1;
p0 = XEXP (const0, 0);
p1 = XEXP (const0, 1);
if ((GET_CODE(p0) == SYMBOL_REF
|| GET_CODE (p0) == LABEL_REF)
&& GET_CODE(p1) == CONST_INT)
{
return arith_plus_operand (p0, GET_MODE(p0));
}
}
}
}
}
return 0;
})
(define_predicate "const_0_operand" (define_predicate "const_0_operand"
(and (match_code "const_int,const_double") (and (match_code "const_int,const_double")
(match_test "op == CONST0_RTX (GET_MODE (op))"))) (match_test "op == CONST0_RTX (GET_MODE (op))")))
...@@ -54,14 +100,21 @@ ...@@ -54,14 +100,21 @@
(match_test "GET_CODE (op) == REG || GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST_INT"))) (match_test "GET_CODE (op) == REG || GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST_INT")))
;; Return if OPERAND is valid as a source operand for a move instruction. ;; Return if OPERAND is valid as a source operand for a move instruction.
(define_predicate "move_operand" (define_predicate "move_src_operand"
(and ( (and (
not ( not (
and (match_code "plus") and (match_code "plus")
(not (match_test "(GET_CODE (XEXP (op, 0)) == REG) ^ (GET_CODE (XEXP (op,1)) == REG)")) (not (match_test "(GET_CODE (XEXP (op, 0)) == REG) ^ (GET_CODE (XEXP (op,1)) == REG)"))
) )
) )
(match_operand 0 "general_operand"))) (match_operand 0 "general_operand"))
{
if (microblaze_tls_referenced_p(op)
|| (flag_pic && (symbol_mentioned_p(op) || label_mentioned_p(op))))
return false;
return true;
})
;; Test for valid PIC call operand ;; Test for valid PIC call operand
(define_predicate "call_insn_plt_operand" (define_predicate "call_insn_plt_operand"
......
...@@ -23205,6 +23205,18 @@ foo: data8 25 ...@@ -23205,6 +23205,18 @@ foo: data8 25
tls_first_minor=13 tls_first_minor=13
tls_as_opt=--fatal-warnings tls_as_opt=--fatal-warnings
;; ;;
microblaze*-*-*)
conftest_s='
.section .tdata,"awT",@progbits
x:
.word 2
.text
addik r5,r20,x@TLSGD
addik r5,r20,x@TLSLDM'
tls_first_major=2
tls_first_minor=20
tls_as_opt='--fatal-warnings'
;;
mips*-*-*) mips*-*-*)
conftest_s=' conftest_s='
.section .tdata,"awT",@progbits .section .tdata,"awT",@progbits
......
...@@ -2968,6 +2968,18 @@ foo: data8 25 ...@@ -2968,6 +2968,18 @@ foo: data8 25
tls_first_minor=13 tls_first_minor=13
tls_as_opt=--fatal-warnings tls_as_opt=--fatal-warnings
;; ;;
microblaze*-*-*)
conftest_s='
.section .tdata,"awT",@progbits
x:
.word 2
.text
addik r5,r20,x@TLSGD
addik r5,r20,x@TLSLDM'
tls_first_major=2
tls_first_minor=20
tls_as_opt='--fatal-warnings'
;;
mips*-*-*) mips*-*-*)
conftest_s=' conftest_s='
.section .tdata,"awT",@progbits .section .tdata,"awT",@progbits
......
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