Move some external functions used by machine description patterns to nds32-md-auxiliary.c module.
gcc/ * config/nds32/nds32.c (nds32_byte_to_size): Move to ... (nds32_output_casesi_pc_relative): Move to ... (nds32_output_casesi): Move to ... (nds32_mem_format): Move to ... (nds32_output_16bit_store): Move to ... (nds32_output_16bit_load): Move to ... (nds32_output_32bit_store): Move to ... (nds32_output_32bit_load): Move to ... (nds32_output_32bit_load_s): Move to ... (nds32_output_stack_push): Move to ... (nds32_output_stack_pop): Move to ... * config/nds32/nds32-md-auxiliary.c: ... here. Co-Authored-By: Kito Cheng <kito@0xlab.org> Co-Authored-By: Monk Chiang <sh.chiang04@gmail.com> From-SVN: r212286
This commit is contained in:
parent
c65cef1051
commit
9e9dbc429c
3 changed files with 832 additions and 779 deletions
|
@ -1,3 +1,20 @@
|
|||
2014-07-04 Chung-Ju Wu <jasonwucj@gmail.com>
|
||||
Kito Cheng <kito@0xlab.org>
|
||||
Monk Chiang <sh.chiang04@gmail.com>
|
||||
|
||||
* config/nds32/nds32.c (nds32_byte_to_size): Move to ...
|
||||
(nds32_output_casesi_pc_relative): Move to ...
|
||||
(nds32_output_casesi): Move to ...
|
||||
(nds32_mem_format): Move to ...
|
||||
(nds32_output_16bit_store): Move to ...
|
||||
(nds32_output_16bit_load): Move to ...
|
||||
(nds32_output_32bit_store): Move to ...
|
||||
(nds32_output_32bit_load): Move to ...
|
||||
(nds32_output_32bit_load_s): Move to ...
|
||||
(nds32_output_stack_push): Move to ...
|
||||
(nds32_output_stack_pop): Move to ...
|
||||
* config/nds32/nds32-md-auxiliary.c: ... here.
|
||||
|
||||
2014-07-04 Chung-Ju Wu <jasonwucj@gmail.com>
|
||||
Ling-Hua Tseng <uranus@tinlans.org>
|
||||
|
||||
|
|
|
@ -18,3 +18,818 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "config.h"
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tm.h"
|
||||
#include "tree.h"
|
||||
#include "stor-layout.h"
|
||||
#include "varasm.h"
|
||||
#include "calls.h"
|
||||
#include "rtl.h"
|
||||
#include "regs.h"
|
||||
#include "hard-reg-set.h"
|
||||
#include "insn-config.h" /* Required by recog.h. */
|
||||
#include "conditions.h"
|
||||
#include "output.h"
|
||||
#include "insn-attr.h" /* For DFA state_t. */
|
||||
#include "insn-codes.h" /* For CODE_FOR_xxx. */
|
||||
#include "reload.h" /* For push_reload(). */
|
||||
#include "flags.h"
|
||||
#include "function.h"
|
||||
#include "expr.h"
|
||||
#include "recog.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "df.h"
|
||||
#include "tm_p.h"
|
||||
#include "tm-constrs.h"
|
||||
#include "optabs.h" /* For GEN_FCN. */
|
||||
#include "target.h"
|
||||
#include "target-def.h"
|
||||
#include "langhooks.h" /* For add_builtin_function(). */
|
||||
#include "ggc.h"
|
||||
#include "builtins.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* A helper function to return character based on byte size. */
|
||||
static char
|
||||
nds32_byte_to_size (int byte)
|
||||
{
|
||||
switch (byte)
|
||||
{
|
||||
case 4:
|
||||
return 'w';
|
||||
case 2:
|
||||
return 'h';
|
||||
case 1:
|
||||
return 'b';
|
||||
default:
|
||||
/* Normally it should not be here. */
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
|
||||
/* A helper function to return memory format. */
|
||||
enum nds32_16bit_address_type
|
||||
nds32_mem_format (rtx op)
|
||||
{
|
||||
enum machine_mode mode_test;
|
||||
int val;
|
||||
int regno;
|
||||
|
||||
if (!TARGET_16_BIT)
|
||||
return ADDRESS_NOT_16BIT_FORMAT;
|
||||
|
||||
mode_test = GET_MODE (op);
|
||||
|
||||
op = XEXP (op, 0);
|
||||
|
||||
/* 45 format. */
|
||||
if (GET_CODE (op) == REG && (mode_test == SImode))
|
||||
return ADDRESS_REG;
|
||||
|
||||
/* 333 format for QI/HImode. */
|
||||
if (GET_CODE (op) == REG && (REGNO (op) < R8_REGNUM))
|
||||
return ADDRESS_LO_REG_IMM3U;
|
||||
|
||||
/* post_inc 333 format. */
|
||||
if ((GET_CODE (op) == POST_INC) && (mode_test == SImode))
|
||||
{
|
||||
regno = REGNO(XEXP (op, 0));
|
||||
|
||||
if (regno < 8)
|
||||
return ADDRESS_POST_INC_LO_REG_IMM3U;
|
||||
}
|
||||
|
||||
/* post_inc 333 format. */
|
||||
if ((GET_CODE (op) == POST_MODIFY)
|
||||
&& (mode_test == SImode)
|
||||
&& (REG_P (XEXP (XEXP (op, 1), 0)))
|
||||
&& (CONST_INT_P (XEXP (XEXP (op, 1), 1))))
|
||||
{
|
||||
regno = REGNO (XEXP (XEXP (op, 1), 0));
|
||||
val = INTVAL (XEXP (XEXP (op, 1), 1));
|
||||
if (regno < 8 && val < 32)
|
||||
return ADDRESS_POST_INC_LO_REG_IMM3U;
|
||||
}
|
||||
|
||||
if ((GET_CODE (op) == PLUS)
|
||||
&& (GET_CODE (XEXP (op, 0)) == REG)
|
||||
&& (GET_CODE (XEXP (op, 1)) == CONST_INT))
|
||||
{
|
||||
val = INTVAL (XEXP (op, 1));
|
||||
|
||||
regno = REGNO(XEXP (op, 0));
|
||||
|
||||
if (regno > 7
|
||||
&& regno != SP_REGNUM
|
||||
&& regno != FP_REGNUM)
|
||||
return ADDRESS_NOT_16BIT_FORMAT;
|
||||
|
||||
switch (mode_test)
|
||||
{
|
||||
case QImode:
|
||||
/* 333 format. */
|
||||
if (val >= 0 && val < 8 && regno < 8)
|
||||
return ADDRESS_LO_REG_IMM3U;
|
||||
break;
|
||||
|
||||
case HImode:
|
||||
/* 333 format. */
|
||||
if (val >= 0 && val < 16 && (val % 2 == 0) && regno < 8)
|
||||
return ADDRESS_LO_REG_IMM3U;
|
||||
break;
|
||||
|
||||
case SImode:
|
||||
case SFmode:
|
||||
case DFmode:
|
||||
/* fp imply 37 format. */
|
||||
if ((regno == FP_REGNUM) &&
|
||||
(val >= 0 && val < 512 && (val % 4 == 0)))
|
||||
return ADDRESS_FP_IMM7U;
|
||||
/* sp imply 37 format. */
|
||||
else if ((regno == SP_REGNUM) &&
|
||||
(val >= 0 && val < 512 && (val % 4 == 0)))
|
||||
return ADDRESS_SP_IMM7U;
|
||||
/* 333 format. */
|
||||
else if (val >= 0 && val < 32 && (val % 4 == 0) && regno < 8)
|
||||
return ADDRESS_LO_REG_IMM3U;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ADDRESS_NOT_16BIT_FORMAT;
|
||||
}
|
||||
|
||||
/* Output 16-bit store. */
|
||||
const char *
|
||||
nds32_output_16bit_store (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
char size;
|
||||
rtx code = XEXP (operands[0], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (nds32_mem_format (operands[0]))
|
||||
{
|
||||
case ADDRESS_REG:
|
||||
operands[0] = code;
|
||||
output_asm_insn ("swi450\t%1, [%0]", operands);
|
||||
break;
|
||||
case ADDRESS_LO_REG_IMM3U:
|
||||
snprintf (pattern, sizeof (pattern), "s%ci333\t%%1, %%0", size);
|
||||
output_asm_insn (pattern, operands);
|
||||
break;
|
||||
case ADDRESS_POST_INC_LO_REG_IMM3U:
|
||||
snprintf (pattern, sizeof (pattern), "s%ci333.bi\t%%1, %%0", size);
|
||||
output_asm_insn (pattern, operands);
|
||||
break;
|
||||
case ADDRESS_FP_IMM7U:
|
||||
output_asm_insn ("swi37\t%1, %0", operands);
|
||||
break;
|
||||
case ADDRESS_SP_IMM7U:
|
||||
/* Get immediate value and set back to operands[1]. */
|
||||
operands[0] = XEXP (code, 1);
|
||||
output_asm_insn ("swi37.sp\t%1, [ + (%0)]", operands);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Output 16-bit load. */
|
||||
const char *
|
||||
nds32_output_16bit_load (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
unsigned char size;
|
||||
rtx code = XEXP (operands[1], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (nds32_mem_format (operands[1]))
|
||||
{
|
||||
case ADDRESS_REG:
|
||||
operands[1] = code;
|
||||
output_asm_insn ("lwi450\t%0, [%1]", operands);
|
||||
break;
|
||||
case ADDRESS_LO_REG_IMM3U:
|
||||
snprintf (pattern, sizeof (pattern), "l%ci333\t%%0, %%1", size);
|
||||
output_asm_insn (pattern, operands);
|
||||
break;
|
||||
case ADDRESS_POST_INC_LO_REG_IMM3U:
|
||||
snprintf (pattern, sizeof (pattern), "l%ci333.bi\t%%0, %%1", size);
|
||||
output_asm_insn (pattern, operands);
|
||||
break;
|
||||
case ADDRESS_FP_IMM7U:
|
||||
output_asm_insn ("lwi37\t%0, %1", operands);
|
||||
break;
|
||||
case ADDRESS_SP_IMM7U:
|
||||
/* Get immediate value and set back to operands[0]. */
|
||||
operands[1] = XEXP (code, 1);
|
||||
output_asm_insn ("lwi37.sp\t%0, [ + (%1)]", operands);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Output 32-bit store. */
|
||||
const char *
|
||||
nds32_output_32bit_store (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
unsigned char size;
|
||||
rtx code = XEXP (operands[0], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (GET_CODE (code))
|
||||
{
|
||||
case REG:
|
||||
/* (mem (reg X))
|
||||
=> access location by using register,
|
||||
use "sbi / shi / swi" */
|
||||
snprintf (pattern, sizeof (pattern), "s%ci\t%%1, %%0", size);
|
||||
break;
|
||||
|
||||
case SYMBOL_REF:
|
||||
case CONST:
|
||||
/* (mem (symbol_ref X))
|
||||
(mem (const (...)))
|
||||
=> access global variables,
|
||||
use "sbi.gp / shi.gp / swi.gp" */
|
||||
operands[0] = XEXP (operands[0], 0);
|
||||
snprintf (pattern, sizeof (pattern), "s%ci.gp\t%%1, [ + %%0]", size);
|
||||
break;
|
||||
|
||||
case POST_INC:
|
||||
/* (mem (post_inc reg))
|
||||
=> access location by using register which will be post increment,
|
||||
use "sbi.bi / shi.bi / swi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"s%ci.bi\t%%1, %%0, %d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_DEC:
|
||||
/* (mem (post_dec reg))
|
||||
=> access location by using register which will be post decrement,
|
||||
use "sbi.bi / shi.bi / swi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"s%ci.bi\t%%1, %%0, -%d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_MODIFY:
|
||||
switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (post_modify (reg) (plus (reg) (reg))))
|
||||
=> access location by using register which will be
|
||||
post modified with reg,
|
||||
use "sb.bi/ sh.bi / sw.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "s%c.bi\t%%1, %%0", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (post_modify (reg) (plus (reg) (const_int))))
|
||||
=> access location by using register which will be
|
||||
post modified with const_int,
|
||||
use "sbi.bi/ shi.bi / swi.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "s%ci.bi\t%%1, %%0", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
switch (GET_CODE (XEXP (code, 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
|
||||
=> access location by adding two registers,
|
||||
use "sb / sh / sw" */
|
||||
snprintf (pattern, sizeof (pattern), "s%c\t%%1, %%0", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (plus reg const_int))
|
||||
=> access location by adding one register with const_int,
|
||||
use "sbi / shi / swi" */
|
||||
snprintf (pattern, sizeof (pattern), "s%ci\t%%1, %%0", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case LO_SUM:
|
||||
operands[2] = XEXP (code, 1);
|
||||
operands[0] = XEXP (code, 0);
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"s%ci\t%%1, [%%0 + lo12(%%2)]", size);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Output 32-bit load. */
|
||||
const char *
|
||||
nds32_output_32bit_load (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
unsigned char size;
|
||||
rtx code;
|
||||
|
||||
code = XEXP (operands[1], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (GET_CODE (code))
|
||||
{
|
||||
case REG:
|
||||
/* (mem (reg X))
|
||||
=> access location by using register,
|
||||
use "lbi / lhi / lwi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%ci\t%%0, %%1", size);
|
||||
break;
|
||||
|
||||
case SYMBOL_REF:
|
||||
case CONST:
|
||||
/* (mem (symbol_ref X))
|
||||
(mem (const (...)))
|
||||
=> access global variables,
|
||||
use "lbi.gp / lhi.gp / lwi.gp" */
|
||||
operands[1] = XEXP (operands[1], 0);
|
||||
snprintf (pattern, sizeof (pattern), "l%ci.gp\t%%0, [ + %%1]", size);
|
||||
break;
|
||||
|
||||
case POST_INC:
|
||||
/* (mem (post_inc reg))
|
||||
=> access location by using register which will be post increment,
|
||||
use "lbi.bi / lhi.bi / lwi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%ci.bi\t%%0, %%1, %d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_DEC:
|
||||
/* (mem (post_dec reg))
|
||||
=> access location by using register which will be post decrement,
|
||||
use "lbi.bi / lhi.bi / lwi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%ci.bi\t%%0, %%1, -%d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_MODIFY:
|
||||
switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (post_modify (reg) (plus (reg) (reg))))
|
||||
=> access location by using register which will be
|
||||
post modified with reg,
|
||||
use "lb.bi/ lh.bi / lw.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%c.bi\t%%0, %%1", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (post_modify (reg) (plus (reg) (const_int))))
|
||||
=> access location by using register which will be
|
||||
post modified with const_int,
|
||||
use "lbi.bi/ lhi.bi / lwi.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%ci.bi\t%%0, %%1", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
switch (GET_CODE (XEXP (code, 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
|
||||
use "lb / lh / lw" */
|
||||
snprintf (pattern, sizeof (pattern), "l%c\t%%0, %%1", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (plus reg const_int))
|
||||
=> access location by adding one register with const_int,
|
||||
use "lbi / lhi / lwi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%ci\t%%0, %%1", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case LO_SUM:
|
||||
operands[2] = XEXP (code, 1);
|
||||
operands[1] = XEXP (code, 0);
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%ci\t%%0, [%%1 + lo12(%%2)]", size);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Output 32-bit load with signed extension. */
|
||||
const char *
|
||||
nds32_output_32bit_load_s (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
unsigned char size;
|
||||
rtx code;
|
||||
|
||||
code = XEXP (operands[1], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (GET_CODE (code))
|
||||
{
|
||||
case REG:
|
||||
/* (mem (reg X))
|
||||
=> access location by using register,
|
||||
use "lbsi / lhsi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%csi\t%%0, %%1", size);
|
||||
break;
|
||||
|
||||
case SYMBOL_REF:
|
||||
case CONST:
|
||||
/* (mem (symbol_ref X))
|
||||
(mem (const (...)))
|
||||
=> access global variables,
|
||||
use "lbsi.gp / lhsi.gp" */
|
||||
operands[1] = XEXP (operands[1], 0);
|
||||
snprintf (pattern, sizeof (pattern), "l%csi.gp\t%%0, [ + %%1]", size);
|
||||
break;
|
||||
|
||||
case POST_INC:
|
||||
/* (mem (post_inc reg))
|
||||
=> access location by using register which will be post increment,
|
||||
use "lbsi.bi / lhsi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%csi.bi\t%%0, %%1, %d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_DEC:
|
||||
/* (mem (post_dec reg))
|
||||
=> access location by using register which will be post decrement,
|
||||
use "lbsi.bi / lhsi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%csi.bi\t%%0, %%1, -%d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_MODIFY:
|
||||
switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (post_modify (reg) (plus (reg) (reg))))
|
||||
=> access location by using register which will be
|
||||
post modified with reg,
|
||||
use "lbs.bi/ lhs.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%cs.bi\t%%0, %%1", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (post_modify (reg) (plus (reg) (const_int))))
|
||||
=> access location by using register which will be
|
||||
post modified with const_int,
|
||||
use "lbsi.bi/ lhsi.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%csi.bi\t%%0, %%1", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
switch (GET_CODE (XEXP (code, 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
|
||||
use "lbs / lhs" */
|
||||
snprintf (pattern, sizeof (pattern), "l%cs\t%%0, %%1", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (plus reg const_int))
|
||||
=> access location by adding one register with const_int,
|
||||
use "lbsi / lhsi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%csi\t%%0, %%1", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case LO_SUM:
|
||||
operands[2] = XEXP (code, 1);
|
||||
operands[1] = XEXP (code, 0);
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%csi\t%%0, [%%1 + lo12(%%2)]", size);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Function to output stack push operation.
|
||||
We need to deal with normal stack push multiple or stack v3push. */
|
||||
const char *
|
||||
nds32_output_stack_push (void)
|
||||
{
|
||||
/* A string pattern for output_asm_insn(). */
|
||||
char pattern[100];
|
||||
/* The operands array which will be used in output_asm_insn(). */
|
||||
rtx operands[3];
|
||||
/* Pick up callee-saved first regno and last regno for further use. */
|
||||
int rb_regno = cfun->machine->callee_saved_regs_first_regno;
|
||||
int re_regno = cfun->machine->callee_saved_regs_last_regno;
|
||||
|
||||
if (TARGET_V3PUSH)
|
||||
{
|
||||
/* For stack v3push:
|
||||
operands[0]: Re
|
||||
operands[1]: imm8u */
|
||||
|
||||
/* This variable is to check if 'push25 Re,imm8u' is available. */
|
||||
int sp_adjust;
|
||||
|
||||
/* Set operands[0]. */
|
||||
operands[0] = gen_rtx_REG (SImode, re_regno);
|
||||
|
||||
/* Check if we can generate 'push25 Re,imm8u',
|
||||
otherwise, generate 'push25 Re,0'. */
|
||||
sp_adjust = cfun->machine->local_size
|
||||
+ cfun->machine->out_args_size
|
||||
+ cfun->machine->callee_saved_area_padding_bytes;
|
||||
if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust))
|
||||
&& NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust))
|
||||
operands[1] = GEN_INT (sp_adjust);
|
||||
else
|
||||
operands[1] = GEN_INT (0);
|
||||
|
||||
/* Create assembly code pattern. */
|
||||
snprintf (pattern, sizeof (pattern), "push25\t%%0, %%1");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For normal stack push multiple:
|
||||
operands[0]: Rb
|
||||
operands[1]: Re
|
||||
operands[2]: En4 */
|
||||
|
||||
/* This variable is used to check if we only need to generate En4 field.
|
||||
As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
|
||||
int push_en4_only_p = 0;
|
||||
|
||||
/* Set operands[0] and operands[1]. */
|
||||
operands[0] = gen_rtx_REG (SImode, rb_regno);
|
||||
operands[1] = gen_rtx_REG (SImode, re_regno);
|
||||
|
||||
/* 'smw.adm $sp,[$sp],$sp,0' means push nothing. */
|
||||
if (!cfun->machine->fp_size
|
||||
&& !cfun->machine->gp_size
|
||||
&& !cfun->machine->lp_size
|
||||
&& REGNO (operands[0]) == SP_REGNUM
|
||||
&& REGNO (operands[1]) == SP_REGNUM)
|
||||
{
|
||||
/* No need to generate instruction. */
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
|
||||
if (REGNO (operands[0]) == SP_REGNUM
|
||||
&& REGNO (operands[1]) == SP_REGNUM)
|
||||
push_en4_only_p = 1;
|
||||
|
||||
/* Create assembly code pattern.
|
||||
We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"push.s\t%s{%s%s%s }",
|
||||
push_en4_only_p ? "" : "%0, %1, ",
|
||||
cfun->machine->fp_size ? " $fp" : "",
|
||||
cfun->machine->gp_size ? " $gp" : "",
|
||||
cfun->machine->lp_size ? " $lp" : "");
|
||||
}
|
||||
}
|
||||
|
||||
/* We use output_asm_insn() to output assembly code by ourself. */
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Function to output stack pop operation.
|
||||
We need to deal with normal stack pop multiple or stack v3pop. */
|
||||
const char *
|
||||
nds32_output_stack_pop (void)
|
||||
{
|
||||
/* A string pattern for output_asm_insn(). */
|
||||
char pattern[100];
|
||||
/* The operands array which will be used in output_asm_insn(). */
|
||||
rtx operands[3];
|
||||
/* Pick up callee-saved first regno and last regno for further use. */
|
||||
int rb_regno = cfun->machine->callee_saved_regs_first_regno;
|
||||
int re_regno = cfun->machine->callee_saved_regs_last_regno;
|
||||
|
||||
if (TARGET_V3PUSH)
|
||||
{
|
||||
/* For stack v3pop:
|
||||
operands[0]: Re
|
||||
operands[1]: imm8u */
|
||||
|
||||
/* This variable is to check if 'pop25 Re,imm8u' is available. */
|
||||
int sp_adjust;
|
||||
|
||||
/* Set operands[0]. */
|
||||
operands[0] = gen_rtx_REG (SImode, re_regno);
|
||||
|
||||
/* Check if we can generate 'pop25 Re,imm8u',
|
||||
otherwise, generate 'pop25 Re,0'.
|
||||
We have to consider alloca issue as well.
|
||||
If the function does call alloca(), the stack pointer is not fixed.
|
||||
In that case, we cannot use 'pop25 Re,imm8u' directly.
|
||||
We have to caculate stack pointer from frame pointer
|
||||
and then use 'pop25 Re,0'. */
|
||||
sp_adjust = cfun->machine->local_size
|
||||
+ cfun->machine->out_args_size
|
||||
+ cfun->machine->callee_saved_area_padding_bytes;
|
||||
if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust))
|
||||
&& NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust)
|
||||
&& !cfun->calls_alloca)
|
||||
operands[1] = GEN_INT (sp_adjust);
|
||||
else
|
||||
operands[1] = GEN_INT (0);
|
||||
|
||||
/* Create assembly code pattern. */
|
||||
snprintf (pattern, sizeof (pattern), "pop25\t%%0, %%1");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For normal stack pop multiple:
|
||||
operands[0]: Rb
|
||||
operands[1]: Re
|
||||
operands[2]: En4 */
|
||||
|
||||
/* This variable is used to check if we only need to generate En4 field.
|
||||
As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
|
||||
int pop_en4_only_p = 0;
|
||||
|
||||
/* Set operands[0] and operands[1]. */
|
||||
operands[0] = gen_rtx_REG (SImode, rb_regno);
|
||||
operands[1] = gen_rtx_REG (SImode, re_regno);
|
||||
|
||||
/* 'lmw.bim $sp,[$sp],$sp,0' means pop nothing. */
|
||||
if (!cfun->machine->fp_size
|
||||
&& !cfun->machine->gp_size
|
||||
&& !cfun->machine->lp_size
|
||||
&& REGNO (operands[0]) == SP_REGNUM
|
||||
&& REGNO (operands[1]) == SP_REGNUM)
|
||||
{
|
||||
/* No need to generate instruction. */
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
|
||||
if (REGNO (operands[0]) == SP_REGNUM
|
||||
&& REGNO (operands[1]) == SP_REGNUM)
|
||||
pop_en4_only_p = 1;
|
||||
|
||||
/* Create assembly code pattern.
|
||||
We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"pop.s\t%s{%s%s%s }",
|
||||
pop_en4_only_p ? "" : "%0, %1, ",
|
||||
cfun->machine->fp_size ? " $fp" : "",
|
||||
cfun->machine->gp_size ? " $gp" : "",
|
||||
cfun->machine->lp_size ? " $lp" : "");
|
||||
}
|
||||
}
|
||||
|
||||
/* We use output_asm_insn() to output assembly code by ourself. */
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Function to generate PC relative jump table.
|
||||
Refer to nds32.md for more details.
|
||||
|
||||
The following is the sample for the case that diff value
|
||||
can be presented in '.short' size.
|
||||
|
||||
addi $r1, $r1, -(case_lower_bound)
|
||||
slti $ta, $r1, (case_number)
|
||||
beqz $ta, .L_skip_label
|
||||
|
||||
la $ta, .L35 ! get jump table address
|
||||
lh $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry
|
||||
addi $ta, $r1, $ta
|
||||
jr5 $ta
|
||||
|
||||
! jump table entry
|
||||
L35:
|
||||
.short .L25-.L35
|
||||
.short .L26-.L35
|
||||
.short .L27-.L35
|
||||
.short .L28-.L35
|
||||
.short .L29-.L35
|
||||
.short .L30-.L35
|
||||
.short .L31-.L35
|
||||
.short .L32-.L35
|
||||
.short .L33-.L35
|
||||
.short .L34-.L35 */
|
||||
const char *
|
||||
nds32_output_casesi_pc_relative (rtx *operands)
|
||||
{
|
||||
enum machine_mode mode;
|
||||
rtx diff_vec;
|
||||
|
||||
diff_vec = PATTERN (NEXT_INSN (operands[1]));
|
||||
|
||||
gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
|
||||
|
||||
/* Step C: "t <-- operands[1]". */
|
||||
output_asm_insn ("la\t$ta, %l1", operands);
|
||||
|
||||
/* Get the mode of each element in the difference vector. */
|
||||
mode = GET_MODE (diff_vec);
|
||||
|
||||
/* Step D: "z <-- (mem (plus (operands[0] << m) t))",
|
||||
where m is 0, 1, or 2 to load address-diff value from table. */
|
||||
switch (mode)
|
||||
{
|
||||
case QImode:
|
||||
output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands);
|
||||
break;
|
||||
case HImode:
|
||||
output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands);
|
||||
break;
|
||||
case SImode:
|
||||
output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Step E: "t <-- z + t".
|
||||
Add table label_ref with address-diff value to
|
||||
obtain target case address. */
|
||||
output_asm_insn ("add\t$ta, %2, $ta", operands);
|
||||
|
||||
/* Step F: jump to target with register t. */
|
||||
if (TARGET_16_BIT)
|
||||
return "jr5\t$ta";
|
||||
else
|
||||
return "jr\t$ta";
|
||||
}
|
||||
|
||||
/* Function to generate normal jump table. */
|
||||
const char *
|
||||
nds32_output_casesi (rtx *operands)
|
||||
{
|
||||
/* Step C: "t <-- operands[1]". */
|
||||
output_asm_insn ("la\t$ta, %l1", operands);
|
||||
|
||||
/* Step D: "z <-- (mem (plus (operands[0] << 2) t))". */
|
||||
output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
|
||||
|
||||
/* No need to perform Step E, which is only used for
|
||||
pc relative jump table. */
|
||||
|
||||
/* Step F: jump to target with register z. */
|
||||
if (TARGET_16_BIT)
|
||||
return "jr5\t%2";
|
||||
else
|
||||
return "jr\t%2";
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
|
|
@ -1106,24 +1106,6 @@ nds32_legitimate_index_p (enum machine_mode outer_mode,
|
|||
}
|
||||
}
|
||||
|
||||
/* A helper function to return character based on byte size. */
|
||||
static char
|
||||
nds32_byte_to_size (int byte)
|
||||
{
|
||||
switch (byte)
|
||||
{
|
||||
case 4:
|
||||
return 'w';
|
||||
case 2:
|
||||
return 'h';
|
||||
case 1:
|
||||
return 'b';
|
||||
default:
|
||||
/* Normally it should not be here. */
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
|
||||
/* A helper function to check if this function should contain prologue. */
|
||||
static int
|
||||
nds32_have_prologue_p (void)
|
||||
|
@ -3436,767 +3418,6 @@ nds32_fp_as_gp_check_available (void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Function to generate PC relative jump table.
|
||||
Refer to nds32.md for more details.
|
||||
|
||||
The following is the sample for the case that diff value
|
||||
can be presented in '.short' size.
|
||||
|
||||
addi $r1, $r1, -(case_lower_bound)
|
||||
slti $ta, $r1, (case_number)
|
||||
beqz $ta, .L_skip_label
|
||||
|
||||
la $ta, .L35 ! get jump table address
|
||||
lh $r1, [$ta + $r1 << 1] ! load symbol diff from jump table entry
|
||||
addi $ta, $r1, $ta
|
||||
jr5 $ta
|
||||
|
||||
! jump table entry
|
||||
L35:
|
||||
.short .L25-.L35
|
||||
.short .L26-.L35
|
||||
.short .L27-.L35
|
||||
.short .L28-.L35
|
||||
.short .L29-.L35
|
||||
.short .L30-.L35
|
||||
.short .L31-.L35
|
||||
.short .L32-.L35
|
||||
.short .L33-.L35
|
||||
.short .L34-.L35 */
|
||||
const char *
|
||||
nds32_output_casesi_pc_relative (rtx *operands)
|
||||
{
|
||||
enum machine_mode mode;
|
||||
rtx diff_vec;
|
||||
|
||||
diff_vec = PATTERN (NEXT_INSN (operands[1]));
|
||||
|
||||
gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
|
||||
|
||||
/* Step C: "t <-- operands[1]". */
|
||||
output_asm_insn ("la\t$ta, %l1", operands);
|
||||
|
||||
/* Get the mode of each element in the difference vector. */
|
||||
mode = GET_MODE (diff_vec);
|
||||
|
||||
/* Step D: "z <-- (mem (plus (operands[0] << m) t))",
|
||||
where m is 0, 1, or 2 to load address-diff value from table. */
|
||||
switch (mode)
|
||||
{
|
||||
case QImode:
|
||||
output_asm_insn ("lb\t%2, [$ta + %0 << 0]", operands);
|
||||
break;
|
||||
case HImode:
|
||||
output_asm_insn ("lh\t%2, [$ta + %0 << 1]", operands);
|
||||
break;
|
||||
case SImode:
|
||||
output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
/* Step E: "t <-- z + t".
|
||||
Add table label_ref with address-diff value to
|
||||
obtain target case address. */
|
||||
output_asm_insn ("add\t$ta, %2, $ta", operands);
|
||||
|
||||
/* Step F: jump to target with register t. */
|
||||
if (TARGET_16_BIT)
|
||||
return "jr5\t$ta";
|
||||
else
|
||||
return "jr\t$ta";
|
||||
}
|
||||
|
||||
/* Function to generate normal jump table. */
|
||||
const char *
|
||||
nds32_output_casesi (rtx *operands)
|
||||
{
|
||||
/* Step C: "t <-- operands[1]". */
|
||||
output_asm_insn ("la\t$ta, %l1", operands);
|
||||
|
||||
/* Step D: "z <-- (mem (plus (operands[0] << 2) t))". */
|
||||
output_asm_insn ("lw\t%2, [$ta + %0 << 2]", operands);
|
||||
|
||||
/* No need to perform Step E, which is only used for
|
||||
pc relative jump table. */
|
||||
|
||||
/* Step F: jump to target with register z. */
|
||||
if (TARGET_16_BIT)
|
||||
return "jr5\t%2";
|
||||
else
|
||||
return "jr\t%2";
|
||||
}
|
||||
|
||||
|
||||
/* Function to return memory format. */
|
||||
enum nds32_16bit_address_type
|
||||
nds32_mem_format (rtx op)
|
||||
{
|
||||
enum machine_mode mode_test;
|
||||
int val;
|
||||
int regno;
|
||||
|
||||
if (!TARGET_16_BIT)
|
||||
return ADDRESS_NOT_16BIT_FORMAT;
|
||||
|
||||
mode_test = GET_MODE (op);
|
||||
|
||||
op = XEXP (op, 0);
|
||||
|
||||
/* 45 format. */
|
||||
if (GET_CODE (op) == REG && (mode_test == SImode))
|
||||
return ADDRESS_REG;
|
||||
|
||||
/* 333 format for QI/HImode. */
|
||||
if (GET_CODE (op) == REG && (REGNO (op) < R8_REGNUM))
|
||||
return ADDRESS_LO_REG_IMM3U;
|
||||
|
||||
/* post_inc 333 format. */
|
||||
if ((GET_CODE (op) == POST_INC) && (mode_test == SImode))
|
||||
{
|
||||
regno = REGNO(XEXP (op, 0));
|
||||
|
||||
if (regno < 8)
|
||||
return ADDRESS_POST_INC_LO_REG_IMM3U;
|
||||
}
|
||||
|
||||
/* post_inc 333 format. */
|
||||
if ((GET_CODE (op) == POST_MODIFY)
|
||||
&& (mode_test == SImode)
|
||||
&& (REG_P (XEXP (XEXP (op, 1), 0)))
|
||||
&& (CONST_INT_P (XEXP (XEXP (op, 1), 1))))
|
||||
{
|
||||
regno = REGNO (XEXP (XEXP (op, 1), 0));
|
||||
val = INTVAL (XEXP (XEXP (op, 1), 1));
|
||||
if (regno < 8 && val < 32)
|
||||
return ADDRESS_POST_INC_LO_REG_IMM3U;
|
||||
}
|
||||
|
||||
if ((GET_CODE (op) == PLUS)
|
||||
&& (GET_CODE (XEXP (op, 0)) == REG)
|
||||
&& (GET_CODE (XEXP (op, 1)) == CONST_INT))
|
||||
{
|
||||
val = INTVAL (XEXP (op, 1));
|
||||
|
||||
regno = REGNO(XEXP (op, 0));
|
||||
|
||||
if (regno > 7
|
||||
&& regno != SP_REGNUM
|
||||
&& regno != FP_REGNUM)
|
||||
return ADDRESS_NOT_16BIT_FORMAT;
|
||||
|
||||
switch (mode_test)
|
||||
{
|
||||
case QImode:
|
||||
/* 333 format. */
|
||||
if (val >= 0 && val < 8 && regno < 8)
|
||||
return ADDRESS_LO_REG_IMM3U;
|
||||
break;
|
||||
|
||||
case HImode:
|
||||
/* 333 format. */
|
||||
if (val >= 0 && val < 16 && (val % 2 == 0) && regno < 8)
|
||||
return ADDRESS_LO_REG_IMM3U;
|
||||
break;
|
||||
|
||||
case SImode:
|
||||
case SFmode:
|
||||
case DFmode:
|
||||
/* fp imply 37 format. */
|
||||
if ((regno == FP_REGNUM) &&
|
||||
(val >= 0 && val < 512 && (val % 4 == 0)))
|
||||
return ADDRESS_FP_IMM7U;
|
||||
/* sp imply 37 format. */
|
||||
else if ((regno == SP_REGNUM) &&
|
||||
(val >= 0 && val < 512 && (val % 4 == 0)))
|
||||
return ADDRESS_SP_IMM7U;
|
||||
/* 333 format. */
|
||||
else if (val >= 0 && val < 32 && (val % 4 == 0) && regno < 8)
|
||||
return ADDRESS_LO_REG_IMM3U;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ADDRESS_NOT_16BIT_FORMAT;
|
||||
}
|
||||
|
||||
/* Output 16-bit store. */
|
||||
const char *
|
||||
nds32_output_16bit_store (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
char size;
|
||||
rtx code = XEXP (operands[0], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (nds32_mem_format (operands[0]))
|
||||
{
|
||||
case ADDRESS_REG:
|
||||
operands[0] = code;
|
||||
output_asm_insn ("swi450\t%1, [%0]", operands);
|
||||
break;
|
||||
case ADDRESS_LO_REG_IMM3U:
|
||||
snprintf (pattern, sizeof (pattern), "s%ci333\t%%1, %%0", size);
|
||||
output_asm_insn (pattern, operands);
|
||||
break;
|
||||
case ADDRESS_POST_INC_LO_REG_IMM3U:
|
||||
snprintf (pattern, sizeof (pattern), "s%ci333.bi\t%%1, %%0", size);
|
||||
output_asm_insn (pattern, operands);
|
||||
break;
|
||||
case ADDRESS_FP_IMM7U:
|
||||
output_asm_insn ("swi37\t%1, %0", operands);
|
||||
break;
|
||||
case ADDRESS_SP_IMM7U:
|
||||
/* Get immediate value and set back to operands[1]. */
|
||||
operands[0] = XEXP (code, 1);
|
||||
output_asm_insn ("swi37.sp\t%1, [ + (%0)]", operands);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Output 16-bit load. */
|
||||
const char *
|
||||
nds32_output_16bit_load (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
unsigned char size;
|
||||
rtx code = XEXP (operands[1], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (nds32_mem_format (operands[1]))
|
||||
{
|
||||
case ADDRESS_REG:
|
||||
operands[1] = code;
|
||||
output_asm_insn ("lwi450\t%0, [%1]", operands);
|
||||
break;
|
||||
case ADDRESS_LO_REG_IMM3U:
|
||||
snprintf (pattern, sizeof (pattern), "l%ci333\t%%0, %%1", size);
|
||||
output_asm_insn (pattern, operands);
|
||||
break;
|
||||
case ADDRESS_POST_INC_LO_REG_IMM3U:
|
||||
snprintf (pattern, sizeof (pattern), "l%ci333.bi\t%%0, %%1", size);
|
||||
output_asm_insn (pattern, operands);
|
||||
break;
|
||||
case ADDRESS_FP_IMM7U:
|
||||
output_asm_insn ("lwi37\t%0, %1", operands);
|
||||
break;
|
||||
case ADDRESS_SP_IMM7U:
|
||||
/* Get immediate value and set back to operands[0]. */
|
||||
operands[1] = XEXP (code, 1);
|
||||
output_asm_insn ("lwi37.sp\t%0, [ + (%1)]", operands);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Output 32-bit store. */
|
||||
const char *
|
||||
nds32_output_32bit_store (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
unsigned char size;
|
||||
rtx code = XEXP (operands[0], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (GET_CODE (code))
|
||||
{
|
||||
case REG:
|
||||
/* (mem (reg X))
|
||||
=> access location by using register,
|
||||
use "sbi / shi / swi" */
|
||||
snprintf (pattern, sizeof (pattern), "s%ci\t%%1, %%0", size);
|
||||
break;
|
||||
|
||||
case SYMBOL_REF:
|
||||
case CONST:
|
||||
/* (mem (symbol_ref X))
|
||||
(mem (const (...)))
|
||||
=> access global variables,
|
||||
use "sbi.gp / shi.gp / swi.gp" */
|
||||
operands[0] = XEXP (operands[0], 0);
|
||||
snprintf (pattern, sizeof (pattern), "s%ci.gp\t%%1, [ + %%0]", size);
|
||||
break;
|
||||
|
||||
case POST_INC:
|
||||
/* (mem (post_inc reg))
|
||||
=> access location by using register which will be post increment,
|
||||
use "sbi.bi / shi.bi / swi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"s%ci.bi\t%%1, %%0, %d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_DEC:
|
||||
/* (mem (post_dec reg))
|
||||
=> access location by using register which will be post decrement,
|
||||
use "sbi.bi / shi.bi / swi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"s%ci.bi\t%%1, %%0, -%d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_MODIFY:
|
||||
switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (post_modify (reg) (plus (reg) (reg))))
|
||||
=> access location by using register which will be
|
||||
post modified with reg,
|
||||
use "sb.bi/ sh.bi / sw.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "s%c.bi\t%%1, %%0", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (post_modify (reg) (plus (reg) (const_int))))
|
||||
=> access location by using register which will be
|
||||
post modified with const_int,
|
||||
use "sbi.bi/ shi.bi / swi.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "s%ci.bi\t%%1, %%0", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
switch (GET_CODE (XEXP (code, 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
|
||||
=> access location by adding two registers,
|
||||
use "sb / sh / sw" */
|
||||
snprintf (pattern, sizeof (pattern), "s%c\t%%1, %%0", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (plus reg const_int))
|
||||
=> access location by adding one register with const_int,
|
||||
use "sbi / shi / swi" */
|
||||
snprintf (pattern, sizeof (pattern), "s%ci\t%%1, %%0", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case LO_SUM:
|
||||
operands[2] = XEXP (code, 1);
|
||||
operands[0] = XEXP (code, 0);
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"s%ci\t%%1, [%%0 + lo12(%%2)]", size);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Output 32-bit load. */
|
||||
const char *
|
||||
nds32_output_32bit_load (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
unsigned char size;
|
||||
rtx code;
|
||||
|
||||
code = XEXP (operands[1], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (GET_CODE (code))
|
||||
{
|
||||
case REG:
|
||||
/* (mem (reg X))
|
||||
=> access location by using register,
|
||||
use "lbi / lhi / lwi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%ci\t%%0, %%1", size);
|
||||
break;
|
||||
|
||||
case SYMBOL_REF:
|
||||
case CONST:
|
||||
/* (mem (symbol_ref X))
|
||||
(mem (const (...)))
|
||||
=> access global variables,
|
||||
use "lbi.gp / lhi.gp / lwi.gp" */
|
||||
operands[1] = XEXP (operands[1], 0);
|
||||
snprintf (pattern, sizeof (pattern), "l%ci.gp\t%%0, [ + %%1]", size);
|
||||
break;
|
||||
|
||||
case POST_INC:
|
||||
/* (mem (post_inc reg))
|
||||
=> access location by using register which will be post increment,
|
||||
use "lbi.bi / lhi.bi / lwi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%ci.bi\t%%0, %%1, %d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_DEC:
|
||||
/* (mem (post_dec reg))
|
||||
=> access location by using register which will be post decrement,
|
||||
use "lbi.bi / lhi.bi / lwi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%ci.bi\t%%0, %%1, -%d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_MODIFY:
|
||||
switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (post_modify (reg) (plus (reg) (reg))))
|
||||
=> access location by using register which will be
|
||||
post modified with reg,
|
||||
use "lb.bi/ lh.bi / lw.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%c.bi\t%%0, %%1", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (post_modify (reg) (plus (reg) (const_int))))
|
||||
=> access location by using register which will be
|
||||
post modified with const_int,
|
||||
use "lbi.bi/ lhi.bi / lwi.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%ci.bi\t%%0, %%1", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
switch (GET_CODE (XEXP (code, 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
|
||||
use "lb / lh / lw" */
|
||||
snprintf (pattern, sizeof (pattern), "l%c\t%%0, %%1", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (plus reg const_int))
|
||||
=> access location by adding one register with const_int,
|
||||
use "lbi / lhi / lwi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%ci\t%%0, %%1", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case LO_SUM:
|
||||
operands[2] = XEXP (code, 1);
|
||||
operands[1] = XEXP (code, 0);
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%ci\t%%0, [%%1 + lo12(%%2)]", size);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Output 32-bit load with signed extension. */
|
||||
const char *
|
||||
nds32_output_32bit_load_s (rtx *operands, int byte)
|
||||
{
|
||||
char pattern[100];
|
||||
unsigned char size;
|
||||
rtx code;
|
||||
|
||||
code = XEXP (operands[1], 0);
|
||||
|
||||
size = nds32_byte_to_size (byte);
|
||||
|
||||
switch (GET_CODE (code))
|
||||
{
|
||||
case REG:
|
||||
/* (mem (reg X))
|
||||
=> access location by using register,
|
||||
use "lbsi / lhsi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%csi\t%%0, %%1", size);
|
||||
break;
|
||||
|
||||
case SYMBOL_REF:
|
||||
case CONST:
|
||||
/* (mem (symbol_ref X))
|
||||
(mem (const (...)))
|
||||
=> access global variables,
|
||||
use "lbsi.gp / lhsi.gp" */
|
||||
operands[1] = XEXP (operands[1], 0);
|
||||
snprintf (pattern, sizeof (pattern), "l%csi.gp\t%%0, [ + %%1]", size);
|
||||
break;
|
||||
|
||||
case POST_INC:
|
||||
/* (mem (post_inc reg))
|
||||
=> access location by using register which will be post increment,
|
||||
use "lbsi.bi / lhsi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%csi.bi\t%%0, %%1, %d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_DEC:
|
||||
/* (mem (post_dec reg))
|
||||
=> access location by using register which will be post decrement,
|
||||
use "lbsi.bi / lhsi.bi" */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%csi.bi\t%%0, %%1, -%d", size, byte);
|
||||
break;
|
||||
|
||||
case POST_MODIFY:
|
||||
switch (GET_CODE (XEXP (XEXP (code, 1), 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (post_modify (reg) (plus (reg) (reg))))
|
||||
=> access location by using register which will be
|
||||
post modified with reg,
|
||||
use "lbs.bi/ lhs.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%cs.bi\t%%0, %%1", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (post_modify (reg) (plus (reg) (const_int))))
|
||||
=> access location by using register which will be
|
||||
post modified with const_int,
|
||||
use "lbsi.bi/ lhsi.bi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%csi.bi\t%%0, %%1", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case PLUS:
|
||||
switch (GET_CODE (XEXP (code, 1)))
|
||||
{
|
||||
case REG:
|
||||
case SUBREG:
|
||||
/* (mem (plus reg reg)) or (mem (plus (mult reg const_int) reg))
|
||||
use "lbs / lhs" */
|
||||
snprintf (pattern, sizeof (pattern), "l%cs\t%%0, %%1", size);
|
||||
break;
|
||||
case CONST_INT:
|
||||
/* (mem (plus reg const_int))
|
||||
=> access location by adding one register with const_int,
|
||||
use "lbsi / lhsi" */
|
||||
snprintf (pattern, sizeof (pattern), "l%csi\t%%0, %%1", size);
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
break;
|
||||
|
||||
case LO_SUM:
|
||||
operands[2] = XEXP (code, 1);
|
||||
operands[1] = XEXP (code, 0);
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"l%csi\t%%0, [%%1 + lo12(%%2)]", size);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Function to output stack push operation.
|
||||
We need to deal with normal stack push multiple or stack v3push. */
|
||||
const char *
|
||||
nds32_output_stack_push (void)
|
||||
{
|
||||
/* A string pattern for output_asm_insn(). */
|
||||
char pattern[100];
|
||||
/* The operands array which will be used in output_asm_insn(). */
|
||||
rtx operands[3];
|
||||
/* Pick up callee-saved first regno and last regno for further use. */
|
||||
int rb_regno = cfun->machine->callee_saved_regs_first_regno;
|
||||
int re_regno = cfun->machine->callee_saved_regs_last_regno;
|
||||
|
||||
if (TARGET_V3PUSH)
|
||||
{
|
||||
/* For stack v3push:
|
||||
operands[0]: Re
|
||||
operands[1]: imm8u */
|
||||
|
||||
/* This variable is to check if 'push25 Re,imm8u' is available. */
|
||||
int sp_adjust;
|
||||
|
||||
/* Set operands[0]. */
|
||||
operands[0] = gen_rtx_REG (SImode, re_regno);
|
||||
|
||||
/* Check if we can generate 'push25 Re,imm8u',
|
||||
otherwise, generate 'push25 Re,0'. */
|
||||
sp_adjust = cfun->machine->local_size
|
||||
+ cfun->machine->out_args_size
|
||||
+ cfun->machine->callee_saved_area_padding_bytes;
|
||||
if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust))
|
||||
&& NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust))
|
||||
operands[1] = GEN_INT (sp_adjust);
|
||||
else
|
||||
operands[1] = GEN_INT (0);
|
||||
|
||||
/* Create assembly code pattern. */
|
||||
snprintf (pattern, sizeof (pattern), "push25\t%%0, %%1");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For normal stack push multiple:
|
||||
operands[0]: Rb
|
||||
operands[1]: Re
|
||||
operands[2]: En4 */
|
||||
|
||||
/* This variable is used to check if we only need to generate En4 field.
|
||||
As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
|
||||
int push_en4_only_p = 0;
|
||||
|
||||
/* Set operands[0] and operands[1]. */
|
||||
operands[0] = gen_rtx_REG (SImode, rb_regno);
|
||||
operands[1] = gen_rtx_REG (SImode, re_regno);
|
||||
|
||||
/* 'smw.adm $sp,[$sp],$sp,0' means push nothing. */
|
||||
if (!cfun->machine->fp_size
|
||||
&& !cfun->machine->gp_size
|
||||
&& !cfun->machine->lp_size
|
||||
&& REGNO (operands[0]) == SP_REGNUM
|
||||
&& REGNO (operands[1]) == SP_REGNUM)
|
||||
{
|
||||
/* No need to generate instruction. */
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
|
||||
if (REGNO (operands[0]) == SP_REGNUM
|
||||
&& REGNO (operands[1]) == SP_REGNUM)
|
||||
push_en4_only_p = 1;
|
||||
|
||||
/* Create assembly code pattern.
|
||||
We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"push.s\t%s{%s%s%s }",
|
||||
push_en4_only_p ? "" : "%0, %1, ",
|
||||
cfun->machine->fp_size ? " $fp" : "",
|
||||
cfun->machine->gp_size ? " $gp" : "",
|
||||
cfun->machine->lp_size ? " $lp" : "");
|
||||
}
|
||||
}
|
||||
|
||||
/* We use output_asm_insn() to output assembly code by ourself. */
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Function to output stack pop operation.
|
||||
We need to deal with normal stack pop multiple or stack v3pop. */
|
||||
const char *
|
||||
nds32_output_stack_pop (void)
|
||||
{
|
||||
/* A string pattern for output_asm_insn(). */
|
||||
char pattern[100];
|
||||
/* The operands array which will be used in output_asm_insn(). */
|
||||
rtx operands[3];
|
||||
/* Pick up callee-saved first regno and last regno for further use. */
|
||||
int rb_regno = cfun->machine->callee_saved_regs_first_regno;
|
||||
int re_regno = cfun->machine->callee_saved_regs_last_regno;
|
||||
|
||||
if (TARGET_V3PUSH)
|
||||
{
|
||||
/* For stack v3pop:
|
||||
operands[0]: Re
|
||||
operands[1]: imm8u */
|
||||
|
||||
/* This variable is to check if 'pop25 Re,imm8u' is available. */
|
||||
int sp_adjust;
|
||||
|
||||
/* Set operands[0]. */
|
||||
operands[0] = gen_rtx_REG (SImode, re_regno);
|
||||
|
||||
/* Check if we can generate 'pop25 Re,imm8u',
|
||||
otherwise, generate 'pop25 Re,0'.
|
||||
We have to consider alloca issue as well.
|
||||
If the function does call alloca(), the stack pointer is not fixed.
|
||||
In that case, we cannot use 'pop25 Re,imm8u' directly.
|
||||
We have to caculate stack pointer from frame pointer
|
||||
and then use 'pop25 Re,0'. */
|
||||
sp_adjust = cfun->machine->local_size
|
||||
+ cfun->machine->out_args_size
|
||||
+ cfun->machine->callee_saved_area_padding_bytes;
|
||||
if (satisfies_constraint_Iu08 (GEN_INT (sp_adjust))
|
||||
&& NDS32_DOUBLE_WORD_ALIGN_P (sp_adjust)
|
||||
&& !cfun->calls_alloca)
|
||||
operands[1] = GEN_INT (sp_adjust);
|
||||
else
|
||||
operands[1] = GEN_INT (0);
|
||||
|
||||
/* Create assembly code pattern. */
|
||||
snprintf (pattern, sizeof (pattern), "pop25\t%%0, %%1");
|
||||
}
|
||||
else
|
||||
{
|
||||
/* For normal stack pop multiple:
|
||||
operands[0]: Rb
|
||||
operands[1]: Re
|
||||
operands[2]: En4 */
|
||||
|
||||
/* This variable is used to check if we only need to generate En4 field.
|
||||
As long as Rb==Re=SP_REGNUM, we set this variable to 1. */
|
||||
int pop_en4_only_p = 0;
|
||||
|
||||
/* Set operands[0] and operands[1]. */
|
||||
operands[0] = gen_rtx_REG (SImode, rb_regno);
|
||||
operands[1] = gen_rtx_REG (SImode, re_regno);
|
||||
|
||||
/* 'lmw.bim $sp,[$sp],$sp,0' means pop nothing. */
|
||||
if (!cfun->machine->fp_size
|
||||
&& !cfun->machine->gp_size
|
||||
&& !cfun->machine->lp_size
|
||||
&& REGNO (operands[0]) == SP_REGNUM
|
||||
&& REGNO (operands[1]) == SP_REGNUM)
|
||||
{
|
||||
/* No need to generate instruction. */
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If Rb==Re=SP_REGNUM, we only need to generate En4 field. */
|
||||
if (REGNO (operands[0]) == SP_REGNUM
|
||||
&& REGNO (operands[1]) == SP_REGNUM)
|
||||
pop_en4_only_p = 1;
|
||||
|
||||
/* Create assembly code pattern.
|
||||
We need to handle the form: "Rb, Re, { $fp $gp $lp }". */
|
||||
snprintf (pattern, sizeof (pattern),
|
||||
"pop.s\t%s{%s%s%s }",
|
||||
pop_en4_only_p ? "" : "%0, %1, ",
|
||||
cfun->machine->fp_size ? " $fp" : "",
|
||||
cfun->machine->gp_size ? " $gp" : "",
|
||||
cfun->machine->lp_size ? " $lp" : "");
|
||||
}
|
||||
}
|
||||
|
||||
/* We use output_asm_insn() to output assembly code by ourself. */
|
||||
output_asm_insn (pattern, operands);
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Return align 2 (log base 2) if the next instruction of LABEL is 4 byte. */
|
||||
int
|
||||
nds32_target_alignment (rtx label)
|
||||
|
|
Loading…
Add table
Reference in a new issue