mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-23 00:51:00 +08:00
Add mips16/nomips16 function attributes and -mflip-mips16 option for testing mixed-mode compilation.
2007-09-05 Sandra Loosemore <sandra@codesourcery.com> David Ung <davidu@mips.com> Nigel Stephens <nigel@mips.com> Add mips16/nomips16 function attributes and -mflip-mips16 option for testing mixed-mode compilation. gcc/ * config/mips/mips.opt (mflip-mips16): New. * config/mips/mips.h (SYMBOL_FLAG_MIPS16_FUNC): Define. (SYMBOL_FLAG_MIPS16_FUNC_P): Define. * config/mips/mips.c (mips_base_target_flags): New. (mips_base_mips16): New. (mips_base_schedule_insns): New. (mips_base_reorder_blocks_and_partition): New. (mips_base_align_loops): New. (mips_base_align_jumps): New. (mips_base_align_functions): New. (mips16_flipper): New. (mips_attribute_table): Add "mips16" and "nomips16" entries. (TARGET_SET_CURRENT_FUNCTION): Define. (mips_mips16_type_p, mips_nomips16_type_p): New. (mips_comp_type_attributes): Check mips16/nomips16 attributes. (mips_function_ok_for_sibcall): Make it deal with functions with mips16 attributes. (mips_init_split_addresses): New, split out from override_options. (mips_init_relocs): New, split out from override_options. (was_mips16_p): New. (mips_set_mips16_mode): New, split out from override_options. (mips_set_current_function): New. (override_options): Add sorry for unsupported mips16/pic combination. Remove error for mips16/dsp combination. Save base option settings. (mips_file_start): Move mips16 mode setting output from here.... (mips_output_function_prologue): ....to here. (mips_output_mi_thunk): Check for mips16 function. (build_mips16_function_stub): Don't set .mips16 here. (build_mips16_call_stub): Likewise. (mips_expand_builtin): Error in mips16 mode. (mips_use_mips16_mode_p): New. (mips_encode_section_info): Check for mips16 function, and set SYMBOL_REF_FLAGS accordingly. * doc/extend.texi (Function Attributes): Document new mips16/nomips16 attributes. * doc/invoke.texi (Option Summary): Add -mflip-mips16. (MIPS Options): Document -mflip-mips16. gcc/testsuite/ * gcc.target/mips/mips16-attributes.c: New. Co-Authored-By: David Ung <davidu@mips.com> Co-Authored-By: Nigel Stephens <nigel@mips.com> From-SVN: r128134
This commit is contained in:
parent
edede0242f
commit
f9e4a411f9
@ -1,6 +1,54 @@
|
||||
2007-09-05 Sandra Loosemore <sandra@codesourcery.com>
|
||||
David Ung <davidu@mips.com>
|
||||
Nigel Stephens <nigel@mips.com>
|
||||
|
||||
Add mips16/nomips16 function attributes and -mflip-mips16 option
|
||||
for testing mixed-mode compilation.
|
||||
|
||||
* config/mips/mips.opt (mflip-mips16): New.
|
||||
|
||||
* config/mips/mips.h (SYMBOL_FLAG_MIPS16_FUNC): Define.
|
||||
(SYMBOL_FLAG_MIPS16_FUNC_P): Define.
|
||||
|
||||
* config/mips/mips.c (mips_base_target_flags): New.
|
||||
(mips_base_mips16): New.
|
||||
(mips_base_schedule_insns): New.
|
||||
(mips_base_reorder_blocks_and_partition): New.
|
||||
(mips_base_align_loops): New.
|
||||
(mips_base_align_jumps): New.
|
||||
(mips_base_align_functions): New.
|
||||
(mips16_flipper): New.
|
||||
(mips_attribute_table): Add "mips16" and "nomips16" entries.
|
||||
(TARGET_SET_CURRENT_FUNCTION): Define.
|
||||
(mips_mips16_type_p, mips_nomips16_type_p): New.
|
||||
(mips_comp_type_attributes): Check mips16/nomips16 attributes.
|
||||
(mips_function_ok_for_sibcall): Make it deal with functions with
|
||||
mips16 attributes.
|
||||
(mips_init_split_addresses): New, split out from override_options.
|
||||
(mips_init_relocs): New, split out from override_options.
|
||||
(was_mips16_p): New.
|
||||
(mips_set_mips16_mode): New, split out from override_options.
|
||||
(mips_set_current_function): New.
|
||||
(override_options): Add sorry for unsupported mips16/pic
|
||||
combination. Remove error for mips16/dsp combination. Save
|
||||
base option settings.
|
||||
(mips_file_start): Move mips16 mode setting output from here....
|
||||
(mips_output_function_prologue): ....to here.
|
||||
(mips_output_mi_thunk): Check for mips16 function.
|
||||
(build_mips16_function_stub): Don't set .mips16 here.
|
||||
(build_mips16_call_stub): Likewise.
|
||||
(mips_expand_builtin): Error in mips16 mode.
|
||||
(mips_use_mips16_mode_p): New.
|
||||
(mips_encode_section_info): Check for mips16 function, and set
|
||||
SYMBOL_REF_FLAGS accordingly.
|
||||
|
||||
* doc/extend.texi (Function Attributes): Document new
|
||||
mips16/nomips16 attributes.
|
||||
* doc/invoke.texi (Option Summary): Add -mflip-mips16.
|
||||
(MIPS Options): Document -mflip-mips16.
|
||||
|
||||
2007-09-05 Sandra Loosemore <sandra@codesourcery.com>
|
||||
|
||||
gcc/
|
||||
* config/mips/mips.c (mips_legitimize_tls_address): Call sorry
|
||||
if we encounter TLS address in MIPS16 mode.
|
||||
(mips_legitimize_const_move): Check cannot_force_const_mem for
|
||||
|
@ -424,6 +424,8 @@ static rtx mips_expand_builtin_bposge (enum mips_builtin_type, rtx);
|
||||
static void mips_encode_section_info (tree, rtx, int);
|
||||
static void mips_extra_live_on_entry (bitmap);
|
||||
static int mips_comp_type_attributes (const_tree, const_tree);
|
||||
static void mips_set_mips16_mode (int);
|
||||
static void mips_set_current_function (tree);
|
||||
static int mips_mode_rep_extended (enum machine_mode, enum machine_mode);
|
||||
static bool mips_offset_within_alignment_p (rtx, HOST_WIDE_INT);
|
||||
static void mips_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
|
||||
@ -617,6 +619,18 @@ int mips_abi = MIPS_ABI_DEFAULT;
|
||||
/* Cost information to use. */
|
||||
const struct mips_rtx_cost_data *mips_cost;
|
||||
|
||||
/* Remember the ambient target flags, excluding mips16. */
|
||||
static int mips_base_target_flags;
|
||||
/* The mips16 command-line target flags only. */
|
||||
static bool mips_base_mips16;
|
||||
/* Similar copies of option settings. */
|
||||
static int mips_base_schedule_insns; /* flag_schedule_insns */
|
||||
static int mips_base_reorder_blocks_and_partition; /* flag_reorder... */
|
||||
static int mips_base_align_loops; /* align_loops */
|
||||
static int mips_base_align_jumps; /* align_jumps */
|
||||
static int mips_base_align_functions; /* align_functions */
|
||||
static GTY(()) int mips16_flipper;
|
||||
|
||||
/* The -mtext-loads setting. */
|
||||
enum mips_code_readable_setting mips_code_readable = CODE_READABLE_YES;
|
||||
|
||||
@ -715,6 +729,9 @@ const struct attribute_spec mips_attribute_table[] =
|
||||
{ "long_call", 0, 0, false, true, true, NULL },
|
||||
{ "far", 0, 0, false, true, true, NULL },
|
||||
{ "near", 0, 0, false, true, true, NULL },
|
||||
/* Switch MIPS16 ASE on and off per-function. */
|
||||
{ "mips16", 0, 0, false, true, true, NULL },
|
||||
{ "nomips16", 0, 0, false, true, true, NULL },
|
||||
{ NULL, 0, 0, false, false, false, NULL }
|
||||
};
|
||||
|
||||
@ -1251,6 +1268,9 @@ static const unsigned char mips16e_save_restore_regs[] = {
|
||||
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
|
||||
#define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall
|
||||
|
||||
#undef TARGET_SET_CURRENT_FUNCTION
|
||||
#define TARGET_SET_CURRENT_FUNCTION mips_set_current_function
|
||||
|
||||
#undef TARGET_VALID_POINTER_MODE
|
||||
#define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
|
||||
#undef TARGET_RTX_COSTS
|
||||
@ -1369,6 +1389,19 @@ mips_far_type_p (const_tree type)
|
||||
|| lookup_attribute ("far", TYPE_ATTRIBUTES (type)) != NULL);
|
||||
}
|
||||
|
||||
/* Similar predicates for "mips16"/"nomips16" attributes. */
|
||||
|
||||
static bool
|
||||
mips_mips16_type_p (const_tree type)
|
||||
{
|
||||
return lookup_attribute ("mips16", TYPE_ATTRIBUTES (type)) != NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
mips_nomips16_type_p (const_tree type)
|
||||
{
|
||||
return lookup_attribute ("nomips16", TYPE_ATTRIBUTES (type)) != NULL;
|
||||
}
|
||||
|
||||
/* Return 0 if the attributes for two types are incompatible, 1 if they
|
||||
are compatible, and 2 if they are nearly compatible (which causes a
|
||||
@ -1387,6 +1420,11 @@ mips_comp_type_attributes (const_tree type1, const_tree type2)
|
||||
if (mips_near_type_p (type1) && mips_far_type_p (type2))
|
||||
return 0;
|
||||
|
||||
/* Mips16/nomips16 attributes must match exactly. */
|
||||
if (mips_nomips16_type_p (type1) != mips_nomips16_type_p (type2)
|
||||
|| mips_mips16_type_p (type1) != mips_mips16_type_p (type2))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -3789,13 +3827,22 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
|
||||
}
|
||||
|
||||
|
||||
/* We can handle any sibcall when TARGET_SIBCALLS is true. */
|
||||
/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */
|
||||
|
||||
static bool
|
||||
mips_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
|
||||
tree exp ATTRIBUTE_UNUSED)
|
||||
mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
|
||||
{
|
||||
return TARGET_SIBCALLS;
|
||||
if (!TARGET_SIBCALLS)
|
||||
return false;
|
||||
|
||||
/* We can't do a sibcall if the called function is a MIPS16 function
|
||||
because there is no direct "jx" instruction equivalent to "jalx" to
|
||||
switch the ISA mode. */
|
||||
if (decl && SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (decl), 0)))
|
||||
return false;
|
||||
|
||||
/* Otherwise OK. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Emit code to move general operand SRC into condition-code
|
||||
@ -5038,6 +5085,231 @@ mips_set_tune (const struct mips_cpu_info *info)
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize mips_split_addresses from the associated command-line
|
||||
settings.
|
||||
|
||||
mips_split_addresses is a half-way house between explicit
|
||||
relocations and the traditional assembler macros. It can
|
||||
split absolute 32-bit symbolic constants into a high/lo_sum
|
||||
pair but uses macros for other sorts of access.
|
||||
|
||||
Like explicit relocation support for REL targets, it relies
|
||||
on GNU extensions in the assembler and the linker.
|
||||
|
||||
Although this code should work for -O0, it has traditionally
|
||||
been treated as an optimization. */
|
||||
|
||||
static void
|
||||
mips_init_split_addresses (void)
|
||||
{
|
||||
if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES
|
||||
&& optimize && !flag_pic
|
||||
&& !ABI_HAS_64BIT_SYMBOLS)
|
||||
mips_split_addresses = 1;
|
||||
else
|
||||
mips_split_addresses = 0;
|
||||
}
|
||||
|
||||
/* (Re-)Initialize information about relocs. */
|
||||
|
||||
static void
|
||||
mips_init_relocs (void)
|
||||
{
|
||||
memset (mips_split_p, '\0', sizeof (mips_split_p));
|
||||
memset (mips_hi_relocs, '\0', sizeof (mips_hi_relocs));
|
||||
memset (mips_lo_relocs, '\0', sizeof (mips_lo_relocs));
|
||||
|
||||
if (ABI_HAS_64BIT_SYMBOLS)
|
||||
{
|
||||
if (TARGET_EXPLICIT_RELOCS)
|
||||
{
|
||||
mips_split_p[SYMBOL_64_HIGH] = true;
|
||||
mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
|
||||
mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
|
||||
|
||||
mips_split_p[SYMBOL_64_MID] = true;
|
||||
mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
|
||||
mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
|
||||
|
||||
mips_split_p[SYMBOL_64_LOW] = true;
|
||||
mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
|
||||
mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
|
||||
|
||||
mips_split_p[SYMBOL_ABSOLUTE] = true;
|
||||
mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TARGET_EXPLICIT_RELOCS || mips_split_addresses || TARGET_MIPS16)
|
||||
{
|
||||
mips_split_p[SYMBOL_ABSOLUTE] = true;
|
||||
mips_hi_relocs[SYMBOL_ABSOLUTE] = "%hi(";
|
||||
mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
|
||||
|
||||
mips_lo_relocs[SYMBOL_32_HIGH] = "%hi(";
|
||||
}
|
||||
}
|
||||
|
||||
if (TARGET_MIPS16)
|
||||
{
|
||||
/* The high part is provided by a pseudo copy of $gp. */
|
||||
mips_split_p[SYMBOL_GP_RELATIVE] = true;
|
||||
mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel(";
|
||||
}
|
||||
|
||||
if (TARGET_EXPLICIT_RELOCS)
|
||||
{
|
||||
/* Small data constants are kept whole until after reload,
|
||||
then lowered by mips_rewrite_small_data. */
|
||||
mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel(";
|
||||
|
||||
mips_split_p[SYMBOL_GOT_PAGE_OFST] = true;
|
||||
if (TARGET_NEWABI)
|
||||
{
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
|
||||
mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%got_ofst(";
|
||||
}
|
||||
else
|
||||
{
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
|
||||
mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo(";
|
||||
}
|
||||
|
||||
if (TARGET_XGOT)
|
||||
{
|
||||
/* The HIGH and LO_SUM are matched by special .md patterns. */
|
||||
mips_split_p[SYMBOL_GOT_DISP] = true;
|
||||
|
||||
mips_split_p[SYMBOL_GOTOFF_DISP] = true;
|
||||
mips_hi_relocs[SYMBOL_GOTOFF_DISP] = "%got_hi(";
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_lo(";
|
||||
|
||||
mips_split_p[SYMBOL_GOTOFF_CALL] = true;
|
||||
mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TARGET_NEWABI)
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_disp(";
|
||||
else
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got(";
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
|
||||
}
|
||||
}
|
||||
|
||||
if (TARGET_NEWABI)
|
||||
{
|
||||
mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
|
||||
mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
|
||||
}
|
||||
|
||||
/* Thread-local relocation operators. */
|
||||
mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
|
||||
mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
|
||||
mips_split_p[SYMBOL_DTPREL] = 1;
|
||||
mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
|
||||
mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
|
||||
mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
|
||||
mips_split_p[SYMBOL_TPREL] = 1;
|
||||
mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
|
||||
mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
|
||||
|
||||
mips_lo_relocs[SYMBOL_HALF] = "%half(";
|
||||
}
|
||||
|
||||
static GTY(()) int was_mips16_p = -1;
|
||||
|
||||
/* Set up the target-dependent global state so that it matches the
|
||||
current function's ISA mode. */
|
||||
|
||||
static void
|
||||
mips_set_mips16_mode (int mips16_p)
|
||||
{
|
||||
if (mips16_p == was_mips16_p)
|
||||
return;
|
||||
|
||||
/* Restore base settings of various flags. */
|
||||
target_flags = mips_base_target_flags;
|
||||
align_loops = mips_base_align_loops;
|
||||
align_jumps = mips_base_align_jumps;
|
||||
align_functions = mips_base_align_functions;
|
||||
flag_schedule_insns = mips_base_schedule_insns;
|
||||
flag_reorder_blocks_and_partition = mips_base_reorder_blocks_and_partition;
|
||||
flag_delayed_branch = mips_flag_delayed_branch;
|
||||
|
||||
if (mips16_p)
|
||||
{
|
||||
/* Select mips16 instruction set. */
|
||||
target_flags |= MASK_MIPS16;
|
||||
|
||||
/* Don't run the scheduler before reload, since it tends to
|
||||
increase register pressure. */
|
||||
flag_schedule_insns = 0;
|
||||
|
||||
/* Don't do hot/cold partitioning. The constant layout code expects
|
||||
the whole function to be in a single section. */
|
||||
flag_reorder_blocks_and_partition = 0;
|
||||
|
||||
/* Silently disable -mexplicit-relocs since it doesn't apply
|
||||
to mips16 code. Even so, it would overly pedantic to warn
|
||||
about "-mips16 -mexplicit-relocs", especially given that
|
||||
we use a %gprel() operator. */
|
||||
target_flags &= ~MASK_EXPLICIT_RELOCS;
|
||||
|
||||
/* Silently disable DSP extensions. */
|
||||
target_flags &= ~MASK_DSP;
|
||||
target_flags &= ~MASK_DSPR2;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reset to select base non-mips16 ISA. */
|
||||
target_flags &= ~MASK_MIPS16;
|
||||
|
||||
/* When using explicit relocs, we call dbr_schedule from within
|
||||
mips_reorg. */
|
||||
if (TARGET_EXPLICIT_RELOCS)
|
||||
flag_delayed_branch = 0;
|
||||
|
||||
/* Provide default values for align_* for 64-bit targets. */
|
||||
if (TARGET_64BIT)
|
||||
{
|
||||
if (align_loops == 0)
|
||||
align_loops = 8;
|
||||
if (align_jumps == 0)
|
||||
align_jumps = 8;
|
||||
if (align_functions == 0)
|
||||
align_functions = 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* (Re)initialize mips target internals for new ISA. */
|
||||
mips_init_split_addresses ();
|
||||
mips_init_relocs ();
|
||||
|
||||
if (was_mips16_p >= 0)
|
||||
/* Reinitialize target-dependent state. */
|
||||
target_reinit ();
|
||||
|
||||
was_mips16_p = TARGET_MIPS16;
|
||||
}
|
||||
|
||||
/* Implement TARGET_SET_CURRENT_FUNCTION. Decide whether the current
|
||||
function should use the MIPS16 ISA and switch modes accordingly. */
|
||||
|
||||
static void
|
||||
mips_set_current_function (tree fndecl)
|
||||
{
|
||||
int mips16p;
|
||||
if (fndecl)
|
||||
mips16p = SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (fndecl), 0));
|
||||
else
|
||||
mips16p = mips_base_mips16;
|
||||
mips_set_mips16_mode (mips16p);
|
||||
}
|
||||
|
||||
/* Implement TARGET_HANDLE_OPTION. */
|
||||
|
||||
static bool
|
||||
@ -5265,6 +5537,14 @@ override_options (void)
|
||||
target_flags &= ~MASK_ABICALLS;
|
||||
}
|
||||
|
||||
/* MIPS16 cannot generate PIC yet. */
|
||||
if (TARGET_MIPS16 && (flag_pic || TARGET_ABICALLS))
|
||||
{
|
||||
sorry ("MIPS16 PIC");
|
||||
target_flags &= ~MASK_ABICALLS;
|
||||
flag_pic = flag_pie = flag_shlib = 0;
|
||||
}
|
||||
|
||||
if (TARGET_ABICALLS)
|
||||
{
|
||||
/* We need to set flag_pic for executables as well as DSOs
|
||||
@ -5282,54 +5562,12 @@ override_options (void)
|
||||
if (TARGET_VXWORKS_RTP && mips_section_threshold > 0)
|
||||
warning (0, "-G and -mrtp are incompatible");
|
||||
|
||||
/* mips_split_addresses is a half-way house between explicit
|
||||
relocations and the traditional assembler macros. It can
|
||||
split absolute 32-bit symbolic constants into a high/lo_sum
|
||||
pair but uses macros for other sorts of access.
|
||||
|
||||
Like explicit relocation support for REL targets, it relies
|
||||
on GNU extensions in the assembler and the linker.
|
||||
|
||||
Although this code should work for -O0, it has traditionally
|
||||
been treated as an optimization. */
|
||||
if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES
|
||||
&& optimize && !flag_pic
|
||||
&& !ABI_HAS_64BIT_SYMBOLS)
|
||||
mips_split_addresses = 1;
|
||||
else
|
||||
mips_split_addresses = 0;
|
||||
|
||||
/* -mvr4130-align is a "speed over size" optimization: it usually produces
|
||||
faster code, but at the expense of more nops. Enable it at -O3 and
|
||||
above. */
|
||||
if (optimize > 2 && (target_flags_explicit & MASK_VR4130_ALIGN) == 0)
|
||||
target_flags |= MASK_VR4130_ALIGN;
|
||||
|
||||
if (TARGET_MIPS16)
|
||||
{
|
||||
/* Don't run the scheduler before reload, since it tends to
|
||||
increase register pressure. */
|
||||
flag_schedule_insns = 0;
|
||||
|
||||
/* Don't do hot/cold partitioning. The constant layout code expects
|
||||
the whole function to be in a single section. */
|
||||
flag_reorder_blocks_and_partition = 0;
|
||||
|
||||
/* Silently disable -mexplicit-relocs since it doesn't apply
|
||||
to mips16 code. Even so, it would overly pedantic to warn
|
||||
about "-mips16 -mexplicit-relocs", especially given that
|
||||
we use a %gprel() operator. */
|
||||
target_flags &= ~MASK_EXPLICIT_RELOCS;
|
||||
}
|
||||
|
||||
/* When using explicit relocs, we call dbr_schedule from within
|
||||
mips_reorg. */
|
||||
if (TARGET_EXPLICIT_RELOCS)
|
||||
{
|
||||
mips_flag_delayed_branch = flag_delayed_branch;
|
||||
flag_delayed_branch = 0;
|
||||
}
|
||||
|
||||
/* Prefer a call to memcpy over inline code when optimizing for size,
|
||||
though see MOVE_RATIO in mips.h. */
|
||||
if (optimize_size && (target_flags_explicit & MASK_MEMCPY) == 0)
|
||||
@ -5363,9 +5601,6 @@ override_options (void)
|
||||
if (TARGET_DSPR2)
|
||||
target_flags |= MASK_DSP;
|
||||
|
||||
if (TARGET_MIPS16 && TARGET_DSP)
|
||||
error ("-mips16 and -mdsp cannot be used together");
|
||||
|
||||
mips_print_operand_punct['?'] = 1;
|
||||
mips_print_operand_punct['#'] = 1;
|
||||
mips_print_operand_punct['/'] = 1;
|
||||
@ -5493,120 +5728,9 @@ override_options (void)
|
||||
initialized yet, so we can't use that here. */
|
||||
gpr_mode = TARGET_64BIT ? DImode : SImode;
|
||||
|
||||
/* Provide default values for align_* for 64-bit targets. */
|
||||
if (TARGET_64BIT && !TARGET_MIPS16)
|
||||
{
|
||||
if (align_loops == 0)
|
||||
align_loops = 8;
|
||||
if (align_jumps == 0)
|
||||
align_jumps = 8;
|
||||
if (align_functions == 0)
|
||||
align_functions = 8;
|
||||
}
|
||||
|
||||
/* Function to allocate machine-dependent function status. */
|
||||
init_machine_status = &mips_init_machine_status;
|
||||
|
||||
if (ABI_HAS_64BIT_SYMBOLS)
|
||||
{
|
||||
if (TARGET_EXPLICIT_RELOCS)
|
||||
{
|
||||
mips_split_p[SYMBOL_64_HIGH] = true;
|
||||
mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
|
||||
mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
|
||||
|
||||
mips_split_p[SYMBOL_64_MID] = true;
|
||||
mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
|
||||
mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
|
||||
|
||||
mips_split_p[SYMBOL_64_LOW] = true;
|
||||
mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
|
||||
mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
|
||||
|
||||
mips_split_p[SYMBOL_ABSOLUTE] = true;
|
||||
mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TARGET_EXPLICIT_RELOCS || mips_split_addresses || TARGET_MIPS16)
|
||||
{
|
||||
mips_split_p[SYMBOL_ABSOLUTE] = true;
|
||||
mips_hi_relocs[SYMBOL_ABSOLUTE] = "%hi(";
|
||||
mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
|
||||
|
||||
mips_lo_relocs[SYMBOL_32_HIGH] = "%hi(";
|
||||
}
|
||||
}
|
||||
|
||||
if (TARGET_MIPS16)
|
||||
{
|
||||
/* The high part is provided by a pseudo copy of $gp. */
|
||||
mips_split_p[SYMBOL_GP_RELATIVE] = true;
|
||||
mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel(";
|
||||
}
|
||||
|
||||
if (TARGET_EXPLICIT_RELOCS)
|
||||
{
|
||||
/* Small data constants are kept whole until after reload,
|
||||
then lowered by mips_rewrite_small_data. */
|
||||
mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel(";
|
||||
|
||||
mips_split_p[SYMBOL_GOT_PAGE_OFST] = true;
|
||||
if (TARGET_NEWABI)
|
||||
{
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
|
||||
mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%got_ofst(";
|
||||
}
|
||||
else
|
||||
{
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
|
||||
mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo(";
|
||||
}
|
||||
|
||||
if (TARGET_XGOT)
|
||||
{
|
||||
/* The HIGH and LO_SUM are matched by special .md patterns. */
|
||||
mips_split_p[SYMBOL_GOT_DISP] = true;
|
||||
|
||||
mips_split_p[SYMBOL_GOTOFF_DISP] = true;
|
||||
mips_hi_relocs[SYMBOL_GOTOFF_DISP] = "%got_hi(";
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_lo(";
|
||||
|
||||
mips_split_p[SYMBOL_GOTOFF_CALL] = true;
|
||||
mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TARGET_NEWABI)
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_disp(";
|
||||
else
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got(";
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
|
||||
}
|
||||
}
|
||||
|
||||
if (TARGET_NEWABI)
|
||||
{
|
||||
mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
|
||||
mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
|
||||
mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
|
||||
}
|
||||
|
||||
/* Thread-local relocation operators. */
|
||||
mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
|
||||
mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
|
||||
mips_split_p[SYMBOL_DTPREL] = 1;
|
||||
mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
|
||||
mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
|
||||
mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
|
||||
mips_split_p[SYMBOL_TPREL] = 1;
|
||||
mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
|
||||
mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
|
||||
|
||||
mips_lo_relocs[SYMBOL_HALF] = "%half(";
|
||||
|
||||
/* Default to working around R4000 errata only if the processor
|
||||
was selected explicitly. */
|
||||
if ((target_flags_explicit & MASK_FIX_R4000) == 0
|
||||
@ -5618,6 +5742,19 @@ override_options (void)
|
||||
if ((target_flags_explicit & MASK_FIX_R4400) == 0
|
||||
&& mips_matching_cpu_name_p (mips_arch_info->name, "r4400"))
|
||||
target_flags |= MASK_FIX_R4400;
|
||||
|
||||
/* Save base state of options. */
|
||||
mips_base_mips16 = TARGET_MIPS16;
|
||||
mips_base_target_flags = target_flags;
|
||||
mips_base_schedule_insns = flag_schedule_insns;
|
||||
mips_base_reorder_blocks_and_partition = flag_reorder_blocks_and_partition;
|
||||
mips_base_align_loops = align_loops;
|
||||
mips_base_align_jumps = align_jumps;
|
||||
mips_base_align_functions = align_functions;
|
||||
mips_flag_delayed_branch = flag_delayed_branch;
|
||||
|
||||
/* Now select the mips16 or 32-bit instruction set, as requested. */
|
||||
mips_set_mips16_mode (mips_base_mips16);
|
||||
}
|
||||
|
||||
/* Swap the register information for registers I and I + 1, which
|
||||
@ -6373,9 +6510,6 @@ mips_file_start (void)
|
||||
if (TARGET_ABICALLS)
|
||||
fprintf (asm_out_file, "\t.abicalls\n");
|
||||
|
||||
if (TARGET_MIPS16)
|
||||
fprintf (asm_out_file, "\t.set\tmips16\n");
|
||||
|
||||
if (flag_verbose_asm)
|
||||
fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
|
||||
ASM_COMMENT_START,
|
||||
@ -7230,6 +7364,12 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
|
||||
&& current_function_args_info.fp_code != 0)
|
||||
build_mips16_function_stub (file);
|
||||
|
||||
/* Select the mips16 mode for this function. */
|
||||
if (TARGET_MIPS16)
|
||||
fprintf (file, "\t.set\tmips16\n");
|
||||
else
|
||||
fprintf (file, "\t.set\tnomips16\n");
|
||||
|
||||
if (!FUNCTION_NAME_ALREADY_DECLARED)
|
||||
{
|
||||
/* Get the function name the same way that toplev.c does before calling
|
||||
@ -8186,7 +8326,6 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
|
||||
TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
|
||||
|
||||
SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
|
||||
|
||||
}
|
||||
|
||||
/* Set up the global pointer for n32 or n64 abicalls. If
|
||||
@ -8237,7 +8376,8 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
|
||||
/* Jump to the target function. Use a sibcall if direct jumps are
|
||||
allowed, otherwise load the address into a register first. */
|
||||
fnaddr = XEXP (DECL_RTL (function), 0);
|
||||
if (TARGET_MIPS16 || TARGET_USE_GOT || SYMBOL_REF_LONG_CALL_P (fnaddr))
|
||||
if (TARGET_MIPS16 || TARGET_USE_GOT || SYMBOL_REF_LONG_CALL_P (fnaddr)
|
||||
|| SYMBOL_REF_MIPS16_FUNC_P (fnaddr))
|
||||
{
|
||||
/* This is messy. gas treats "la $25,foo" as part of a call
|
||||
sequence and may allow a global "foo" to be lazily bound.
|
||||
@ -9073,8 +9213,6 @@ build_mips16_function_stub (FILE *file)
|
||||
fputs ("\n", file);
|
||||
}
|
||||
|
||||
fprintf (file, "\t.set\tmips16\n");
|
||||
|
||||
switch_to_section (function_section (current_function_decl));
|
||||
}
|
||||
|
||||
@ -9406,8 +9544,6 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
|
||||
fputs ("\n", asm_out_file);
|
||||
}
|
||||
|
||||
fprintf (asm_out_file, "\t.set\tmips16\n");
|
||||
|
||||
/* Record this stub. */
|
||||
l = (struct mips16_stub *) xmalloc (sizeof *l);
|
||||
l->name = xstrdup (fnname);
|
||||
@ -11648,6 +11784,13 @@ mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
|
||||
fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
|
||||
fcode = DECL_FUNCTION_CODE (fndecl);
|
||||
|
||||
if (TARGET_MIPS16)
|
||||
{
|
||||
error ("built-in function %qs not supported for MIPS16",
|
||||
IDENTIFIER_POINTER (DECL_NAME (fndecl)));
|
||||
return const0_rtx;
|
||||
}
|
||||
|
||||
bdesc = NULL;
|
||||
for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
|
||||
{
|
||||
@ -12154,6 +12297,43 @@ mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target)
|
||||
const1_rtx, const0_rtx);
|
||||
}
|
||||
|
||||
/* Return true if we should force MIPS16 mode for the function named by
|
||||
the SYMBOL_REF SYMBOL, which belongs to DECL and has type TYPE.
|
||||
FIRST is true if this is the first time handling this decl. */
|
||||
|
||||
static bool
|
||||
mips_use_mips16_mode_p (rtx symbol, tree decl, int first, tree type)
|
||||
{
|
||||
tree parent;
|
||||
|
||||
/* Explicit function attributes take precedence. */
|
||||
if (mips_mips16_type_p (type))
|
||||
return true;
|
||||
if (mips_nomips16_type_p (type))
|
||||
return false;
|
||||
|
||||
/* A nested function should inherit the MIPS16 setting from its parent. */
|
||||
parent = decl_function_context (decl);
|
||||
if (parent)
|
||||
return SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (parent), 0));
|
||||
|
||||
/* Handle -mflip-mips16. */
|
||||
if (TARGET_FLIP_MIPS16
|
||||
&& !DECL_BUILT_IN (decl)
|
||||
&& !DECL_ARTIFICIAL (decl))
|
||||
{
|
||||
if (!first)
|
||||
/* Use the setting we picked first time around. */
|
||||
return SYMBOL_REF_MIPS16_FUNC_P (symbol);
|
||||
|
||||
mips16_flipper = !mips16_flipper;
|
||||
if (mips16_flipper)
|
||||
return !mips_base_mips16;
|
||||
}
|
||||
|
||||
return mips_base_mips16;
|
||||
}
|
||||
|
||||
/* Set SYMBOL_REF_FLAGS for the SYMBOL_REF inside RTL, which belongs to DECL.
|
||||
FIRST is true if this is the first time handling this decl. */
|
||||
|
||||
@ -12165,10 +12345,19 @@ mips_encode_section_info (tree decl, rtx rtl, int first)
|
||||
if (TREE_CODE (decl) == FUNCTION_DECL)
|
||||
{
|
||||
rtx symbol = XEXP (rtl, 0);
|
||||
tree type = TREE_TYPE (decl);
|
||||
|
||||
if ((TARGET_LONG_CALLS && !mips_near_type_p (TREE_TYPE (decl)))
|
||||
|| mips_far_type_p (TREE_TYPE (decl)))
|
||||
if ((TARGET_LONG_CALLS && !mips_near_type_p (type))
|
||||
|| mips_far_type_p (type))
|
||||
SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
|
||||
|
||||
if (mips_use_mips16_mode_p (symbol, decl, first, type))
|
||||
{
|
||||
if (flag_pic || TARGET_ABICALLS)
|
||||
sorry ("MIPS16 PIC");
|
||||
else
|
||||
SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_MIPS16_FUNC;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2301,6 +2301,11 @@ typedef struct mips_args {
|
||||
#define SYMBOL_REF_LONG_CALL_P(X) \
|
||||
((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_LONG_CALL) != 0)
|
||||
|
||||
/* Flag to mark a function decl symbol a "mips16" function. */
|
||||
#define SYMBOL_FLAG_MIPS16_FUNC (SYMBOL_FLAG_MACH_DEP << 1)
|
||||
#define SYMBOL_REF_MIPS16_FUNC_P(RTX) \
|
||||
((SYMBOL_REF_FLAGS (RTX) & SYMBOL_FLAG_MIPS16_FUNC) != 0)
|
||||
|
||||
/* True if we're generating a form of MIPS16 code in which jump tables
|
||||
are stored in the text section and encoded as 16-bit PC-relative
|
||||
offsets. This is only possible when general text loads are allowed,
|
||||
|
@ -42,6 +42,10 @@ mbranch-likely
|
||||
Target Report Mask(BRANCHLIKELY)
|
||||
Use Branch Likely instructions, overriding the architecture default
|
||||
|
||||
mflip-mips16
|
||||
Target Report Var(TARGET_FLIP_MIPS16)
|
||||
Switch on/off MIPS16 ASE on alternating functions for compiler testing
|
||||
|
||||
mcheck-zero-division
|
||||
Target Report Mask(CHECK_ZERO_DIV)
|
||||
Trap on integer divide by zero
|
||||
|
@ -2202,6 +2202,24 @@ long as the old pointer is never referred to (including comparing it
|
||||
to the new pointer) after the function returns a non-@code{NULL}
|
||||
value.
|
||||
|
||||
@item mips16/nomips16
|
||||
@cindex @code{mips16} attribute
|
||||
@cindex @code{nomips16} attribute
|
||||
|
||||
On MIPS targets, you can use the @code{mips16} and @code{nomips16}
|
||||
function attributes to locally select or turn off MIPS16 code generation.
|
||||
A function with the @code{mips16} attribute is emitted as MIPS16 code,
|
||||
while MIPS16 code generation is disabled for functions with the
|
||||
@code{nomips16} attribute. These attributes override the
|
||||
@option{-mips16} and @option{-mno-mips16} options on the command line
|
||||
(@pxref{MIPS Options}).
|
||||
|
||||
When compiling files containing mixed MIPS16 and non-MIPS16 code, the
|
||||
preprocessor symbol @code{__mips16} reflects the setting on the command line,
|
||||
not that within individual functions. Mixed MIPS16 and non-MIPS16 code
|
||||
may interact badly with some GCC extensions such as @code{__builtin_apply}
|
||||
(@pxref{Constructing Calls}).
|
||||
|
||||
@item model (@var{model-name})
|
||||
@cindex function addressability on the M32R/D
|
||||
@cindex variable addressability on the IA-64
|
||||
|
@ -620,7 +620,8 @@ Objective-C and Objective-C++ Dialects}.
|
||||
@emph{MIPS Options}
|
||||
@gccoptlist{-EL -EB -march=@var{arch} -mtune=@var{arch} @gol
|
||||
-mips1 -mips2 -mips3 -mips4 -mips32 -mips32r2 -mips64 @gol
|
||||
-mips16 -mno-mips16 -mabi=@var{abi} -mabicalls -mno-abicalls @gol
|
||||
-mips16 -mno-mips16 -mflip-mips16 @gol
|
||||
-mabi=@var{abi} -mabicalls -mno-abicalls @gol
|
||||
-mshared -mno-shared -mxgot -mno-xgot -mgp32 -mgp64 @gol
|
||||
-mfp32 -mfp64 -mhard-float -msoft-float @gol
|
||||
-msingle-float -mdouble-float -mdsp -mno-dsp -mdspr2 -mno-dspr2 @gol
|
||||
@ -11626,6 +11627,16 @@ Equivalent to @samp{-march=mips64}.
|
||||
Generate (do not generate) MIPS16 code. If GCC is targetting a
|
||||
MIPS32 or MIPS64 architecture, it will make use of the MIPS16e ASE@.
|
||||
|
||||
MIPS16 code generation can also be controlled on a per-function basis
|
||||
by means of @code{mips16} and @code{nomips16} attributes.
|
||||
@xref{Function Attributes}, for more information.
|
||||
|
||||
@item -mflip-mips16
|
||||
@opindex mflip-mips16
|
||||
Generate MIPS16 code on alternating functions. This option is provided
|
||||
for regression testing of mixed MIPS16/non-MIPS16 code generation, and is
|
||||
not intended for ordinary use in compiling user code.
|
||||
|
||||
@item -mabi=32
|
||||
@itemx -mabi=o64
|
||||
@itemx -mabi=n32
|
||||
|
@ -1,3 +1,9 @@
|
||||
2007-09-05 Sandra Loosemore <sandra@codesourcery.com>
|
||||
David Ung <davidu@mips.com>
|
||||
Nigel Stephens <nigel@mips.com>
|
||||
|
||||
* gcc.target/mips/mips16-attributes.c: New.
|
||||
|
||||
2007-09-05 Paul Thomas <pault@gcc.gnu.org>
|
||||
|
||||
PR fortran/31564
|
||||
|
82
gcc/testsuite/gcc.target/mips/mips16-attributes.c
Normal file
82
gcc/testsuite/gcc.target/mips/mips16-attributes.c
Normal file
@ -0,0 +1,82 @@
|
||||
/* Verify that mips16 and nomips16 attributes work, checking all combinations
|
||||
of calling a nomips16/mips16/default function from a nomips16/mips16/default
|
||||
function. */
|
||||
/* { dg-do run { target mipsisa*-*-elf* } } */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define ATTR1 __attribute__ ((nomips16))
|
||||
#define ATTR2 __attribute__ ((mips16))
|
||||
#define ATTR3
|
||||
|
||||
double ATTR1
|
||||
f1 (int i, float f, double d)
|
||||
{
|
||||
return i + f + d;
|
||||
}
|
||||
|
||||
double ATTR2
|
||||
f2 (int i, float f, double d)
|
||||
{
|
||||
return i + f + d;
|
||||
}
|
||||
|
||||
double ATTR3
|
||||
f3 (int i, float f, double d)
|
||||
{
|
||||
return i + f + d;
|
||||
}
|
||||
|
||||
void ATTR1
|
||||
g1 (int i, float f, double d)
|
||||
{
|
||||
double r = i + f + d;
|
||||
|
||||
if (f1 (i, f, d) != r)
|
||||
abort ();
|
||||
if (f2 (i+1, f+1, d+1) != r + 3)
|
||||
abort ();
|
||||
if (f3 (i+2, f+2, d+2) != r + 6)
|
||||
abort ();
|
||||
}
|
||||
|
||||
void ATTR2
|
||||
g2 (int i, float f, double d)
|
||||
{
|
||||
double r = i + f + d;
|
||||
|
||||
if (f1 (i, f, d) != r)
|
||||
abort ();
|
||||
if (f2 (i+1, f+1, d+1) != r + 3)
|
||||
abort ();
|
||||
if (f3 (i+2, f+2, d+2) != r + 6)
|
||||
abort ();
|
||||
}
|
||||
|
||||
void ATTR3
|
||||
g3 (int i, float f, double d)
|
||||
{
|
||||
double r = i + f + d;
|
||||
|
||||
if (f1 (i, f, d) != r)
|
||||
abort ();
|
||||
if (f2 (i+1, f+1, d+1) != r + 3)
|
||||
abort ();
|
||||
if (f3 (i+2, f+2, d+2) != r + 6)
|
||||
abort ();
|
||||
}
|
||||
|
||||
int ATTR3
|
||||
main (void)
|
||||
{
|
||||
int i = 1;
|
||||
float f = -2.0;
|
||||
double d = 3.0;
|
||||
|
||||
g1 (i, f, d);
|
||||
g2 (i, f, d);
|
||||
g3 (i, f, d);
|
||||
|
||||
exit (0);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user