#include "function.h"
#include "ggc.h"
#include "basic-block.h"
+#include "libfuncs.h"
#include "toplev.h"
#include "sched-int.h"
#include "timevar.h"
ATTRIBUTE_UNUSED;
static void ia64_soft_fp_init_libfuncs (void)
ATTRIBUTE_UNUSED;
+static bool ia64_vms_valid_pointer_mode (enum machine_mode mode)
+ ATTRIBUTE_UNUSED;
static tree ia64_handle_model_attribute (tree *, tree, tree, int, bool *);
static tree ia64_handle_version_id_attribute (tree *, tree, tree, int, bool *);
static const char *ia64_invalid_unary_op (int, const_tree);
static const char *ia64_invalid_binary_op (int, const_tree, const_tree);
static enum machine_mode ia64_c_mode_for_suffix (char);
+static enum machine_mode ia64_promote_function_mode (const_tree,
+ enum machine_mode,
+ int *,
+ const_tree,
+ int);
\f
/* Table of valid machine attributes. */
static const struct attribute_spec ia64_attribute_table[] =
#define TARGET_ASM_OUTPUT_DWARF_DTPREL ia64_output_dwarf_dtprel
#endif
-/* ??? ABI doesn't allow us to define this. */
-#if 0
#undef TARGET_PROMOTE_FUNCTION_MODE
-#define TARGET_PROMOTE_FUNCTION_MODE default_promote_function_mode_always_promote
-#endif
+#define TARGET_PROMOTE_FUNCTION_MODE ia64_promote_function_mode
/* ??? Investigate. */
#if 0
if (sibcall_p)
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), b0);
+
+ if (TARGET_ABI_OPEN_VMS)
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
+ gen_rtx_REG (DImode, GR_REG (25)));
}
static void
void
ia64_initialize_trampoline (rtx addr, rtx fnaddr, rtx static_chain)
{
- rtx addr_reg, eight = GEN_INT (8);
+ rtx addr_reg, tramp, eight = GEN_INT (8);
/* The Intel assembler requires that the global __ia64_trampoline symbol
be declared explicitly */
/* The first two words are the fake descriptor:
__ia64_trampoline, ADDR+16. */
- emit_move_insn (gen_rtx_MEM (Pmode, addr_reg),
- gen_rtx_SYMBOL_REF (Pmode, "__ia64_trampoline"));
+ tramp = gen_rtx_SYMBOL_REF (Pmode, "__ia64_trampoline");
+ if (TARGET_ABI_OPEN_VMS)
+ {
+ /* HP decided to break the ELF ABI on VMS (to deal with an ambiguity
+ in the Macro-32 compiler) and changed the semantics of the LTOFF22
+ relocation against function symbols to make it identical to the
+ LTOFF_FPTR22 relocation. Emit the latter directly to stay within
+ strict ELF and dereference to get the bare code address. */
+ rtx reg = gen_reg_rtx (Pmode);
+ SYMBOL_REF_FLAGS (tramp) |= SYMBOL_FLAG_FUNCTION;
+ emit_move_insn (reg, tramp);
+ emit_move_insn (reg, gen_rtx_MEM (Pmode, reg));
+ tramp = reg;
+ }
+ emit_move_insn (gen_rtx_MEM (Pmode, addr_reg), tramp);
emit_insn (gen_adddi3 (addr_reg, addr_reg, eight));
emit_move_insn (gen_rtx_MEM (Pmode, addr_reg),
static int
ia64_function_arg_offset (CUMULATIVE_ARGS *cum, tree type, int words)
{
- if ((cum->words & 1) == 0)
+ /* No registers are skipped on VMS. */
+ if (TARGET_ABI_OPEN_VMS || (cum->words & 1) == 0)
return 0;
if (type
int offset = ia64_function_arg_offset (cum, type, words);
enum machine_mode hfa_mode = VOIDmode;
+ /* For OPEN VMS, emit the instruction setting up the argument register here,
+ when we know this will be together with the other arguments setup related
+ insns. This is not the conceptually best place to do this, but this is
+ the easiest as we have convenient access to cumulative args info. */
+
+ if (TARGET_ABI_OPEN_VMS && mode == VOIDmode && type == void_type_node
+ && named == 1)
+ {
+ unsigned HOST_WIDE_INT regval = cum->words;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ regval |= ((int) cum->atypes[i]) << (i * 3 + 8);
+
+ emit_move_insn (gen_rtx_REG (DImode, GR_REG (25)),
+ GEN_INT (regval));
+ }
+
/* If all argument slots are used, then it must go on the stack. */
if (cum->words + offset >= MAX_ARGUMENT_SLOTS)
return 0;
}
return gen_rtx_PARALLEL (mode, gen_rtvec_v (i, loc));
}
+
+ /* On OpenVMS variable argument is either in Rn or Fn. */
+ else if (TARGET_ABI_OPEN_VMS && named == 0)
+ {
+ if (FLOAT_MODE_P (mode))
+ return gen_rtx_REG (mode, FR_ARG_FIRST + cum->words);
+ else
+ return gen_rtx_REG (mode, basereg + cum->words);
+ }
/* Integral and aggregates go in general registers. If we have run out of
FR registers, then FP values must also go in general registers. This can
return (MAX_ARGUMENT_SLOTS - cum->words - offset) * UNITS_PER_WORD;
}
+/* Return ivms_arg_type based on machine_mode. */
+
+static enum ivms_arg_type
+ia64_arg_type (enum machine_mode mode)
+{
+ switch (mode)
+ {
+ case SFmode:
+ return FS;
+ case DFmode:
+ return FT;
+ default:
+ return I64;
+ }
+}
+
/* Update CUM to point after this argument. This is patterned after
ia64_function_arg. */
/* If all arg slots are already full, then there is nothing to do. */
if (cum->words >= MAX_ARGUMENT_SLOTS)
- return;
+ {
+ cum->words += words + offset;
+ return;
+ }
+ cum->atypes[cum->words] = ia64_arg_type (mode);
cum->words += words + offset;
/* Check for and handle homogeneous FP aggregates. */
cum->fp_regs = fp_regs;
}
+ /* On OpenVMS variable argument is either in Rn or Fn. */
+ else if (TARGET_ABI_OPEN_VMS && named == 0)
+ {
+ cum->int_regs = cum->words;
+ cum->fp_regs = cum->words;
+ }
+
/* Integral and aggregates go in general registers. So do TFmode FP values.
If we have run out of FR registers, then other FP values must also go in
general registers. This can happen when we have a SFmode HFA. */
/* Return rtx for register that holds the function return value. */
rtx
-ia64_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED)
+ia64_function_value (const_tree valtype, const_tree func)
{
enum machine_mode mode;
enum machine_mode hfa_mode;
+ int unsignedp;
mode = TYPE_MODE (valtype);
hfa_mode = hfa_element_mode (valtype, 0);
return gen_rtx_PARALLEL (mode, gen_rtvec_v (i, loc));
}
+ mode = ia64_promote_function_mode (valtype, mode, &unsignedp,
+ func ? TREE_TYPE (func) : NULL_TREE,
+ true);
+
return gen_rtx_REG (mode, GR_RET_FIRST);
}
}
align_functions = 64;
if (align_loops <= 0)
align_loops = 32;
+
+ if (TARGET_ABI_OPEN_VMS)
+ flag_no_common = 1;
}
/* Initialize the record of emitted frame related registers. */
(*lang_hooks.types.register_builtin_type) (long_double_type_node,
"__float128");
+ /* Fwrite on VMS is non-standard. */
+ if (TARGET_ABI_OPEN_VMS)
+ {
+ implicit_built_in_decls[(int) BUILT_IN_FWRITE] = NULL_TREE;
+ implicit_built_in_decls[(int) BUILT_IN_FWRITE_UNLOCKED] = NULL_TREE;
+ }
+
#define def_builtin(name, type, code) \
add_builtin_function ((name), (type), (code), BUILT_IN_MD, \
NULL, NULL_TREE)
int need_visibility = ((*targetm.binds_local_p) (decl)
&& maybe_assemble_visibility (decl));
+#ifdef DO_CRTL_NAMES
+ DO_CRTL_NAMES;
+#endif
+
/* GNU as does not need anything here, but the HP linker does
need something for external functions. */
if ((TARGET_HPUX_LD || !TARGET_GNU_AS)
set_optab_libfunc (smod_optab, DImode, "OTS$REM_L");
set_optab_libfunc (umod_optab, SImode, "OTS$REM_UI");
set_optab_libfunc (umod_optab, DImode, "OTS$REM_UL");
+ abort_libfunc = init_one_libfunc ("decc$abort");
+ memcmp_libfunc = init_one_libfunc ("decc$memcmp");
+#ifdef MEM_LIBFUNCS_INIT
+ MEM_LIBFUNCS_INIT;
+#endif
}
/* Rename the TFmode libfuncs available from soft-fp in glibc using
ia64_soft_fp_init_libfuncs (void)
{
}
+
+static bool
+ia64_vms_valid_pointer_mode (enum machine_mode mode)
+{
+ return (mode == SImode || mode == DImode);
+}
\f
/* For HPUX, it is illegal to have relocations in shared segments. */
ia64_struct_value_rtx (tree fntype,
int incoming ATTRIBUTE_UNUSED)
{
- if (fntype && ia64_struct_retval_addr_is_first_parm_p (fntype))
+ if (TARGET_ABI_OPEN_VMS ||
+ (fntype && ia64_struct_retval_addr_is_first_parm_p (fntype)))
return NULL_RTX;
return gen_rtx_REG (Pmode, GR_REG (8));
}
return VOIDmode;
}
+static enum machine_mode
+ia64_promote_function_mode (const_tree type,
+ enum machine_mode mode,
+ int *punsignedp,
+ const_tree funtype ATTRIBUTE_UNUSED,
+ int for_return ATTRIBUTE_UNUSED)
+{
+ /* Special processing required for OpenVMS ... */
+
+ if (!TARGET_ABI_OPEN_VMS)
+ return mode;
+
+ /* HP OpenVMS Calling Standard dated June, 2004, that describes
+ HP OpenVMS I64 Version 8.2EFT,
+ chapter 4 "OpenVMS I64 Conventions"
+ section 4.7 "Procedure Linkage"
+ subsection 4.7.5.2, "Normal Register Parameters"
+
+ "Unsigned integral (except unsigned 32-bit), set, and VAX floating-point
+ values passed in registers are zero-filled; signed integral values as
+ well as unsigned 32-bit integral values are sign-extended to 64 bits.
+ For all other types passed in the general registers, unused bits are
+ undefined." */
+
+ if (!AGGREGATE_TYPE_P (type)
+ && GET_MODE_CLASS (mode) == MODE_INT
+ && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
+ {
+ if (mode == SImode)
+ *punsignedp = 0;
+ return DImode;
+ }
+ else
+ return promote_mode (type, mode, punsignedp);
+}
+
static GTY(()) rtx ia64_dconst_0_5_rtx;
rtx
#define TARGET_HPUX 0
#define TARGET_HPUX_LD 0
+#define TARGET_ABI_OPEN_VMS 0
+
#ifndef TARGET_ILP32
#define TARGET_ILP32 0
#endif
#define DOUBLE_TYPE_SIZE 64
-/* long double is XFmode normally, TFmode for HPUX. */
-#define LONG_DOUBLE_TYPE_SIZE (TARGET_HPUX ? 128 : 80)
+/* long double is XFmode normally, and TFmode for HPUX. It should be
+ TFmode for VMS as well but we only support up to DFmode now. */
+#define LONG_DOUBLE_TYPE_SIZE \
+ (TARGET_HPUX ? 128 \
+ : TARGET_ABI_OPEN_VMS ? 64 \
+ : 80)
+
+/* We always want the XFmode operations from libgcc2.c, except on VMS
+ where this yields references to unimplemented "insns". */
+#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE (TARGET_ABI_OPEN_VMS ? 64 : 80)
-/* We always want the XFmode operations from libgcc2.c. */
-#define LIBGCC2_LONG_DOUBLE_TYPE_SIZE 80
/* On HP-UX, we use the l suffix for TFmode in libgcc2.c. */
#define LIBGCC2_TF_CEXT l
`FUNCTION_ARG' and other related values. For some target machines, the type
`int' suffices and can hold the number of bytes of argument so far. */
+enum ivms_arg_type {I64, FF, FD, FG, FS, FT};
+/* VMS floating point formats VAX F, VAX D, VAX G, IEEE S, IEEE T. */
+
typedef struct ia64_args
{
int words; /* # words of arguments so far */
int int_regs; /* # GR registers used so far */
int fp_regs; /* # FR registers used so far */
int prototype; /* whether function prototyped */
+ enum ivms_arg_type atypes[8]; /* which VMS float type or if not float */
} CUMULATIVE_ARGS;
/* A C statement (sans semicolon) for initializing the variable CUM for the
(CUM).int_regs = 0; \
(CUM).fp_regs = 0; \
(CUM).prototype = ((FNTYPE) && TYPE_ARG_TYPES (FNTYPE)) || (LIBNAME); \
+ (CUM).atypes[0] = (CUM).atypes[1] = (CUM).atypes[2] = I64; \
+ (CUM).atypes[3] = (CUM).atypes[4] = (CUM).atypes[5] = I64; \
+ (CUM).atypes[6] = (CUM).atypes[7] = I64; \
} while (0)
/* Like `INIT_CUMULATIVE_ARGS' but overrides it for the purposes of finding the
(CUM).int_regs = 0; \
(CUM).fp_regs = 0; \
(CUM).prototype = 1; \
+ (CUM).atypes[0] = (CUM).atypes[1] = (CUM).atypes[2] = I64; \
+ (CUM).atypes[3] = (CUM).atypes[4] = (CUM).atypes[5] = I64; \
+ (CUM).atypes[6] = (CUM).atypes[7] = I64; \
} while (0)
/* A C statement (sans semicolon) to update the summarizer variable CUM to
do { \
assemble_name (STREAM, NAME); \
fputs (" = ", STREAM); \
+ if (ISDIGIT (*VALUE)) \
+ ia64_asm_output_label = 1; \
assemble_name (STREAM, VALUE); \
fputc ('\n', STREAM); \
+ ia64_asm_output_label = 0; \
} while (0)
\f