This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
gcc S/390 backend 1
- To: gcc-patches at gcc dot gnu dot org
- Subject: gcc S/390 backend 1
- From: Hartmut Penner <hp at zuck175192 dot boeblingen dot de dot ibm dot com>
- Date: Mon, 9 Oct 2000 17:58:27 +0200
- Cc: hpenner at de dot ibm dot com
- Organization: IBM
- Reply-To: hpenner at de dot ibm dot com
Hi,
Part 1 of changed S/390 backend
2000-10-09 Hartmut Penner <hpenner@de.ibm.com>
* configure.in: Add new target s390-ibm-linux-gnu.
* config/mt-s390pic: New file.
* config/mh-s390pic: New file.
* gcc/config/s390/s390.md: New file, S/390 machine description.
* gcc/config/s390/s390.h: New file, S/390 basic target macros.
* gcc/config/s390/linux.h: New file, S/390 linux target macros.
* gcc/config/s390/s390.c: New file, S/390 basic target functions.
* gcc/config/s390/linux.c: New file, S/390 linux target functions.
* gcc/config/s390/t-linux: New file.
* gcc/config/s390/xm-s390: New file.
diff -r -u --new-file egcs-20001002/config/mh-s390pic egcs-20001002-s390/config/mh-s390pic
--- egcs-20001002/config/mh-s390pic Thu Jan 1 01:00:00 1970
+++ egcs-20001002-s390/config/mh-s390pic Fri Oct 6 12:53:33 2000
@@ -0,0 +1 @@
+PICFLAG=-fpic
diff -r -u --new-file egcs-20001002/config/mt-s390pic egcs-20001002-s390/config/mt-s390pic
--- egcs-20001002/config/mt-s390pic Thu Jan 1 01:00:00 1970
+++ egcs-20001002-s390/config/mt-s390pic Fri Oct 6 12:53:33 2000
@@ -0,0 +1 @@
+PICFLAG_FOR_TARGET=-fpic
diff -r -u --new-file egcs-20001002/gcc/config/s390/linux.c egcs-20001002-s390/gcc/config/s390/linux.c
--- egcs-20001002/gcc/config/s390/linux.c Thu Jan 1 01:00:00 1970
+++ egcs-20001002-s390/gcc/config/s390/linux.c Mon Oct 9 17:20:22 2000
@@ -0,0 +1,1056 @@
+/* Subroutines used for code generation on linux on S/390
+ Copyright (C) 1999, 2000 Free Software Foundation, Inc.
+ Contributed by Hartmut Penner (hpenner@de.ibm.com).
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
+
+
+#include "config.h"
+#include "system.h"
+#include "rtl.h"
+#include "tree.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "except.h"
+#include "function.h"
+#include "recog.h"
+#include "expr.h"
+#include "math.h"
+
+#include "tm.h"
+#include "tm_p.h"
+
+
+#ifdef DWARF2_DEBUGGING_INFO
+extern char *dwarf2out_cfi_label PARAMS ((void));
+#endif
+
+/* Flag set in prologue, used in epilog to know
+ if stack is allocated or not. */
+
+static int leaf_function_flag;
+rtx s390_got_label;
+rtx s390_profile[10];
+
+/* Current function is a leaf function, without automatics,
+ alloca or vararg stuff. */
+
+static int
+cur_is_leaf_function ()
+{
+ int lsize = get_frame_size () + current_function_outgoing_args_size;
+ if (leaf_function_p () && ((lsize) == 0) &&
+ ! (current_function_calls_alloca) &&
+ ! (current_function_stdarg) && ! (current_function_varargs))
+ return 1;
+ return 0;
+}
+
+/* Calculate offset between argument pointer and frame pointer
+ initialy after prologue. */
+
+int s390_arg_frame_offset ()
+{
+ int lsize = get_frame_size () + current_function_outgoing_args_size;
+
+ if (cur_is_leaf_function ())
+ return STACK_POINTER_OFFSET;
+ else
+ return 2*STACK_POINTER_OFFSET + lsize;
+}
+
+
+/* This function generates the assembly code for function entry. */
+
+int
+s390_function_prologue (FILE * file, int lsize)
+{
+ extern int profile_label_no;
+ int i, j, so;
+ rtx stack_label = 0, got_label = 0, tmp;
+ char *l;
+
+ /* Profile code (-p, -a, -ax needs some literals). */
+
+ if (profile_block_flag)
+ {
+ tmp = gen_rtx (SYMBOL_REF, Pmode, &"F@__bb_init_func"[1]);
+ SYMBOL_REF_FLAG (tmp) = 1;
+ s390_profile[0] = force_const_mem (Pmode, tmp);
+ emit_insn_before (gen_rtx (USE, Pmode, s390_profile[0]), get_insns ());
+
+ tmp = gen_rtx (SYMBOL_REF, Pmode, &"F@__bb_init_trace_func"[1]);
+ SYMBOL_REF_FLAG (tmp) = 1;
+ s390_profile[1] = force_const_mem (Pmode, tmp);
+ emit_insn_before (gen_rtx (USE, Pmode, s390_profile[1]), get_insns ());
+
+ tmp = gen_rtx (SYMBOL_REF, Pmode, &"F@__bb_trace_func"[1]);
+ SYMBOL_REF_FLAG (tmp) = 1;
+ s390_profile[2] = force_const_mem (Pmode, tmp);
+ emit_insn_before (gen_rtx (USE, Pmode, s390_profile[2]), get_insns ());
+
+ tmp = gen_rtx (SYMBOL_REF, Pmode, &"F@__bb_trace_ret"[1]);
+ SYMBOL_REF_FLAG (tmp) = 1;
+ s390_profile[3] = force_const_mem (Pmode, tmp);
+ emit_insn_before (gen_rtx (USE, Pmode, s390_profile[3]), get_insns ());
+
+ tmp = gen_rtx (SYMBOL_REF, Pmode, "__bb");
+ SYMBOL_REF_FLAG (tmp) = 1;
+ s390_profile[5] = force_const_mem (Pmode, tmp);
+ emit_insn_before (gen_rtx (USE, Pmode, s390_profile[5]), get_insns ());
+
+ tmp = gen_rtx (SYMBOL_REF, Pmode, ".LPBX0");
+ s390_profile[6] = force_const_mem (Pmode, tmp);
+ emit_insn_before (gen_rtx (USE, Pmode, s390_profile[6]), get_insns ());
+
+ tmp = gen_rtx (SYMBOL_REF, Pmode, ".LPBX2");
+ s390_profile[7] = force_const_mem (Pmode, tmp);
+ emit_insn_before (gen_rtx (USE, Pmode, s390_profile[7]), get_insns ());
+ }
+
+ if (profile_flag)
+ {
+ static char label[128];
+ tmp = gen_rtx (SYMBOL_REF, Pmode, &"F@_mcount"[1]);
+ SYMBOL_REF_FLAG (tmp) = 1;
+ s390_profile[4] = force_const_mem (Pmode, tmp);
+ emit_insn_before (gen_rtx (USE, Pmode, s390_profile[4]), get_insns ());
+ sprintf (label, "%sP%d", LPREFIX, profile_label_no);
+ tmp = gen_rtx (SYMBOL_REF, Pmode, label);
+ s390_profile[9] = force_const_mem (Pmode, tmp);
+ emit_insn_before (gen_rtx (USE, Pmode, s390_profile[9]), get_insns ());
+ }
+
+ if (get_pool_size () > S390_POOL_MAX)
+ s390_final_chunkify (1);
+ else
+ s390_final_chunkify (0);
+
+ if (current_function_uses_pic_offset_table)
+ {
+
+ got_label = force_const_mem (Pmode,
+ gen_rtx (SYMBOL_REF, Pmode,
+ &"i@_GLOBAL_OFFSET_TABLE_"[2]));
+ emit_insn_before (gen_rtx (USE, Pmode, got_label), get_insns ());
+ }
+
+ if ((so = STARTING_FRAME_OFFSET + lsize) > 0x7fff)
+ {
+
+ stack_label = force_const_mem (Pmode, gen_rtx_CONST_INT (Pmode, so));
+ emit_insn_before (gen_rtx (USE, Pmode, stack_label), get_insns ());
+ }
+
+ if (cur_is_leaf_function ())
+ {
+ leaf_function_flag = 1;
+ fprintf (file, "%s\tleaf function\n", ASM_COMMENT_START);
+ fprintf (file, "%s\thas varargs %d\n", ASM_COMMENT_START,
+ current_function_stdarg);
+ fprintf (file, "%s\tincoming args (stack) %d\n", ASM_COMMENT_START,
+ current_function_args_size);
+ fprintf (file, "%s\tfunction length %d\n", ASM_COMMENT_START,
+ insn_current_address);
+
+ /* Save gprs 6 - 15 and fprs 4 and 6. */
+ if (flag_pic)
+ for (i = 6; i < 12 && (regs_ever_live[i] == 0); i++);
+ else
+ for (i = 6; i < 13 && (regs_ever_live[i] == 0); i++);
+
+ if (! (! flag_pic && i == 13 &&
+ regs_ever_live[14] == 0 && ! get_pool_size ()))
+ {
+ fprintf (file, "\tSTM\t%d,14,%d(%d)\n", i, i * 4,
+ STACK_POINTER_REGNUM);
+#ifdef INCOMING_RETURN_ADDR_RTX
+ if (dwarf2out_do_frame ())
+ {
+ l = dwarf2out_cfi_label ();
+ dwarf2out_def_cfa (l, STACK_POINTER_REGNUM,
+ STACK_POINTER_OFFSET);
+ for (j = i; j <= 14; j++)
+ dwarf2out_reg_save (l, j, (j-24)*4);
+ if (regs_ever_live[18])
+ dwarf2out_reg_save (l, 18, -16);
+ if (regs_ever_live[19])
+ dwarf2out_reg_save (l, 19, -8);
+ }
+
+#endif
+ }
+
+ /* Output constant pool. */
+ if (get_pool_size ())
+ {
+ s390_pool_count = 0;
+ fprintf (file, "\tBRAS\t%d,.LTN%X_%X\n", BASE_REGISTER,
+ s390_function_count, s390_pool_count);
+ fprintf (file, ".LT%X_%X:\n", s390_function_count, s390_pool_count);
+ output_constant_pool (current_function_name, current_function_decl);
+ fprintf (file, ".LTN%X_%X:\n", s390_function_count,
+ s390_pool_count);
+ regs_ever_live[BASE_REGISTER] = 1;
+ }
+
+ /* Save fprs. */
+
+ if (regs_ever_live[18])
+ fprintf (file, "\tSTD\t4,80(%d)\n", STACK_POINTER_REGNUM);
+ if (regs_ever_live[19])
+ fprintf (file, "\tSTD\t6,88(%d)\n", STACK_POINTER_REGNUM);
+
+ }
+ else
+ { /* No leaf function. */
+ fprintf (file, "%s\tleaf function %d\n", ASM_COMMENT_START,
+ leaf_function_p ());
+ fprintf (file, "%s\tautomatics %d\n", ASM_COMMENT_START,
+ lsize);
+ fprintf (file, "%s\toutgoing args %d\n", ASM_COMMENT_START,
+ current_function_outgoing_args_size);
+ fprintf (file, "%s\tneed frame pointer %d\n", ASM_COMMENT_START,
+ frame_pointer_needed);
+ fprintf (file, "%s\tcall alloca %d\n", ASM_COMMENT_START,
+ current_function_calls_alloca);
+ fprintf (file, "%s\thas varargs %d\n", ASM_COMMENT_START,
+ current_function_stdarg || current_function_varargs);
+ fprintf (file, "%s\tincoming args (stack) %d\n", ASM_COMMENT_START,
+ current_function_args_size);
+ fprintf (file, "%s\tfunction length %d\n", ASM_COMMENT_START,
+ insn_current_address);
+
+ /* Save gprs 6 - 15 and fprs 4 and 6. */
+
+ if (current_function_stdarg || current_function_varargs)
+ {
+ i = 2;
+ }
+ else
+ {
+ for (i = 6; i < 11 && (regs_ever_live[i] == 0); i++);
+ }
+
+ fprintf (file, "\tSTM\t%d,15,%d(%d)\n", i, i * 4, STACK_POINTER_REGNUM);
+
+#ifdef INCOMING_RETURN_ADDR_RTX
+ if (dwarf2out_do_frame ())
+ {
+ l = dwarf2out_cfi_label ();
+ dwarf2out_def_cfa (l, STACK_POINTER_REGNUM, STACK_POINTER_OFFSET);
+ for (j = i; j <= 15; j++)
+ dwarf2out_reg_save (l, j, (j-24)*4);
+ if (regs_ever_live[18])
+ dwarf2out_reg_save (l, 18, -16);
+ if (regs_ever_live[19])
+ dwarf2out_reg_save (l, 19, -8);
+ }
+#endif
+
+
+ /* Output constant pool. */
+
+ if (get_pool_size ())
+ {
+ s390_pool_count = 0;
+ fprintf (file, "\tBRAS\t%d,.LTN%X_%X\n", BASE_REGISTER,
+ s390_function_count, s390_pool_count);
+ fprintf (file, ".LT%X_%X:\n", s390_function_count, s390_pool_count);
+ output_constant_pool (current_function_name, current_function_decl);
+ fprintf (file, ".LTN%X_%X:\n", s390_function_count,
+ s390_pool_count);
+ }
+
+ /* Save fprs. */
+
+ if (current_function_stdarg || current_function_varargs)
+ {
+ fprintf (file, "\tSTD\t0,64(%d)\n", STACK_POINTER_REGNUM);
+ fprintf (file, "\tSTD\t2,72(%d)\n", STACK_POINTER_REGNUM);
+ }
+ if (regs_ever_live[18])
+ fprintf (file, "\tSTD\t4,80(%d)\n", STACK_POINTER_REGNUM);
+ if (regs_ever_live[19])
+ fprintf (file, "\tSTD\t6,88(%d)\n", STACK_POINTER_REGNUM);
+
+ /* Decrement stack. */
+
+ if (TARGET_BACKCHAIN || (STARTING_FRAME_OFFSET + lsize + 4 * i > 4095
+ || frame_pointer_needed
+ || current_function_calls_alloca))
+ {
+
+ fprintf (file, "\tLR\t1,%d\n", STACK_POINTER_REGNUM);
+ }
+
+ if (stack_label)
+ {
+ rtx operands[2];
+
+ operands[0] = gen_rtx (REG, Pmode, 15);
+ operands[1] = stack_label;
+ output_asm_insn ("S\t%0,%1", operands);
+ }
+ else
+ {
+ fprintf (file, "\tAHI\t%d,-%d\n", STACK_POINTER_REGNUM, so);
+ }
+#ifdef INCOMING_RETURN_ADDR_RTX
+ if (dwarf2out_do_frame ())
+ {
+ if (frame_pointer_needed)
+ dwarf2out_def_cfa ("", FRAME_POINTER_REGNUM,
+ STACK_POINTER_OFFSET+so);
+ else
+ dwarf2out_def_cfa ("", STACK_POINTER_REGNUM,
+ STACK_POINTER_OFFSET+so);
+ }
+#endif
+
+
+ /* Generate backchain. */
+
+ if (TARGET_BACKCHAIN || (STARTING_FRAME_OFFSET + lsize + 4 * i > 4095
+ || frame_pointer_needed
+ || current_function_calls_alloca))
+ {
+ fprintf (file, "\tST\t1,0(%d)\n", STACK_POINTER_REGNUM);
+ }
+ }
+
+ if (frame_pointer_needed)
+ {
+ fprintf (file, "\tLR\t%d,%d\n", FRAME_POINTER_REGNUM,
+ STACK_POINTER_REGNUM);
+ }
+
+ /* Load GOT if used and emit use insn that optimizer does not
+ erase literal pool entry. */
+
+ if (current_function_uses_pic_offset_table)
+ {
+ rtx operands[3];
+
+ operands[0] = gen_rtx (REG, Pmode, 12);
+ operands[1] = got_label;
+ operands[2] = gen_rtx (REG, Pmode, 13);
+ output_asm_insn ("L\t%0,%1\n\tAR\t%0,%2", operands);
+ }
+ return 0;
+}
+
+/* This function generates the assembly code for function exit. */
+
+int
+s390_function_epilogue (FILE * file, int lsize)
+{
+/* Register is call clobbered and not used for eh or return. */
+#define FREE_REG 4
+
+ int i;
+ int return_reg = RETURN_REGNUM;
+ int fp, offset;
+
+ if (leaf_function_flag)
+ {
+ if (flag_pic)
+ for (i = 6; i < 12 && (regs_ever_live[i] == 0); i++);
+ else
+ for (i = 6; i < 13 && (regs_ever_live[i] == 0); i++);
+ if (! (! flag_pic && i == 13 &&
+ regs_ever_live[14] == 0 && ! get_pool_size ()))
+ {
+ if (flag_pic > 1)
+ {
+ return_reg = FREE_REG;
+ fprintf (file, "\tL\t%d,56(%d)\n", return_reg,
+ STACK_POINTER_REGNUM);
+ }
+ if (regs_ever_live[18])
+ fprintf (file, "\tLD\t4,80(%d)\n", STACK_POINTER_REGNUM);
+ if (regs_ever_live[19])
+ fprintf (file, "\tLD\t6,88(%d)\n", STACK_POINTER_REGNUM);
+ if (flag_pic > 1)
+ fprintf (file, "\tLM\t%d,14,%d(%d)\n", i, 4 * i,
+ STACK_POINTER_REGNUM);
+ else
+ fprintf (file, "\tLM\t%d,13,%d(%d)\n", i, 4 * i,
+ STACK_POINTER_REGNUM);
+ }
+ }
+ else
+ {
+ for (i = 6; i < 11 && (regs_ever_live[i] == 0); i++);
+
+ if (STARTING_FRAME_OFFSET + lsize + 88 > 4095)
+ {
+ offset = 0;
+ fp = STACK_POINTER_REGNUM;
+ }
+ else if (frame_pointer_needed || current_function_calls_alloca)
+ {
+ offset = STARTING_FRAME_OFFSET + lsize;
+ fp = FRAME_POINTER_REGNUM;
+ }
+ else
+ {
+ offset = STARTING_FRAME_OFFSET + lsize;
+ fp = STACK_POINTER_REGNUM;
+ }
+ if (offset == 0)
+ fprintf (file, "\tL\t%d,0(%d)\n",fp,fp);
+ return_reg = FREE_REG;
+ fprintf (file, "\tL\t%d,%d(%d)\n",return_reg,56+offset,fp);
+ if (regs_ever_live[18])
+ fprintf (file, "\tLD\t4,%d(%d)\n",80+offset,fp);
+ if (regs_ever_live[19])
+ fprintf (file, "\tLD\t6,%d(%d)\n",88+offset,fp);
+
+ fprintf (file, "\tLM\t%d,15,%d(%d)\n", i, (4 * i) + offset,fp);
+ }
+
+ fprintf (file, "\tBR\t%d\n", return_reg);
+
+ current_function_uses_pic_offset_table = 0;
+ leaf_function_flag = 0;
+ s390_pool_start_insn = NULL_RTX;
+ s390_pool_count = -1;
+ s390_function_count++;
+ return 0;
+}
+
+/* This is epilogue code, maybe should use generic in except.c. */
+
+void
+s390_expand_eh_epilogue (rtx reg1, rtx reg2, rtx reg3)
+{
+ rtx stub_start, after_stub;
+ rtx ra, tmp;
+
+ /* Otherwise, use the same stub technique we had before. */
+
+ eh_return_stub_label = stub_start = gen_label_rtx ();
+ after_stub = gen_label_rtx ();
+
+ /* Set the return address to the stub label. */
+
+ ra = expand_builtin_return_addr (BUILT_IN_RETURN_ADDRESS,
+ 0, hard_frame_pointer_rtx);
+ if (GET_CODE (ra) == REG && REGNO (ra) >= FIRST_PSEUDO_REGISTER)
+ abort ();
+
+ tmp = memory_address (Pmode, gen_rtx_LABEL_REF (Pmode, stub_start));
+
+ tmp = force_operand (tmp, ra);
+ if (tmp != ra)
+ emit_move_insn (ra, tmp);
+
+ /* Indicate that the registers are in fact used. */
+ emit_insn (gen_rtx_USE (VOIDmode, reg1));
+ emit_insn (gen_rtx_USE (VOIDmode, reg2));
+ emit_insn (gen_rtx_USE (VOIDmode, reg3));
+ if (GET_CODE (ra) == REG)
+ emit_insn (gen_rtx_USE (VOIDmode, ra));
+
+ emit_move_insn (gen_rtx_REG (Pmode, 1), reg3);
+ /* Generate the stub. */
+
+ emit_jump (after_stub);
+ emit_label (stub_start);
+
+ reg1 = gen_rtx_REG (Pmode, 2);
+
+ emit_indirect_jump (gen_rtx_REG (Pmode, 1));
+
+ emit_label (after_stub);
+}
+
+
+/* For structs of odd size the address is passed as reference.
+ Complex number are also passes on the stack.
+
+ Note: We don't use mode, since a struct with the following format
+ is BLKmode, but has size 4.
+ struct
+ {
+ char a;
+ char b[3]
+ }.
+ The ABI states, that this value has to be passed in register. */
+
+int
+s390_function_arg_pass_by_reference (enum machine_mode mode, tree type)
+{
+ int size;
+ if (type)
+ size = int_size_in_bytes (type);
+ else
+ {
+ /* Library call, no type available. */
+ if (mode == BLKmode)
+ return 1;
+ return 0;
+ }
+
+ if ((AGGREGATE_TYPE_P (type) &&
+ size != 1 &&
+ size != 2 &&
+ size != 4 &&
+ size != 8) ||
+ (TREE_CODE (type) == COMPLEX_TYPE))
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/* Update the data in CUM to advance over an argument of mode MODE and
+ data type TYPE. (TYPE is null for libcalls where that information
+ may not be available.). */
+
+void
+s390_function_arg_advance (CUMULATIVE_ARGS * cum,
+ enum machine_mode mode, tree type, int named)
+{
+ if (! TARGET_SOFT_FLOAT && (mode == DFmode || mode == SFmode))
+ {
+ cum->fprs++;
+ }
+ else if (s390_function_arg_pass_by_reference (mode, type))
+ {
+ cum->gprs += 1;
+ }
+ else
+ {
+ cum->gprs += ((GET_MODE_SIZE (mode) + 3) / 4);
+ }
+}
+
+
+
+/* Define where to put the arguments to a function. Value is zero to push
+ the argument on the stack, or a hard register in which to store the
+ argument. Gprs 2-6 and Fprs 0 and 2 are used as arguments.
+ All integral values go into register, until all are used up, the rest
+ goes onto stack. The same is valid for floating-point values. */
+
+struct rtx_def *
+s390_function_arg (CUMULATIVE_ARGS * cum,
+ enum machine_mode mode, tree type, int named)
+{
+ if (s390_function_arg_pass_by_reference (mode, type))
+ return 0;
+
+ if (! TARGET_SOFT_FLOAT && (mode == DFmode || mode == SFmode))
+ {
+ if (cum->fprs > 1)
+ {
+ return 0;
+ }
+ else
+ return gen_rtx (REG, mode, cum->fprs + 16);
+ }
+ else
+ {
+ if ((cum->gprs > 4) ||
+ ((mode == DFmode || mode == DImode) && cum->gprs > 3))
+ {
+ return 0;
+ }
+ else
+ return gen_rtx (REG, mode, cum->gprs + 2);
+ }
+}
+
+
+/* Builtin va_list stuff
+ va_list is a structure of four elements:
+ gpr: number of named args passed in general purpose register
+ gpr: number of named args passed in floating purpose register
+ overflow_arg_area: address of area, where arguments are passed
+ if they do not fit in gprs 2 to 6 and fpr 0 and 2
+ reg_save_area: address, where register passed args are saved
+ in prologue. */
+
+tree
+s390_build_va_list ()
+{
+ tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl;
+
+ record = make_lang_type (RECORD_TYPE);
+ type_decl =
+ build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
+
+ f_gpr = build_decl (FIELD_DECL, get_identifier ("gpr"), integer_type_node);
+ f_fpr = build_decl (FIELD_DECL, get_identifier ("fpr"), integer_type_node);
+ f_ovf = build_decl (FIELD_DECL, get_identifier ("overflow_arg_area"),
+ ptr_type_node);
+ f_sav = build_decl (FIELD_DECL, get_identifier ("reg_save_area"),
+ ptr_type_node);
+
+ DECL_FIELD_CONTEXT (f_gpr) = record;
+ DECL_FIELD_CONTEXT (f_fpr) = record;
+ DECL_FIELD_CONTEXT (f_ovf) = record;
+ DECL_FIELD_CONTEXT (f_sav) = record;
+
+ TREE_CHAIN (record) = type_decl;
+ TYPE_NAME (record) = type_decl;
+ TYPE_FIELDS (record) = f_gpr;
+ TREE_CHAIN (f_gpr) = f_fpr;
+ TREE_CHAIN (f_fpr) = f_ovf;
+ TREE_CHAIN (f_ovf) = f_sav;
+
+ layout_type (record);
+
+ /* The correct type is an array type of one element. */
+ return build_array_type (record, build_index_type (size_zero_node));
+}
+
+/* Builtin va_start
+ The va_list struct is set with the values.
+ gpr: compile time known got out of current_function_args_info
+ fpr: compile time known got out of current_function_args_info
+ overflow_arg_area: address passed with register 7 (incoming args register)
+ (setup in prologue)
+ reg_save_area: address of save area where first 5 gprs and 2 fprs sare
+ saved (saved in prologue). */
+
+void
+s390_va_start (int stdarg_p, tree valist, rtx nextarg)
+{
+ HOST_WIDE_INT n_gpr, n_fpr;
+ int off;
+ tree f_gpr, f_fpr, f_ovf, f_sav;
+ tree gpr, fpr, ovf, sav, t;
+
+ f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
+ f_fpr = TREE_CHAIN (f_gpr);
+ f_ovf = TREE_CHAIN (f_fpr);
+ f_sav = TREE_CHAIN (f_ovf);
+
+ valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
+ gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr);
+ fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr);
+ ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf);
+ sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav);
+
+ /* Count number of gp and fp argument registers used. */
+
+ n_gpr = current_function_args_info.gprs;
+ n_fpr = current_function_args_info.fprs;
+
+ t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, build_int_2 (n_gpr, 0));
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr, build_int_2 (n_fpr, 0));
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Find the overflow area. */
+ t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
+
+ off = INTVAL (current_function_arg_offset_rtx);
+ off = off < 0 ? 0 : off;
+ if (! stdarg_p)
+ off = off > 0 ? off - 4 : off;
+ if (TARGET_DEBUG_ARG)
+ fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n",
+ n_gpr, n_fpr, off);
+
+ t = build (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_2 (off, 0));
+
+ t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Find the register save area. */
+ t = make_tree (TREE_TYPE (sav), virtual_incoming_args_rtx);
+ t = build (PLUS_EXPR, TREE_TYPE (sav), t,
+ build_int_2 (-STACK_POINTER_OFFSET, -1));
+ t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+}
+
+
+/* Builtin va_arg.
+
+ Works like following:
+
+ if (integral value) {
+ if (size <= 4 && args.gpr < 5 ||
+ size > 4 && args.gpr < 4 )
+ ret = args.reg_save_area[args.gpr+8]
+ else
+ ret = *args.overflow_arg_area++;
+ } else if (float value) {
+ if (args.fgpr < 2)
+ ret = args.reg_save_area[args.fpr+64]
+ else
+ ret = *args.overflow_arg_area++;
+ } else if (aggregate value) {
+ if (args.gpr < 5)
+ ret = *args.reg_save_area[args.gpr]
+ else
+ ret = **args.overflow_arg_area++;
+ } */
+
+
+rtx
+s390_va_arg (tree valist, tree type)
+{
+ tree f_gpr, f_fpr, f_ovf, f_sav;
+ tree gpr, fpr, ovf, sav, reg, t, u;
+ int indirect_p, size, rsize, n_reg, sav_ofs, sav_scale, max_reg;
+ rtx lab_false, lab_over, addr_rtx, r;
+
+ f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
+ f_fpr = TREE_CHAIN (f_gpr);
+ f_ovf = TREE_CHAIN (f_fpr);
+ f_sav = TREE_CHAIN (f_ovf);
+
+ valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), valist);
+ gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr);
+ fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr);
+ ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf);
+ sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav);
+
+ size = int_size_in_bytes (type);
+ rsize = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+ if (s390_function_arg_pass_by_reference (TYPE_MODE (type), type))
+ {
+ if (TARGET_DEBUG_ARG)
+ {
+ fprintf (stderr, "va_arg: aggregate type");
+ debug_tree (type);
+ }
+
+ /* Aggregates are passed by reference. */
+ indirect_p = 1;
+ reg = gpr;
+ n_reg = 1;
+ sav_ofs = 8;
+ sav_scale = 4;
+ size = rsize = UNITS_PER_WORD;
+ max_reg = 5;
+ }
+ else if (FLOAT_TYPE_P (type) && ! TARGET_SOFT_FLOAT)
+ {
+ if (TARGET_DEBUG_ARG)
+ {
+ fprintf (stderr, "va_arg: float type");
+ debug_tree (type);
+ }
+
+ /* FP args go in FP registers, if present. */
+ indirect_p = 0;
+ reg = fpr;
+ n_reg = 1;
+ sav_ofs = 64;
+ sav_scale = 8;
+ max_reg = 1;
+ }
+ else
+ {
+ if (TARGET_DEBUG_ARG)
+ {
+ fprintf (stderr, "va_arg: other type");
+ debug_tree (type);
+ }
+
+ /* Otherwise into GP registers. */
+ indirect_p = 0;
+ reg = gpr;
+ n_reg = rsize;
+ sav_ofs =
+ TYPE_MODE (type) == HImode ? 10 : TYPE_MODE (type) == QImode ? 11 : 8;
+ sav_scale = 4;
+ if (rsize > 1)
+ max_reg = 3;
+ else
+ max_reg = 4;
+ }
+
+ /* Pull the value out of the saved registers ... */
+
+ lab_false = gen_label_rtx ();
+ lab_over = gen_label_rtx ();
+ addr_rtx = gen_reg_rtx (Pmode);
+
+
+ emit_cmp_and_jump_insns (expand_expr (reg, NULL_RTX, SImode, EXPAND_NORMAL),
+ GEN_INT (max_reg),
+ GT, const1_rtx, SImode, 0, 1, lab_false);
+
+ if (sav_ofs)
+ t = build (PLUS_EXPR, ptr_type_node, sav, build_int_2 (sav_ofs, 0));
+ else
+ t = sav;
+
+ u = build (MULT_EXPR, integer_type_node, reg, build_int_2 (sav_scale, 0));
+ TREE_SIDE_EFFECTS (u) = 1;
+
+ t = build (PLUS_EXPR, ptr_type_node, t, u);
+ TREE_SIDE_EFFECTS (t) = 1;
+
+ r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+ if (r != addr_rtx)
+ emit_move_insn (addr_rtx, r);
+
+ u = build (PREINCREMENT_EXPR, TREE_TYPE (reg), reg, build_int_2 (n_reg, 0));
+ TREE_SIDE_EFFECTS (u) = 1;
+ expand_expr (u, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ emit_jump_insn (gen_jump (lab_over));
+ emit_barrier ();
+ emit_label (lab_false);
+
+ /* ... Otherwise out of the overflow area. */
+
+ t = save_expr (ovf);
+
+ r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+ if (r != addr_rtx)
+ emit_move_insn (addr_rtx, r);
+
+ t = build (PLUS_EXPR, TREE_TYPE (t), t, build_int_2 (size, 0));
+ t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
+ TREE_SIDE_EFFECTS (t) = 1;
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ emit_label (lab_over);
+
+ if (indirect_p)
+ {
+ r = gen_rtx_MEM (Pmode, addr_rtx);
+ MEM_ALIAS_SET (r) = get_varargs_alias_set ();
+ emit_move_insn (addr_rtx, r);
+ }
+
+
+ return addr_rtx;
+}
+
+/* Implementation of Trampoline
+ Gpr 1 is used as base register and for the jump
+ to the nested function.
+ Gpr 0 is static chain. */
+
+void
+s390_trampoline_template (FILE * file)
+{
+ fprintf (file, "basr\t1,0\n");
+ fprintf (file, "L\t0,10(1)\n");
+ fprintf (file, "L\t1,14(1)\n");
+ fprintf (file, "BR\t1\n");
+ fprintf (file, ".long\t0\n");
+ fprintf (file, ".long\t0\n");
+}
+
+void
+s390_initialize_trampoline (addr, fnaddr, cxt)
+ rtx addr;
+ rtx fnaddr;
+ rtx cxt;
+{
+ emit_move_insn (gen_rtx (MEM, Pmode,
+ memory_address (Pmode, plus_constant (addr, 12))),
+ cxt);
+ emit_move_insn (gen_rtx
+ (MEM, Pmode,
+ memory_address (Pmode, plus_constant (addr, 16))), fnaddr);
+}
+
+
+/* Print an integer constant expression in assembler syntax. Addition
+ and subtraction are the only arithmetic that may appear in these
+ expressions. FILE is the stdio stream to write to, X is the rtx. */
+
+void
+output_pic_addr_const (FILE * file, rtx x)
+{
+ char code = ' ';
+ char buf[256];
+ const char *name;
+ switch (GET_CODE (x))
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ if (GET_CODE (x) == SYMBOL_REF)
+ {
+ if (XSTR (x, 0)[0] == '@')
+ {
+ code = 'P';
+ name = XSTR (x, 0) + 1;
+ }
+ else
+ {
+ code = XSTR (x, 0)[-2];
+ name = XSTR (x, 0);
+ }
+ assemble_name (asm_out_file,(char*) name);
+ }
+ else
+ {
+
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L",
+ CODE_LABEL_NUMBER (XEXP (x, 0)));
+ assemble_name (asm_out_file, buf);
+ }
+ if (code == 'P')
+ {
+ fprintf (file, "@PLT-.LT%X_%X",
+ s390_function_count, s390_pool_count);
+ }
+ else if (SYMBOL_REF_FLAG (x))
+ {
+ fprintf (file, "@GOT");
+ }
+ else
+ {
+ fprintf (file, "-.LT%X_%X", s390_function_count, s390_pool_count);
+ }
+ break;
+
+ case CODE_LABEL:
+ ASM_GENERATE_INTERNAL_LABEL (buf, "L", CODE_LABEL_NUMBER (x));
+ assemble_name (asm_out_file, buf);
+ break;
+
+ case CONST_INT:
+ fprintf (file, "%d", INTVAL (x));
+ break;
+
+ case CONST:
+ output_pic_addr_const (file, XEXP (x, 0));
+ break;
+
+ case PLUS:
+ output_pic_addr_const (file, XEXP (x, 0));
+ fprintf (file, "+");
+ output_pic_addr_const (file, XEXP (x, 1));
+ break;
+
+ case MINUS:
+ output_pic_addr_const (file, XEXP (x, 0));
+ fprintf (file, "-");
+ output_pic_addr_const (file, XEXP (x, 1));
+ break;
+
+ default:
+ output_addr_const (file, x);
+ }
+}
+
+
+void
+s390_output_const (FILE * file, rtx x, enum machine_mode mode)
+{
+
+ char code;
+ REAL_VALUE_TYPE rv;
+
+ if (mode == SFmode)
+ {
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ ASM_OUTPUT_FLOAT (file, rv);
+ }
+ else if (mode == DFmode)
+ {
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ ASM_OUTPUT_DOUBLE (file, rv);
+ }
+ else if (mode == HImode)
+ ASM_OUTPUT_SHORT (file, x);
+ else if (mode == DImode)
+ ASM_OUTPUT_DOUBLE_INT (file, x);
+ else
+ {
+ fprintf (file, "%s\t", ASM_LONG);
+ if (flag_pic)
+ {
+ output_pic_addr_const (file, x);
+ }
+ else
+ {
+ if ((GET_CODE (x) == SYMBOL_REF) &&
+ (XSTR (x, 0)[0] == '@') &&
+ (((code = XSTR (x, 0)[-1]) == 'F') || (code == 'f')))
+ {
+
+ assemble_name (file, &XSTR (x, 0)[1]);
+ }
+ else
+ {
+ output_addr_const (file, x);
+ }
+ }
+ }
+}
+
+
+
+void
+s390_asm_output_external_libcall (FILE * file, rtx x)
+{
+ char *new_str;
+ new_str = permalloc (strlen (XSTR (x, 0)) + 3);
+ strcpy (new_str + 2, XSTR (x, 0));
+ XSTR (x, 0) = new_str + 2;
+ new_str[0] = 'F';
+ new_str[1] = '@';
+ ASM_GLOBALIZE_LABEL (file, new_str + 2);
+}
+
+int
+legitimate_pic_operand_p (rtx x)
+{
+ if (GET_CODE (x) == SYMBOL_REF && ! (flag_pic > 1) && SYMBOL_REF_FLAG (x))
+ return 1;
+ return legitimate_constant_p (x);
+}
+
+int
+legitimate_constant_p (rtx x)
+{
+ if (flag_pic)
+ {
+ if (GET_CODE (x) == CONST ||
+ GET_CODE (x) == LABEL_REF ||
+ GET_CODE (x) == SYMBOL_REF)
+ return 1;
+ }
+ if (GET_CODE (x) == CONST_DOUBLE ||
+ GET_CODE (x) == CONST ||
+ GET_CODE (x) == LABEL_REF ||
+ GET_CODE (x) == SYMBOL_REF ||
+ (GET_CODE (x) == CONST_INT &&
+ (INTVAL (x) < -32768 || INTVAL (x) > 32767)))
+ return 0;
+ return 1;
+}