@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see
#include "target.h"
#include "rtl.h"
#include "tree.h"
+#include "gimple.h"
#include "df.h"
#include "tm_p.h"
#include "stringpool.h"
@@ -142,6 +143,7 @@ static void pa_asm_out_destructor (rtx, int);
#endif
static void pa_init_builtins (void);
static rtx pa_expand_builtin (tree, rtx, rtx, machine_mode mode, int);
+static tree pa_builtin_decl (unsigned, bool);
static rtx hppa_builtin_saveregs (void);
static void hppa_va_start (tree, rtx);
static tree hppa_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
@@ -205,6 +207,7 @@ static bool pa_modes_tieable_p (machine_mode, machine_mode);
static bool pa_can_change_mode_class (machine_mode, machine_mode, reg_class_t);
static HOST_WIDE_INT pa_starting_frame_offset (void);
static section* pa_elf_select_rtx_section(machine_mode, rtx, unsigned HOST_WIDE_INT) ATTRIBUTE_UNUSED;
+static void pa_atomic_assign_expand_fenv (tree *, tree *, tree *);
/* The following extra sections are only used for SOM. */
static GTY(()) section *som_readonly_data_section;
@@ -314,9 +317,10 @@ static size_t n_deferred_plabels = 0;
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS pa_init_builtins
-
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN pa_expand_builtin
+#undef TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL pa_builtin_decl
#undef TARGET_REGISTER_MOVE_COST
#define TARGET_REGISTER_MOVE_COST hppa_register_move_cost
@@ -426,6 +430,9 @@ static size_t n_deferred_plabels = 0;
#undef TARGET_HAVE_SPECULATION_SAFE_VALUE
#define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
+#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV
+#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV pa_atomic_assign_expand_fenv
+
struct gcc_target targetm = TARGET_INITIALIZER;
/* Parse the -mfixed-range= option string. */
@@ -592,6 +599,10 @@ pa_option_override (void)
enum pa_builtins
{
+ /* FPU builtins. */
+ PA_BUILTIN_GET_FPSR,
+ PA_BUILTIN_SET_FPSR,
+
PA_BUILTIN_COPYSIGNQ,
PA_BUILTIN_FABSQ,
PA_BUILTIN_INFQ,
@@ -600,10 +611,48 @@ enum pa_builtins
};
static GTY(()) tree pa_builtins[(int) PA_BUILTIN_max];
+static GTY(()) enum insn_code pa_builtins_icode[(int) PA_BUILTIN_max];
+
+/* Add a PA builtin function with NAME, ICODE, CODE and TYPE. Return the
+ function decl or NULL_TREE if the builtin was not added. */
+
+static tree
+def_builtin (const char *name, enum insn_code icode, enum pa_builtins code,
+ tree type)
+{
+ tree t
+ = add_builtin_function (name, type, code, BUILT_IN_MD, NULL, NULL_TREE);
+
+ if (t)
+ {
+ pa_builtins[code] = t;
+ pa_builtins_icode[code] = icode;
+ }
+
+ return t;
+}
+
+/* Create builtin functions for FPU instructions. */
+
+static void
+pa_fpu_init_builtins (void)
+{
+ tree ftype;
+
+ ftype = build_function_type_list (unsigned_type_node, 0);
+ def_builtin ("__builtin_get_fpsr", CODE_FOR_get_fpsr,
+ PA_BUILTIN_GET_FPSR, ftype);
+ ftype = build_function_type_list (void_type_node, unsigned_type_node, 0);
+ def_builtin ("__builtin_set_fpsr", CODE_FOR_set_fpsr,
+ PA_BUILTIN_SET_FPSR, ftype);
+}
static void
pa_init_builtins (void)
{
+ if (!TARGET_SOFT_FLOAT)
+ pa_fpu_init_builtins ();
+
#ifdef DONT_HAVE_FPUTC_UNLOCKED
{
tree decl = builtin_decl_explicit (BUILT_IN_PUTC_UNLOCKED);
@@ -663,6 +712,92 @@ pa_init_builtins (void)
}
}
+/* Implement TARGET_BUILTIN_DECL. */
+
+static tree
+pa_builtin_decl (unsigned int code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+ if (code >= PA_BUILTIN_max)
+ return error_mark_node;
+ return pa_builtins[code];
+}
+
+static rtx
+pa_expand_builtin_1 (tree exp, rtx target,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ machine_mode tmode ATTRIBUTE_UNUSED,
+ int ignore ATTRIBUTE_UNUSED)
+{
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ enum pa_builtins code
+ = (enum pa_builtins) DECL_MD_FUNCTION_CODE (fndecl);
+ enum insn_code icode = pa_builtins_icode[code];
+ bool nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
+ call_expr_arg_iterator iter;
+ int arg_count = 0;
+ rtx pat, op[4];
+ tree arg;
+
+ if (nonvoid)
+ {
+ machine_mode tmode = insn_data[icode].operand[0].mode;
+ if (!target
+ || GET_MODE (target) != tmode
+ || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+ op[0] = gen_reg_rtx (tmode);
+ else
+ op[0] = target;
+ }
+ else
+ op[0] = NULL_RTX;
+
+ FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
+ {
+ const struct insn_operand_data *insn_op;
+ int idx;
+
+ if (arg == error_mark_node)
+ return NULL_RTX;
+
+ arg_count++;
+ idx = arg_count - !nonvoid;
+ insn_op = &insn_data[icode].operand[idx];
+ op[arg_count] = expand_normal (arg);
+
+ if (! (*insn_data[icode].operand[idx].predicate) (op[arg_count],
+ insn_op->mode))
+ op[arg_count] = copy_to_mode_reg (insn_op->mode, op[arg_count]);
+ }
+
+ switch (arg_count)
+ {
+ case 0:
+ pat = GEN_FCN (icode) (op[0]);
+ break;
+ case 1:
+ if (nonvoid)
+ pat = GEN_FCN (icode) (op[0], op[1]);
+ else
+ pat = GEN_FCN (icode) (op[1]);
+ break;
+ case 2:
+ pat = GEN_FCN (icode) (op[0], op[1], op[2]);
+ break;
+ case 3:
+ pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (!pat)
+ return NULL_RTX;
+
+ emit_insn (pat);
+
+ return (nonvoid ? op[0] : const0_rtx);
+}
+
static rtx
pa_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
machine_mode mode ATTRIBUTE_UNUSED,
@@ -673,6 +808,10 @@ pa_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
switch (fcode)
{
+ case PA_BUILTIN_GET_FPSR:
+ case PA_BUILTIN_SET_FPSR:
+ return pa_expand_builtin_1 (exp, target, subtarget, mode, ignore);
+
case PA_BUILTIN_FABSQ:
case PA_BUILTIN_COPYSIGNQ:
return expand_call (exp, target, ignore);
@@ -11099,4 +11238,78 @@ pa_function_arg_size (machine_mode mode, const_tree type)
return (int) CEIL (size, UNITS_PER_WORD);
}
+/* Implement TARGET_ATOMIC_ASSIGN_EXPAND_FENV. */
+
+static void
+pa_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
+{
+ const unsigned PA_FE_INEXACT = 1;
+ const unsigned PA_FE_UNDERFLOW = 2;
+ const unsigned PA_FE_OVERFLOW = 4;
+ const unsigned PA_FE_DIVBYZERO = 8;
+ const unsigned PA_FE_INVALID = 16;
+ const unsigned HOST_WIDE_INT PA_FE_ALL_EXCEPT = (PA_FE_INVALID
+ | PA_FE_DIVBYZERO
+ | PA_FE_OVERFLOW
+ | PA_FE_UNDERFLOW
+ | PA_FE_INEXACT);
+ const unsigned HOST_WIDE_INT PA_FE_EXCEPT_SHIFT = 27;
+ tree fenv_var, get_fpsr, set_fpsr, mask, ld_fenv, masked_fenv;
+ tree hold_all, new_fenv_var, reload_fenv, restore_fnenv;
+ tree get_fpsr_call, set_fpsr_call, update_call, atomic_feraiseexcept;
+
+ if (TARGET_SOFT_FLOAT)
+ return;
+
+ /* Generate the equivalent of :
+ unsigned int fenv_var;
+ fenv_var = __builtin_get_fpsr ();
+
+ unsigned int masked_fenv;
+ masked_fenv = fenv_var & mask;
+
+ __builtin_set_fpsr (masked_fenv); */
+
+ fenv_var = create_tmp_var_raw (unsigned_type_node);
+ get_fpsr = pa_builtins[PA_BUILTIN_GET_FPSR];
+ set_fpsr = pa_builtins[PA_BUILTIN_SET_FPSR];
+ mask = build_int_cst (unsigned_type_node,
+ ~((PA_FE_ALL_EXCEPT << PA_FE_EXCEPT_SHIFT)
+ | PA_FE_ALL_EXCEPT));
+
+ get_fpsr_call = build_call_expr (get_fpsr, 0);
+ ld_fenv = build4 (TARGET_EXPR, unsigned_type_node,
+ fenv_var, get_fpsr_call,
+ NULL_TREE, NULL_TREE);
+ masked_fenv = build2 (BIT_AND_EXPR, unsigned_type_node, fenv_var, mask);
+ hold_all = build2 (COMPOUND_EXPR, void_type_node, masked_fenv, ld_fenv);
+ set_fpsr_call = build_call_expr (set_fpsr, 1, masked_fenv);
+ *hold = build2 (COMPOUND_EXPR, void_type_node, hold_all, set_fpsr_call);
+
+ /* Store the value of masked_fenv to clear the exceptions:
+ __builtin_set_fpsr (masked_fenv); */
+
+ *clear = set_fpsr_call;
+
+ /* Generate the equivalent of :
+ unsigned int new_fenv_var;
+ new_fenv_var = __builtin_get_fpsr ();
+
+ __builtin_set_fpsr (fenv_var);
+
+ __atomic_feraiseexcept (new_fenv_var); */
+
+ new_fenv_var = create_tmp_var_raw (unsigned_type_node);
+ reload_fenv = build4 (TARGET_EXPR, unsigned_type_node, new_fenv_var,
+ get_fpsr_call, NULL_TREE, NULL_TREE);
+ restore_fnenv = build_call_expr (set_fpsr, 1, fenv_var);
+ atomic_feraiseexcept = builtin_decl_implicit (BUILT_IN_ATOMIC_FERAISEEXCEPT);
+ update_call = build_call_expr (atomic_feraiseexcept, 1,
+ fold_convert (integer_type_node,
+ new_fenv_var));
+ *update = build2 (COMPOUND_EXPR, void_type_node,
+ build2 (COMPOUND_EXPR, void_type_node,
+ reload_fenv, restore_fnenv), update_call);
+}
+
#include "gt-pa.h"
@@ -96,6 +96,8 @@
UNSPECV_OPC ; outline_prologue_call
UNSPECV_OEC ; outline_epilogue_call
UNSPECV_LONGJMP ; builtin_longjmp
+ UNSPECV_GET_FPSR ; get floating-point status register
+ UNSPECV_SET_FPSR ; set floating-point status register
])
;; Maximum pc-relative branch offsets.
@@ -10784,3 +10786,85 @@ add,l %2,%3,%3\;bv,n %%r0(%3)"
"ldo 15(%%sp),%1\n\t{dep|depw} %%r0,31,3,%1\n\t{ldcw|ldcw,co} 0(%1),%1"
[(set_attr "type" "binary")
(set_attr "length" "12")])
+
+;; Get floating-point status register.
+
+(define_expand "get_fpsr"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec_volatile:SI [(const_int 0)] UNSPECV_GET_FPSR))]
+ ""
+{
+ if (TARGET_SOFT_FLOAT)
+ FAIL;
+
+ if (TARGET_64BIT)
+ emit_insn (gen_get_fpsr_64 (operands[0]));
+ else
+ emit_insn (gen_get_fpsr_32 (operands[0]));
+ DONE;
+})
+
+;; The floating-point status register is stored to an unused slot in
+;; the frame marker and then loaded to register operand 0. The final
+;; floating-point load restores the T bit in the status register.
+
+;; The final load might be avoided if a word mode store was used to
+;; store the status register. It is unclear why we need a double-word
+;; store. I suspect PA 1.0 didn't support single-word stores of the
+;; status register.
+
+(define_insn "get_fpsr_32"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec_volatile:SI [(const_int 0)] UNSPECV_GET_FPSR))]
+ "!TARGET_SOFT_FLOAT && !TARGET_64BIT"
+ "{fstds|fstd} %%fr0,-16(%%sp)\n\tldw -16(%%sp),%0\n\t{fldds|fldd} -16(%%sp),%%fr0"
+ [(set_attr "type" "fpstore_load")
+ (set_attr "length" "12")])
+
+;; The 64-bit pattern is similar to the 32-bit pattern except we need
+;; compute the address of the frame location as long displacements aren't
+;; supported on Linux targets.
+
+(define_insn "get_fpsr_64"
+ [(set (match_operand:SI 0 "register_operand" "=r")
+ (unspec_volatile:SI [(const_int 0)] UNSPECV_GET_FPSR))
+ (clobber (match_scratch:DI 1 "=&r"))]
+ "!TARGET_SOFT_FLOAT && TARGET_64BIT"
+ "ldo -40(%%sp),%1\n\tfstd %%fr0,0(%1)\n\tldw 0(%1),%0\n\tfldd 0(%1),%%fr0"
+ [(set_attr "type" "fpstore_load")
+ (set_attr "length" "16")])
+
+;; Set floating-point status register.
+
+(define_expand "set_fpsr"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")] UNSPECV_SET_FPSR)]
+ ""
+{
+ if (TARGET_SOFT_FLOAT)
+ FAIL;
+
+ if (TARGET_64BIT)
+ emit_insn (gen_set_fpsr_64 (operands[0]));
+ else
+ emit_insn (gen_set_fpsr_32 (operands[0]));
+ DONE;
+})
+
+;; The old T bit is extracted and stored in the new status register.
+
+(define_insn "set_fpsr_32"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")] UNSPECV_SET_FPSR)
+ (clobber (match_scratch:SI 1 "=&r"))]
+ "!TARGET_SOFT_FLOAT && !TARGET_64BIT"
+ "{fstds|fstd} %%fr0,-16(%%sp)\n\tldw -16(%%sp),%1\n\t{extru|extrw,u} %1,25,1,%1\n\t{dep|depw} %1,25,1,%0\n\tstw %0,-16(%%sp)\n\t{fldds|fldd} -16(%%sp),%%fr0"
+ [(set_attr "type" "store_fpload")
+ (set_attr "length" "24")])
+
+(define_insn "set_fpsr_64"
+ [(unspec_volatile [(match_operand:SI 0 "register_operand" "r")] UNSPECV_SET_FPSR)
+ (clobber (match_scratch:DI 1 "=&r"))
+ (clobber (match_scratch:SI 2 "=&r"))]
+ "!TARGET_SOFT_FLOAT && TARGET_64BIT"
+ "ldo -40(%%sp),%1\n\tfstd %%fr0,0(%1)\n\tldw 0(%1),%2\n\textrw,u %2,25,1,%2\n\tdepw %2,25,1,%0\n\tstw %0,0(%1)\n\tfldd 0(%1),%%fr0"
+ [(set_attr "type" "store_fpload")
+ (set_attr "length" "28")])