#include "backend.h"
#include "target.h"
#include "rtl.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
#include "tree.h"
#include "memmodel.h"
#include "optabs.h" /* For GEN_FCN. */
#include "stor-layout.h"
#include "expr.h"
#include "langhooks.h" /* For add_builtin_function(). */
+#include "recog.h"
+#include "explow.h"
/* ------------------------------------------------------------------------ */
-/* Function to expand builtin function for
- '[(unspec_volatile [(reg)])]'. */
+/* Read the requested argument from the EXP given by INDEX.
+ Return the value as an rtx. */
static rtx
-nds32_expand_builtin_null_ftype_reg (enum insn_code icode,
- tree exp, rtx target)
+nds32_read_argument (tree exp, unsigned int index)
{
- /* Mapping:
- ops[0] <--> value0 <--> arg0 */
- struct expand_operand ops[1];
- tree arg0;
- rtx value0;
+ return expand_normal (CALL_EXPR_ARG (exp, index));
+}
- /* Grab the incoming arguments and extract its rtx. */
- arg0 = CALL_EXPR_ARG (exp, 0);
- value0 = expand_normal (arg0);
+/* Return a legitimate rtx for instruction ICODE's return value. Use TARGET
+ if it's not null, has the right mode, and satisfies operand 0's
+ predicate. */
+static rtx
+nds32_legitimize_target (enum insn_code icode, rtx target)
+{
+ enum machine_mode mode = insn_data[icode].operand[0].mode;
+
+ if (! target
+ || GET_MODE (target) != mode
+ || ! (*insn_data[icode].operand[0].predicate) (target, mode))
+ return gen_reg_rtx (mode);
+ else
+ return target;
+}
- /* Create operands. */
- create_input_operand (&ops[0], value0, TYPE_MODE (TREE_TYPE (arg0)));
+/* Given that ARG is being passed as operand OPNUM to instruction ICODE,
+ check whether ARG satisfies the operand's constraints. If it doesn't,
+ copy ARG to a temporary register and return that. Otherwise return ARG
+ itself. */
+static rtx
+nds32_legitimize_argument (enum insn_code icode, int opnum, rtx arg)
+{
+ enum machine_mode mode = insn_data[icode].operand[opnum].mode;
- /* Emit new instruction. */
- if (!maybe_expand_insn (icode, 1, ops))
- error ("invalid argument to built-in function");
+ if ((*insn_data[icode].operand[opnum].predicate) (arg, mode))
+ return arg;
+ else if (VECTOR_MODE_P (mode) && CONST_INT_P (arg))
+ {
+ /* Handle CONST_INT covert to CONST_VECTOR. */
+ int nunits = GET_MODE_NUNITS (mode);
+ int i, shift = 0;
+ rtvec v = rtvec_alloc (nunits);
+ int val = INTVAL (arg);
+ enum machine_mode val_mode = (mode == V4QImode) ? QImode : HImode;
+ int shift_acc = (val_mode == QImode) ? 8 : 16;
+ int mask = (val_mode == QImode) ? 0xff : 0xffff;
+ int tmp_val = val;
+
+ if (TARGET_BIG_ENDIAN)
+ for (i = 0; i < nunits; i++)
+ {
+ tmp_val = (val >> shift) & mask;
+ RTVEC_ELT (v, nunits - i - 1) = gen_int_mode (tmp_val, val_mode);
+ shift += shift_acc;
+ }
+ else
+ for (i = 0; i < nunits; i++)
+ {
+ tmp_val = (val >> shift) & mask;
+ RTVEC_ELT (v, i) = gen_int_mode (tmp_val, val_mode);
+ shift += shift_acc;
+ }
+
+ return copy_to_mode_reg (mode, gen_rtx_CONST_VECTOR (mode, v));
+ }
+ else
+ {
+ rtx tmp_rtx = gen_reg_rtx (mode);
+ convert_move (tmp_rtx, arg, false);
+ return tmp_rtx;
+ }
+}
- return target;
+/* Return true if OPVAL can be used for operand OPNUM of instruction ICODE.
+ The instruction should require a constant operand of some sort. The
+ function prints an error if OPVAL is not valid. */
+static int
+nds32_check_constant_argument (enum insn_code icode, int opnum, rtx opval,
+ const char *name)
+{
+ if (GET_CODE (opval) != CONST_INT)
+ {
+ error ("invalid argument to built-in function %s", name);
+ return false;
+ }
+ if (! (*insn_data[icode].operand[opnum].predicate) (opval, VOIDmode))
+ {
+ error ("constant argument out of range for %s", name);
+
+ return false;
+ }
+ return true;
}
-/* Function to expand builtin function for
- '[(set (reg) (unspec_volatile [(imm)]))]'. */
+/* Expand builtins that take one operand. */
static rtx
-nds32_expand_builtin_reg_ftype_imm (enum insn_code icode,
- tree exp, rtx target)
+nds32_expand_unop_builtin (enum insn_code icode, tree exp, rtx target,
+ bool return_p)
{
- /* Mapping:
- ops[0] <--> target <--> exp
- ops[1] <--> value0 <--> arg0 */
- struct expand_operand ops[2];
- tree arg0;
- rtx value0;
+ rtx pat;
+ rtx op0 = nds32_read_argument (exp, 0);
+ int op0_num = return_p ? 1 : 0;
+
+ if (return_p)
+ target = nds32_legitimize_target (icode, target);
- /* Grab the incoming arguments and extract its rtx. */
- arg0 = CALL_EXPR_ARG (exp, 0);
- value0 = expand_normal (arg0);
+ op0 = nds32_legitimize_argument (icode, op0_num, op0);
- /* Create operands. */
- create_output_operand (&ops[0], target, TYPE_MODE (TREE_TYPE (exp)));
- create_input_operand (&ops[1], value0, TYPE_MODE (TREE_TYPE (arg0)));
+ /* Emit and return the new instruction. */
+ if (return_p)
+ pat = GEN_FCN (icode) (target, op0);
+ else
+ pat = GEN_FCN (icode) (op0);
- /* Emit new instruction. */
- if (!maybe_expand_insn (icode, 2, ops))
- error ("invalid argument to built-in function");
+ if (! pat)
+ return NULL_RTX;
+ emit_insn (pat);
return target;
}
-/* Function to expand builtin function for
- '[(unspec_volatile [(reg) (imm)])]' pattern. */
+/* Expand builtins that take one operands and the first is immediate. */
static rtx
-nds32_expand_builtin_null_ftype_reg_imm (enum insn_code icode,
- tree exp, rtx target)
+nds32_expand_unopimm_builtin (enum insn_code icode, tree exp, rtx target,
+ bool return_p, const char *name)
{
- /* Mapping:
- ops[0] <--> value0 <--> arg0
- ops[1] <--> value1 <--> arg1 */
- struct expand_operand ops[2];
- tree arg0, arg1;
- rtx value0, value1;
-
- /* Grab the incoming arguments and extract its rtx. */
- arg0 = CALL_EXPR_ARG (exp, 0);
- arg1 = CALL_EXPR_ARG (exp, 1);
- value0 = expand_normal (arg0);
- value1 = expand_normal (arg1);
-
- /* Create operands. */
- create_input_operand (&ops[0], value0, TYPE_MODE (TREE_TYPE (arg0)));
- create_input_operand (&ops[1], value1, TYPE_MODE (TREE_TYPE (arg1)));
-
- /* Emit new instruction. */
- if (!maybe_expand_insn (icode, 2, ops))
- error ("invalid argument to built-in function");
+ rtx pat;
+ rtx op0 = nds32_read_argument (exp, 0);
+ int op0_num = return_p ? 1 : 0;
+
+ if (return_p)
+ target = nds32_legitimize_target (icode, target);
+
+ if (!nds32_check_constant_argument (icode, op0_num, op0, name))
+ return NULL_RTX;
+
+ op0 = nds32_legitimize_argument (icode, op0_num, op0);
+
+ /* Emit and return the new instruction. */
+ if (return_p)
+ pat = GEN_FCN (icode) (target, op0);
+ else
+ pat = GEN_FCN (icode) (op0);
+ if (! pat)
+ return NULL_RTX;
+
+ emit_insn (pat);
return target;
}
-/* ------------------------------------------------------------------------ */
-
-void
-nds32_init_builtins_impl (void)
+/* Expand builtins that take two operands. */
+static rtx
+nds32_expand_binop_builtin (enum insn_code icode, tree exp, rtx target,
+ bool return_p)
{
- tree pointer_type_node = build_pointer_type (integer_type_node);
+ rtx pat;
+ rtx op0 = nds32_read_argument (exp, 0);
+ rtx op1 = nds32_read_argument (exp, 1);
+ int op0_num = return_p ? 1 : 0;
+ int op1_num = return_p ? 2 : 1;
- tree void_ftype_void = build_function_type (void_type_node,
- void_list_node);
+ if (return_p)
+ target = nds32_legitimize_target (icode, target);
- tree void_ftype_pint = build_function_type_list (void_type_node,
- pointer_type_node,
- NULL_TREE);
+ op0 = nds32_legitimize_argument (icode, op0_num, op0);
+ op1 = nds32_legitimize_argument (icode, op1_num, op1);
- tree int_ftype_int = build_function_type_list (integer_type_node,
- integer_type_node,
- NULL_TREE);
+ /* Emit and return the new instruction. */
+ if (return_p)
+ pat = GEN_FCN (icode) (target, op0, op1);
+ else
+ pat = GEN_FCN (icode) (op0, op1);
- tree void_ftype_int_int = build_function_type_list (void_type_node,
- integer_type_node,
- integer_type_node,
- NULL_TREE);
+ if (! pat)
+ return NULL_RTX;
- /* Cache. */
- add_builtin_function ("__builtin_nds32_isync", void_ftype_pint,
- NDS32_BUILTIN_ISYNC,
- BUILT_IN_MD, NULL, NULL_TREE);
- add_builtin_function ("__builtin_nds32_isb", void_ftype_void,
- NDS32_BUILTIN_ISB,
- BUILT_IN_MD, NULL, NULL_TREE);
+ emit_insn (pat);
+ return target;
+}
- /* Register Transfer. */
- add_builtin_function ("__builtin_nds32_mfsr", int_ftype_int,
- NDS32_BUILTIN_MFSR,
- BUILT_IN_MD, NULL, NULL_TREE);
- add_builtin_function ("__builtin_nds32_mfusr", int_ftype_int,
- NDS32_BUILTIN_MFUSR,
- BUILT_IN_MD, NULL, NULL_TREE);
- add_builtin_function ("__builtin_nds32_mtsr", void_ftype_int_int,
- NDS32_BUILTIN_MTSR,
- BUILT_IN_MD, NULL, NULL_TREE);
- add_builtin_function ("__builtin_nds32_mtusr", void_ftype_int_int,
- NDS32_BUILTIN_MTUSR,
- BUILT_IN_MD, NULL, NULL_TREE);
+struct builtin_description
+{
+ const enum insn_code icode;
+ const char *name;
+ enum nds32_builtins code;
+ bool return_p;
+};
+
+#define NDS32_BUILTIN(code, string, builtin) \
+ { CODE_FOR_##code, "__nds32__" string, \
+ NDS32_BUILTIN_##builtin, true },
+
+#define NDS32_NO_TARGET_BUILTIN(code, string, builtin) \
+ { CODE_FOR_##code, "__nds32__" string, \
+ NDS32_BUILTIN_##builtin, false },
+
+/* Intrinsics that take just one argument. */
+static struct builtin_description bdesc_1arg[] =
+{
+ NDS32_NO_TARGET_BUILTIN(unspec_volatile_isync, "isync", ISYNC)
+};
- /* Interrupt. */
- add_builtin_function ("__builtin_nds32_setgie_en", void_ftype_void,
- NDS32_BUILTIN_SETGIE_EN,
- BUILT_IN_MD, NULL, NULL_TREE);
- add_builtin_function ("__builtin_nds32_setgie_dis", void_ftype_void,
- NDS32_BUILTIN_SETGIE_DIS,
- BUILT_IN_MD, NULL, NULL_TREE);
-}
+/* Intrinsics that take just one argument. and the argument is immediate. */
+static struct builtin_description bdesc_1argimm[] =
+{
+ NDS32_BUILTIN(unspec_volatile_mfsr, "mfsr", MFSR)
+ NDS32_BUILTIN(unspec_volatile_mfusr, "mfsr", MFUSR)
+};
+/* Intrinsics that take two arguments. */
+static struct builtin_description bdesc_2arg[] =
+{
+ NDS32_NO_TARGET_BUILTIN(unspec_volatile_mtsr, "mtsr", MTSR)
+ NDS32_NO_TARGET_BUILTIN(unspec_volatile_mtusr, "mtusr", MTUSR)
+};
rtx
nds32_expand_builtin_impl (tree exp,
rtx target,
rtx subtarget ATTRIBUTE_UNUSED,
- machine_mode mode ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
int ignore ATTRIBUTE_UNUSED)
{
tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+ unsigned i;
+ struct builtin_description *d;
- int fcode = DECL_FUNCTION_CODE (fndecl);
-
+ /* Since there are no result and operands, we can simply emit this rtx. */
switch (fcode)
{
- /* Cache. */
- case NDS32_BUILTIN_ISYNC:
- return nds32_expand_builtin_null_ftype_reg
- (CODE_FOR_unspec_volatile_isync, exp, target);
case NDS32_BUILTIN_ISB:
- /* Since there are no result and operands for isb instruciton,
- we can simply emit this rtx. */
emit_insn (gen_unspec_volatile_isb ());
return target;
-
- /* Register Transfer. */
- case NDS32_BUILTIN_MFSR:
- return nds32_expand_builtin_reg_ftype_imm
- (CODE_FOR_unspec_volatile_mfsr, exp, target);
- case NDS32_BUILTIN_MFUSR:
- return nds32_expand_builtin_reg_ftype_imm
- (CODE_FOR_unspec_volatile_mfusr, exp, target);
- case NDS32_BUILTIN_MTSR:
- return nds32_expand_builtin_null_ftype_reg_imm
- (CODE_FOR_unspec_volatile_mtsr, exp, target);
- case NDS32_BUILTIN_MTUSR:
- return nds32_expand_builtin_null_ftype_reg_imm
- (CODE_FOR_unspec_volatile_mtusr, exp, target);
-
- /* Interrupt. */
case NDS32_BUILTIN_SETGIE_EN:
- /* Since there are no result and operands for setgie.e instruciton,
- we can simply emit this rtx. */
emit_insn (gen_unspec_volatile_setgie_en ());
return target;
case NDS32_BUILTIN_SETGIE_DIS:
- /* Since there are no result and operands for setgie.d instruciton,
- we can simply emit this rtx. */
emit_insn (gen_unspec_volatile_setgie_dis ());
return target;
-
default:
- gcc_unreachable ();
+ break;
}
+ /* Expand groups of builtins. */
+ for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
+ if (d->code == fcode)
+ return nds32_expand_unop_builtin (d->icode, exp, target, d->return_p);
+
+ for (i = 0, d = bdesc_1argimm; i < ARRAY_SIZE (bdesc_1argimm); i++, d++)
+ if (d->code == fcode)
+ return nds32_expand_unopimm_builtin (d->icode, exp, target,
+ d->return_p, d->name);
+
+ for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
+ if (d->code == fcode)
+ return nds32_expand_binop_builtin (d->icode, exp, target, d->return_p);
+
+
return NULL_RTX;
}
-/* ------------------------------------------------------------------------ */
+static GTY(()) tree nds32_builtin_decls[NDS32_BUILTIN_COUNT];
+
+/* Return the NDS32 builtin for CODE. */
+tree
+nds32_builtin_decl_impl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+ if (code >= NDS32_BUILTIN_COUNT)
+ return error_mark_node;
+
+ return nds32_builtin_decls[code];
+}
+
+void
+nds32_init_builtins_impl (void)
+{
+#define ADD_NDS32_BUILTIN0(NAME, RET_TYPE, CODE) \
+ nds32_builtin_decls[NDS32_BUILTIN_ ## CODE] = \
+ add_builtin_function ("__builtin_nds32_" NAME, \
+ build_function_type_list (RET_TYPE##_type_node, \
+ NULL_TREE), \
+ NDS32_BUILTIN_ ## CODE, BUILT_IN_MD, NULL, NULL_TREE)
+
+#define ADD_NDS32_BUILTIN1(NAME, RET_TYPE, ARG_TYPE, CODE) \
+ nds32_builtin_decls[NDS32_BUILTIN_ ## CODE] = \
+ add_builtin_function ("__builtin_nds32_" NAME, \
+ build_function_type_list (RET_TYPE##_type_node, \
+ ARG_TYPE##_type_node, \
+ NULL_TREE), \
+ NDS32_BUILTIN_ ## CODE, BUILT_IN_MD, NULL, NULL_TREE)
+
+#define ADD_NDS32_BUILTIN2(NAME, RET_TYPE, ARG_TYPE1, ARG_TYPE2, CODE) \
+ nds32_builtin_decls[NDS32_BUILTIN_ ## CODE] = \
+ add_builtin_function ("__builtin_nds32_" NAME, \
+ build_function_type_list (RET_TYPE##_type_node, \
+ ARG_TYPE1##_type_node,\
+ ARG_TYPE2##_type_node,\
+ NULL_TREE), \
+ NDS32_BUILTIN_ ## CODE, BUILT_IN_MD, NULL, NULL_TREE)
+
+#define ADD_NDS32_BUILTIN3(NAME, RET_TYPE, \
+ ARG_TYPE1, ARG_TYPE2, ARG_TYPE3, CODE) \
+ nds32_builtin_decls[NDS32_BUILTIN_ ## CODE] = \
+ add_builtin_function ("__builtin_nds32_" NAME, \
+ build_function_type_list (RET_TYPE##_type_node, \
+ ARG_TYPE1##_type_node,\
+ ARG_TYPE2##_type_node,\
+ ARG_TYPE3##_type_node,\
+ NULL_TREE), \
+ NDS32_BUILTIN_ ## CODE, BUILT_IN_MD, NULL, NULL_TREE)
+
+ /* Looking for return type and argument can be found in tree.h file. */
+ tree ptr_uint_type_node = build_pointer_type (unsigned_type_node);
+
+ /* Cache. */
+ ADD_NDS32_BUILTIN1 ("isync", void, ptr_uint, ISYNC);
+ ADD_NDS32_BUILTIN0 ("isb", void, ISB);
+
+ /* Register Transfer. */
+ ADD_NDS32_BUILTIN1 ("mfsr", unsigned, integer, MFSR);
+ ADD_NDS32_BUILTIN1 ("mfusr", unsigned, integer, MFUSR);
+ ADD_NDS32_BUILTIN2 ("mtsr", void, unsigned, integer, MTSR);
+ ADD_NDS32_BUILTIN2 ("mtusr", void, unsigned, integer, MTUSR);
+
+ /* Interrupt. */
+ ADD_NDS32_BUILTIN0 ("setgie_en", void, SETGIE_EN);
+ ADD_NDS32_BUILTIN0 ("setgie_dis", void, SETGIE_DIS);
+}