OSDN Git Service

config:
[pf3gnuchains/gcc-fork.git] / gcc / config / i386 / winnt.c
index 813724c..2d85b66 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines for insn-output.c for Windows NT.
    Contributed by Douglas Rupp (drupp@cs.washington.edu)
-   Copyright (C) 1995 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1997, 1998 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -16,76 +16,566 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
-#include <stdio.h>
 #include "config.h"
+#include "system.h"
 #include "rtl.h"
 #include "regs.h"
 #include "hard-reg-set.h"
 #include "output.h"
 #include "tree.h"
 #include "flags.h"
+#include "tm_p.h"
+#include "toplev.h"
 
-/* This function generates the assembly code for function entry.
-   FILE is an stdio stream to output the code to.
-   SIZE is an int: how many units of temporary storage to allocate. */
+/* i386/PE specific attribute support.
+
+   i386/PE has two new attributes:
+   dllexport - for exporting a function/variable that will live in a dll
+   dllimport - for importing a function/variable from a dll
+
+   Microsoft allows multiple declspecs in one __declspec, separating
+   them with spaces.  We do NOT support this.  Instead, use __declspec
+   multiple times.
+*/
+
+static tree associated_type PARAMS ((tree));
+const char * gen_stdcall_suffix PARAMS ((tree));
+int i386_pe_dllexport_p PARAMS ((tree));
+int i386_pe_dllimport_p PARAMS ((tree));
+void i386_pe_mark_dllexport PARAMS ((tree));
+void i386_pe_mark_dllimport PARAMS ((tree));
+
+/* Return nonzero if ATTR is a valid attribute for DECL.
+   ATTRIBUTES are any existing attributes and ARGS are the arguments
+   supplied with ATTR.  */
+
+int
+i386_pe_valid_decl_attribute_p (decl, attributes, attr, args)
+     tree decl;
+     tree attributes;
+     tree attr;
+     tree args;
+{
+  if (args == NULL_TREE)
+    {
+      if (is_attribute_p ("dllexport", attr))
+       return 1;
+      if (is_attribute_p ("dllimport", attr))
+       return 1;
+      if (is_attribute_p ("shared", attr))
+       return TREE_CODE (decl) == VAR_DECL;
+    }
+
+  return 0;
+}
+
+/* Return nonzero if ATTR is a valid attribute for TYPE.
+   ATTRIBUTES are any existing attributes and ARGS are the arguments
+   supplied with ATTR.  */
+
+int
+i386_pe_valid_type_attribute_p (type, attributes, attr, args)
+     tree type;
+     tree attributes;
+     tree attr;
+     tree args;
+{
+  if (args == NULL_TREE
+      && (TREE_CODE (type) == RECORD_TYPE || TREE_CODE (type) == UNION_TYPE))
+    {
+      if (is_attribute_p ("dllexport", attr))
+       return 1;
+      if (is_attribute_p ("dllimport", attr))
+       return 1;
+    }
+
+  return ix86_valid_type_attribute_p (type, attributes, attr, args);
+}
+\f
+/* Return the type that we should use to determine if DECL is
+   imported or exported.  */
+
+static tree
+associated_type (decl)
+     tree decl;
+{
+  tree t = NULL_TREE;
+
+  /* In the C++ frontend, DECL_CONTEXT for a method doesn't actually refer
+     to the containing class.  So we look at the 'this' arg.  */
+  if (TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE)
+    {
+      /* Artificial methods are not affected by the import/export status of
+        their class unless they are virtual.  */
+      if (! DECL_ARTIFICIAL (decl) || DECL_VINDEX (decl))
+       t = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (decl))));
+    }
+  else if (DECL_CONTEXT (decl)
+          && TREE_CODE_CLASS (TREE_CODE (DECL_CONTEXT (decl))) == 't')
+    t = DECL_CONTEXT (decl);
+
+  return t;
+}
+
+/* Return non-zero if DECL is a dllexport'd object.  */
+
+int
+i386_pe_dllexport_p (decl)
+     tree decl;
+{
+  tree exp;
+
+  if (TREE_CODE (decl) != VAR_DECL
+      && TREE_CODE (decl) != FUNCTION_DECL)
+    return 0;
+  exp = lookup_attribute ("dllexport", DECL_MACHINE_ATTRIBUTES (decl));
+  if (exp)
+    return 1;
+
+  /* Class members get the dllexport status of their class.  */
+  if (associated_type (decl))
+    {
+      exp = lookup_attribute ("dllexport",
+                             TYPE_ATTRIBUTES (associated_type (decl)));
+      if (exp)
+       return 1;
+    }
+
+  return 0;
+}
+
+/* Return non-zero if DECL is a dllimport'd object.  */
+
+int
+i386_pe_dllimport_p (decl)
+     tree decl;
+{
+  tree imp;
+
+  if (TREE_CODE (decl) == FUNCTION_DECL
+      && TARGET_NOP_FUN_DLLIMPORT)
+    return 0;
+
+  if (TREE_CODE (decl) != VAR_DECL
+      && TREE_CODE (decl) != FUNCTION_DECL)
+    return 0;
+  imp = lookup_attribute ("dllimport", DECL_MACHINE_ATTRIBUTES (decl));
+  if (imp)
+    return 1;
+
+  /* Class members get the dllimport status of their class.  */
+  if (associated_type (decl))
+    {
+      imp = lookup_attribute ("dllimport",
+                             TYPE_ATTRIBUTES (associated_type (decl)));
+      if (imp)
+       return 1;
+    }
+
+  return 0;
+}
+
+/* Return non-zero if SYMBOL is marked as being dllexport'd.  */
+
+int
+i386_pe_dllexport_name_p (symbol)
+     const char *symbol;
+{
+  return symbol[0] == '@' && symbol[1] == 'e' && symbol[2] == '.';
+}
+
+/* Return non-zero if SYMBOL is marked as being dllimport'd.  */
+
+int
+i386_pe_dllimport_name_p (symbol)
+     const char *symbol;
+{
+  return symbol[0] == '@' && symbol[1] == 'i' && symbol[2] == '.';
+}
+
+/* Mark a DECL as being dllexport'd.
+   Note that we override the previous setting (eg: dllimport).  */
 
 void
-winnt_function_prologue (file, size)
-     FILE *file;
-     int size;
-{
-  register int regno;
-  int limit;
-  rtx xops[4];
-  int pic_reg_used = flag_pic && (current_function_uses_pic_offset_table
-                                 || current_function_uses_const_pool);
-
-  xops[0] = stack_pointer_rtx;
-  xops[1] = frame_pointer_rtx;
-  xops[2] = GEN_INT (size);
-  xops[3] = gen_rtx (REG, Pmode, 0); /* eax */
-  if (frame_pointer_needed)
+i386_pe_mark_dllexport (decl)
+     tree decl;
+{
+  const char *oldname;
+  char  *newname;
+  rtx rtlname;
+  tree idp;
+
+  rtlname = XEXP (DECL_RTL (decl), 0);
+  if (GET_CODE (rtlname) == SYMBOL_REF)
+    oldname = XSTR (rtlname, 0);
+  else if (GET_CODE (rtlname) == MEM
+          && GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
+    oldname = XSTR (XEXP (rtlname, 0), 0);
+  else
+    abort ();
+  if (i386_pe_dllimport_name_p (oldname))
+    oldname += 9;
+  else if (i386_pe_dllexport_name_p (oldname))
+    return; /* already done */
+
+  newname = alloca (strlen (oldname) + 4);
+  sprintf (newname, "@e.%s", oldname);
+
+  /* We pass newname through get_identifier to ensure it has a unique
+     address.  RTL processing can sometimes peek inside the symbol ref
+     and compare the string's addresses to see if two symbols are
+     identical.  */
+  idp = get_identifier (newname);
+
+  XEXP (DECL_RTL (decl), 0) =
+    gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (idp));
+}
+
+/* Mark a DECL as being dllimport'd.  */
+
+void
+i386_pe_mark_dllimport (decl)
+     tree decl;
+{
+  const char *oldname;
+  char  *newname;
+  tree idp;
+  rtx rtlname, newrtl;
+
+  rtlname = XEXP (DECL_RTL (decl), 0);
+  if (GET_CODE (rtlname) == SYMBOL_REF)
+    oldname = XSTR (rtlname, 0);
+  else if (GET_CODE (rtlname) == MEM
+          && GET_CODE (XEXP (rtlname, 0)) == SYMBOL_REF)
+    oldname = XSTR (XEXP (rtlname, 0), 0);
+  else
+    abort ();
+  if (i386_pe_dllexport_name_p (oldname))
     {
-      output_asm_insn ("push%L1 %1", xops);
-      output_asm_insn (AS2 (mov%L0,%0,%1), xops);
+      error ("`%s' declared as both exported to and imported from a DLL.",
+             IDENTIFIER_POINTER (DECL_NAME (decl)));
+      return;
     }
+  else if (i386_pe_dllimport_name_p (oldname))
+    {
+      /* Already done, but force correct linkage since the redeclaration 
+         might have omitted explicit extern.  Sigh.  */
+      if (TREE_CODE (decl) == VAR_DECL
+         /* ??? Is this test for vtables needed?  */
+         && !DECL_VIRTUAL_P (decl))
+       {
+         DECL_EXTERNAL (decl) = 1;
+         TREE_PUBLIC (decl) = 1;
+       }
+      return;
+    }
+
+  /* ??? One can well ask why we're making these checks here,
+     and that would be a good question.  */
 
-  if (size > 4095)
+  /* Imported variables can't be initialized. Note that C++ classes
+     are marked initial, so we need to check.  */
+  if (TREE_CODE (decl) == VAR_DECL
+      && !DECL_VIRTUAL_P (decl)
+      && (DECL_INITIAL (decl)
+          && ! TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl))))
+    {
+      error_with_decl (decl, "initialized variable `%s' is marked dllimport");
+      return;
+    }
+  /* Nor can they be static.  */
+  if (TREE_CODE (decl) == VAR_DECL
+      /* ??? Is this test for vtables needed?  */
+      && !DECL_VIRTUAL_P (decl)
+      && 0 /*???*/)
     {
-      output_asm_insn (AS2 (mov%L0, %2, %3), xops);
-      output_asm_insn ("call __chkstk", xops);
+      error_with_decl (decl, "static variable `%s' is marked dllimport");
+      return;
     }
-  else if (size)
-    output_asm_insn (AS2 (sub%L0,%2,%0), xops);
-
-  /* Note If use enter it is NOT reversed args.
-     This one is not reversed from intel!!
-     I think enter is slower.  Also sdb doesn't like it.
-     But if you want it the code is:
-     {
-     xops[3] = const0_rtx;
-     output_asm_insn ("enter %2,%3", xops);
-     }
-     */
-  limit = (frame_pointer_needed ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM);
-  for (regno = limit - 1; regno >= 0; regno--)
-    if ((regs_ever_live[regno] && ! call_used_regs[regno])
-       || (regno == PIC_OFFSET_TABLE_REGNUM && pic_reg_used))
+
+  /* `extern' needn't be specified with dllimport.
+     Specify `extern' now and hope for the best.  Sigh.  */
+  if (TREE_CODE (decl) == VAR_DECL
+      /* ??? Is this test for vtables needed?  */
+      && !DECL_VIRTUAL_P (decl))
+    {
+      DECL_EXTERNAL (decl) = 1;
+      TREE_PUBLIC (decl) = 1;
+    }
+
+  newname = alloca (strlen (oldname) + 11);
+  sprintf (newname, "@i._imp__%s", oldname);
+
+  /* We pass newname through get_identifier to ensure it has a unique
+     address.  RTL processing can sometimes peek inside the symbol ref
+     and compare the string's addresses to see if two symbols are
+     identical.  */
+  idp = get_identifier (newname);
+
+  newrtl = gen_rtx (MEM, Pmode,
+                   gen_rtx (SYMBOL_REF, Pmode,
+                            IDENTIFIER_POINTER (idp)));
+  XEXP (DECL_RTL (decl), 0) = newrtl;
+
+  /* Can't treat a pointer to this as a constant address */
+  DECL_NON_ADDR_CONST_P (decl) = 1;
+}
+
+/* Return string which is the former assembler name modified with a 
+   suffix consisting of an atsign (@) followed by the number of bytes of 
+   arguments */
+
+const char *
+gen_stdcall_suffix (decl)
+  tree decl;
+{
+  int total = 0;
+  /* ??? This probably should use XSTR (XEXP (DECL_RTL (decl), 0), 0) instead
+     of DECL_ASSEMBLER_NAME.  */
+  const char *asmname = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+  char *newsym;
+
+  if (TYPE_ARG_TYPES (TREE_TYPE (decl)))
+    if (TREE_VALUE (tree_last (TYPE_ARG_TYPES (TREE_TYPE (decl)))) 
+        == void_type_node)
       {
-       xops[0] = gen_rtx (REG, SImode, regno);
-       output_asm_insn ("push%L0 %0", xops);
+       tree formal_type = TYPE_ARG_TYPES (TREE_TYPE (decl));
+
+       while (TREE_VALUE (formal_type) != void_type_node)
+         {
+           int parm_size
+             = TREE_INT_CST_LOW (TYPE_SIZE (TREE_VALUE (formal_type)));
+           /* Must round up to include padding.  This is done the same
+              way as in store_one_arg.  */
+           parm_size = ((parm_size + PARM_BOUNDARY - 1)
+                        / PARM_BOUNDARY * PARM_BOUNDARY);
+           total += parm_size;
+           formal_type = TREE_CHAIN (formal_type);
+         }
       }
 
-  if (pic_reg_used)
+  newsym = xmalloc (strlen (asmname) + 10);
+  sprintf (newsym, "%s@%d", asmname, total/BITS_PER_UNIT);
+  return IDENTIFIER_POINTER (get_identifier (newsym));
+}
+
+/* Cover function to implement ENCODE_SECTION_INFO.  */
+
+void
+i386_pe_encode_section_info (decl)
+     tree decl;
+{
+  /* This bit is copied from i386.h.  */
+  if (optimize > 0 && TREE_CONSTANT (decl)
+      && (!flag_writable_strings || TREE_CODE (decl) != STRING_CST))
+    {
+      rtx rtl = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd'
+                 ? TREE_CST_RTL (decl) : DECL_RTL (decl));
+      SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
+    }
+
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    if (lookup_attribute ("stdcall",
+                         TYPE_ATTRIBUTES (TREE_TYPE (decl))))
+      XEXP (DECL_RTL (decl), 0) = 
+       gen_rtx (SYMBOL_REF, Pmode, gen_stdcall_suffix (decl));
+
+  /* Mark the decl so we can tell from the rtl whether the object is
+     dllexport'd or dllimport'd.  */
+
+  if (i386_pe_dllexport_p (decl))
+    i386_pe_mark_dllexport (decl);
+  else if (i386_pe_dllimport_p (decl))
+    i386_pe_mark_dllimport (decl);
+  /* It might be that DECL has already been marked as dllimport, but a
+     subsequent definition nullified that.  The attribute is gone but
+     DECL_RTL still has @i._imp__foo.  We need to remove that. Ditto
+     for the DECL_NON_ADDR_CONST_P flag.  */
+  else if ((TREE_CODE (decl) == FUNCTION_DECL
+           || TREE_CODE (decl) == VAR_DECL)
+          && DECL_RTL (decl) != NULL_RTX
+          && GET_CODE (DECL_RTL (decl)) == MEM
+          && GET_CODE (XEXP (DECL_RTL (decl), 0)) == MEM
+          && GET_CODE (XEXP (XEXP (DECL_RTL (decl), 0), 0)) == SYMBOL_REF
+          && i386_pe_dllimport_name_p (XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0)))
+    {
+      const char *oldname = XSTR (XEXP (XEXP (DECL_RTL (decl), 0), 0), 0);
+      tree idp = get_identifier (oldname + 9);
+      rtx newrtl = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (idp));
+
+      XEXP (DECL_RTL (decl), 0) = newrtl;
+
+      DECL_NON_ADDR_CONST_P (decl) = 0;
+
+      /* We previously set TREE_PUBLIC and DECL_EXTERNAL.
+        We leave these alone for now.  */
+    }
+}
+
+/* Cover function for UNIQUE_SECTION.  */
+
+void
+i386_pe_unique_section (decl, reloc)
+     tree decl;
+     int reloc;
+{
+  int len;
+  const char *name, *prefix;
+  char *string;
+
+  name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+  /* Strip off any encoding in fnname.  */
+  STRIP_NAME_ENCODING (name, name);
+
+  /* The object is put in, for example, section .text$foo.
+     The linker will then ultimately place them in .text
+     (everything from the $ on is stripped). Don't put
+     read-only data in .rdata section to avoid a PE linker 
+     bug when .rdata$* grouped sections are used in code
+     without a .rdata section.  */
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    prefix = ".text$";
+/* else if (DECL_INITIAL (decl) == 0
+          || DECL_INITIAL (decl) == error_mark_node)
+    prefix = ".bss";  */
+  else if (DECL_READONLY_SECTION (decl, reloc))
+#ifdef READONLY_DATA_SECTION
+    prefix = ".rdata$";
+#else
+    prefix = ".text$";
+#endif
+  else
+    prefix = ".data$";
+  len = strlen (name) + strlen (prefix);
+  string = alloca (len + 1);
+  sprintf (string, "%s%s", prefix, name);
+
+  DECL_SECTION_NAME (decl) = build_string (len, string);
+}
+\f
+/* The Microsoft linker requires that every function be marked as
+   DT_FCN.  When using gas on cygwin, we must emit appropriate .type
+   directives.  */
+
+#include "gsyms.h"
+
+/* Mark a function appropriately.  This should only be called for
+   functions for which we are not emitting COFF debugging information.
+   FILE is the assembler output file, NAME is the name of the
+   function, and PUBLIC is non-zero if the function is globally
+   visible.  */
+
+void
+i386_pe_declare_function_type (file, name, public)
+     FILE *file;
+     const char *name;
+     int public;
+{
+  fprintf (file, "\t.def\t");
+  assemble_name (file, name);
+  fprintf (file, ";\t.scl\t%d;\t.type\t%d;\t.endef\n",
+          public ? (int) C_EXT : (int) C_STAT,
+          (int) DT_FCN << N_BTSHFT);
+}
+
+/* Keep a list of external functions.  */
+
+struct extern_list
+{
+  struct extern_list *next;
+  const char *name;
+};
+
+static struct extern_list *extern_head;
+
+/* Assemble an external function reference.  We need to keep a list of
+   these, so that we can output the function types at the end of the
+   assembly.  We can't output the types now, because we might see a
+   definition of the function later on and emit debugging information
+   for it then.  */
+
+void
+i386_pe_record_external_function (name)
+     const char *name;
+{
+  struct extern_list *p;
+
+  p = (struct extern_list *) permalloc (sizeof *p);
+  p->next = extern_head;
+  p->name = name;
+  extern_head = p;
+}
+
+/* Keep a list of exported symbols.  */
+
+struct export_list
+{
+  struct export_list *next;
+  const char *name;
+  int is_data;         /* used to type tag exported symbols. */
+};
+
+static struct export_list *export_head;
+
+/* Assemble an export symbol entry.  We need to keep a list of
+   these, so that we can output the export list at the end of the
+   assembly.  We used to output these export symbols in each function,
+   but that causes problems with GNU ld when the sections are 
+   linkonce.  */
+
+void
+i386_pe_record_exported_symbol (name, is_data)
+     const char *name;
+     int is_data;
+{
+  struct export_list *p;
+
+  p = (struct export_list *) permalloc (sizeof *p);
+  p->next = export_head;
+  p->name = name;
+  p->is_data = is_data;
+  export_head = p;
+}
+
+/* This is called at the end of assembly.  For each external function
+   which has not been defined, we output a declaration now.  We also
+   output the .drectve section.  */
+
+void
+i386_pe_asm_file_end (file)
+     FILE *file;
+{
+  struct extern_list *p;
+
+  ix86_asm_file_end (file);
+
+  for (p = extern_head; p != NULL; p = p->next)
     {
-      xops[0] = pic_offset_table_rtx;
-      xops[1] = (rtx) gen_label_rtx ();
+      tree decl;
+
+      decl = get_identifier (p->name);
 
-      output_asm_insn (AS1 (call,%P1), xops);
-      ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (xops[1]));
-      output_asm_insn (AS1 (pop%L0,%0), xops);
-      output_asm_insn ("addl $_GLOBAL_OFFSET_TABLE_+[.-%P1],%0", xops);
+      /* Positively ensure only one declaration for any given symbol.  */
+      if (! TREE_ASM_WRITTEN (decl) && TREE_SYMBOL_REFERENCED (decl))
+       {
+         TREE_ASM_WRITTEN (decl) = 1;
+         i386_pe_declare_function_type (file, p->name, TREE_PUBLIC (decl));
+       }
+    }
+
+  if (export_head)
+    {
+      struct export_list *q;
+      drectve_section ();
+      for (q = export_head; q != NULL; q = q->next)
+       {
+         fprintf (file, "\t.ascii \" -export:%s%s\"\n",
+                  I386_PE_STRIP_ENCODING (q->name),
+                  (q->is_data) ? ",data" : "");
+       }
     }
 }
+