Add a compatibility function attribute (and flag) for nacl-gcc to
be ABI compatible with llvm/PNaCl.

Due to the fact that llvm bitcode loses information about the original source,
(unions, alignment) it is hard to match the ABI (see notes for known issues:
https://docs.google.com/a/google.com/document/d/1P3WrT_LHwN20gbs75LnHuMrbd804fSdSHKGK7Q3B20k/edit?hl=en_US#bookmark=id.sb20hms96tga).

Thus, for now, we make an attribute (and flag) to pass and return
structures and unions on the stack. Vectors and scalars can still use registers. This is calling convention is easy
to match. With this nacl-gcc is calling convention compatible with
pnacl. If at some point in the future we get richer type information
in bitcode we could do without this.

*****
Example uses of the attribute:

typedef struct {int x; float y; } s1;

typedef  void (__attribute__((pnaclcall)) *pnacl_fp)(int, s1, int);
typedef  s1 (__attribute__((pnaclcall)) *pnacl_ret_fp)(int, int) ;

extern void foo(int x, s1 y, int z);

__attribute__((pnaclcall)) void bar(int x, s1 y, int z)
{
  foo(x, y, z);
  if (x > 0)
    bar(x - 1, y, z);
}

void bar_fp_casted(void) {
  s1 s = { 80, 81.0f };
  void (__attribute__((pnaclcall)) *temp_fp)(int, s1, int) ;
  temp_fp = (pnacl_fp)&foo;
  (*temp_fp)(79, s, 82);
}

void bar_fp_casted2(void) {
  s1 s = { 80, 81.0f };
  void (*temp_fp)(int, s1, int) = &foo;
  (*(pnacl_fp)temp_fp)(79, s, 82);
}

*****

BUG= http://code.google.com/p/nativeclient/issues/detail?id=1819
BUG= http://code.google.com/p/nativeclient/issues/detail?id=2413
TEST= http://codereview.chromium.org/8502006/
and toolchain trybots

Review URL: http://codereview.chromium.org/8479017
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index f958b16..8f0111a 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -4260,6 +4260,12 @@
 
   if (TARGET_64BIT)
     {
+      /* Usually x86-64 does not have attributes so they warn here, but
+         we have added an attribute pnaclcall so allow that.  */
+      if (is_attribute_p ("pnaclcall", name))
+        /* Ignore the other i386 attributes and return.  */
+        return NULL_TREE;
+
       /* Do not warn when emulating the MS ABI.  */
       if (TREE_CODE (*node) != FUNCTION_TYPE || ix86_function_type_abi (*node)!=MS_ABI)
 	warning (OPT_Wattributes, "%qs attribute ignored",
@@ -4343,6 +4349,11 @@
       != !lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type2)))
     return 0;
 
+  /* Check for mismatched pnaclcall types.  */
+  if (!lookup_attribute ("pnaclcall", TYPE_ATTRIBUTES (type1))
+      != !lookup_attribute ("pnaclcall", TYPE_ATTRIBUTES (type2)))
+    return 0;
+
   /* Check for mismatched return types (cdecl vs stdcall).  */
   if (!lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type1))
       != !lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type2)))
@@ -4685,6 +4696,19 @@
 /* regclass.c  */
 extern void init_regs (void);
 
+/* Return true if the function is decorated with a pnaclcall attribute.  */
+static bool is_pnaclcall(const_tree fntype)
+{
+  return (fntype && lookup_attribute ("pnaclcall", TYPE_ATTRIBUTES (fntype)));
+}
+
+static bool ix86_cfun_pnaclcall (void)
+{
+  if (! cfun )
+    return 0;
+  return cfun->machine->pnaclcall;
+}
+
 /* Implementation of call abi switching target hook. Specific to FNDECL
    the specific call register sets are set. See also CONDITIONAL_REGISTER_USAGE
    for more details.  */
@@ -4692,9 +4716,15 @@
 ix86_call_abi_override (const_tree fndecl)
 {
   if (fndecl == NULL_TREE)
-    cfun->machine->call_abi = DEFAULT_ABI;
+    {
+      cfun->machine->call_abi = DEFAULT_ABI;
+      cfun->machine->pnaclcall = 0;
+    }
   else
-    cfun->machine->call_abi = ix86_function_type_abi (TREE_TYPE (fndecl));
+    {
+      cfun->machine->call_abi = ix86_function_type_abi (TREE_TYPE (fndecl));
+      cfun->machine->pnaclcall = is_pnaclcall (TREE_TYPE (fndecl));
+    }
 }
 
 /* MS and SYSV ABI have different set of call used registers.  Avoid expensive
@@ -4765,6 +4795,9 @@
 		      ? (!prototype_p (fntype) || stdarg_p (fntype))
 		      : !libname);
 
+  if (TARGET_64BIT)
+    cum->pnaclcall = is_pnaclcall (fntype);
+
   if (!TARGET_64BIT)
     {
       /* If there are variable arguments, then we won't pass anything
@@ -4946,7 +4979,8 @@
 
 static int
 classify_argument (enum machine_mode mode, const_tree type,
-		   enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset)
+		   enum x86_64_reg_class classes[MAX_CLASSES], int bit_offset,
+		   bool is_pnacl_cconv)
 {
   HOST_WIDE_INT bytes =
     (mode == BLKmode) ? int_size_in_bytes (type) : (int) GET_MODE_SIZE (mode);
@@ -4966,6 +5000,10 @@
       tree field;
       enum x86_64_reg_class subclasses[MAX_CLASSES];
 
+      /* Pass aggregates on the stack for PNaCl. */
+      if (TARGET_PNACL_CCONV || is_pnacl_cconv)
+	return 0;
+
       /* On x86-64 we pass structures larger than 32 bytes on the stack.  */
       if (bytes > 32)
 	return 0;
@@ -5035,7 +5073,8 @@
 		      num = classify_argument (TYPE_MODE (type), type,
 					       subclasses,
 					       (int_bit_position (field)
-						+ bit_offset) % 256);
+						+ bit_offset) % 256,
+						is_pnacl_cconv);
 		      if (!num)
 			return 0;
 		      for (i = 0; i < num; i++)
@@ -5055,7 +5094,8 @@
 	  {
 	    int num;
 	    num = classify_argument (TYPE_MODE (TREE_TYPE (type)),
-				     TREE_TYPE (type), subclasses, bit_offset);
+				     TREE_TYPE (type), subclasses, bit_offset,
+				     is_pnacl_cconv);
 	    if (!num)
 	      return 0;
 
@@ -5086,7 +5126,8 @@
 
 		  num = classify_argument (TYPE_MODE (TREE_TYPE (field)),
 					   TREE_TYPE (field), subclasses,
-					   bit_offset);
+					   bit_offset,
+					   is_pnacl_cconv);
 		  if (!num)
 		    return 0;
 		  for (i = 0; i < num; i++)
@@ -5327,10 +5368,10 @@
    class.  Return 0 iff parameter should be passed in memory.  */
 static int
 examine_argument (enum machine_mode mode, const_tree type, int in_return,
-		  int *int_nregs, int *sse_nregs)
+		     int *int_nregs, int *sse_nregs, bool is_pnacl_cconv)
 {
   enum x86_64_reg_class regclass[MAX_CLASSES];
-  int n = classify_argument (mode, type, regclass, 0);
+  int n = classify_argument (mode, type, regclass, 0, is_pnacl_cconv);
 
   *int_nregs = 0;
   *sse_nregs = 0;
@@ -5370,7 +5411,7 @@
 static rtx
 construct_container (enum machine_mode mode, enum machine_mode orig_mode,
 		     const_tree type, int in_return, int nintregs, int nsseregs,
-		     const int *intreg, int sse_regno)
+		     const int *intreg, int sse_regno, bool is_pnacl_cconv)
 {
   /* The following variables hold the static issued_error state.  */
   static bool issued_sse_arg_error;
@@ -5388,11 +5429,11 @@
   rtx exp[MAX_CLASSES];
   rtx ret;
 
-  n = classify_argument (mode, type, regclass, 0);
+  n = classify_argument (mode, type, regclass, 0, is_pnacl_cconv);
   if (!n)
     return NULL;
   if (!examine_argument (mode, type, in_return, &needed_intregs,
-			 &needed_sseregs))
+			 &needed_sseregs, is_pnacl_cconv))
     return NULL;
   if (needed_intregs > nintregs || needed_sseregs > nsseregs)
     return NULL;
@@ -5666,7 +5707,7 @@
   if (!named && VALID_AVX256_REG_MODE (mode))
     return;
 
-  if (!examine_argument (mode, type, 0, &int_nregs, &sse_nregs))
+  if (!examine_argument (mode, type, 0, &int_nregs, &sse_nregs, cum->pnaclcall))
     cum->words += words;
   else if (sse_nregs <= cum->sse_nregs && int_nregs <= cum->nregs)
     {
@@ -5882,7 +5923,7 @@
   return construct_container (mode, orig_mode, type, 0, cum->nregs,
 			      cum->sse_nregs,
 			      &x86_64_int_parameter_registers [cum->regno],
-			      cum->sse_regno);
+			      cum->sse_regno, cum->pnaclcall);
 }
 
 static rtx
@@ -6180,7 +6221,7 @@
 
 static rtx
 function_value_64 (enum machine_mode orig_mode, enum machine_mode mode,
-		   const_tree valtype)
+		   const_tree valtype, bool is_pnacl_cconv)
 {
   rtx ret;
 
@@ -6210,7 +6251,7 @@
 
   ret = construct_container (mode, orig_mode, valtype, 1,
 			     X86_64_REGPARM_MAX, X86_64_SSE_REGPARM_MAX,
-			     x86_64_int_return_registers, 0);
+			     x86_64_int_return_registers, 0, is_pnacl_cconv);
 
   /* For zero sized structures, construct_container returns NULL, but we
      need to keep rest of compiler happy by returning meaningful value.  */
@@ -6260,7 +6301,7 @@
   if (TARGET_64BIT && ix86_function_type_abi (fntype) == MS_ABI)
     return function_value_ms_64 (orig_mode, mode);
   else if (TARGET_64BIT)
-    return function_value_64 (orig_mode, mode, valtype);
+    return function_value_64 (orig_mode, mode, valtype, is_pnaclcall (fntype));
   else
     return function_value_32 (orig_mode, mode, fntype, fn);
 }
@@ -6330,10 +6371,12 @@
 }
 
 static int ATTRIBUTE_UNUSED
-return_in_memory_64 (const_tree type, enum machine_mode mode)
+return_in_memory_64 (const_tree type, enum machine_mode mode,
+		   bool is_pnacl_cconv)
 {
   int needed_intregs, needed_sseregs;
-  return !examine_argument (mode, type, 1, &needed_intregs, &needed_sseregs);
+  return !examine_argument (mode, type, 1, &needed_intregs, &needed_sseregs,
+		   is_pnacl_cconv);
 }
 
 static int ATTRIBUTE_UNUSED
@@ -6363,7 +6406,9 @@
       if (ix86_function_type_abi (fntype) == MS_ABI)
 	return return_in_memory_ms_64 (type, mode);
       else
-	return return_in_memory_64 (type, mode);
+	/* NOTE: fntype was meant to be unused, but we need to know
+	   if a call was a pnaclcall.  */
+	return return_in_memory_64 (type, mode, is_pnaclcall (fntype));
     }
   else
     return return_in_memory_32 (type, mode);
@@ -6382,7 +6427,7 @@
   enum machine_mode mode = type_natural_mode (type, NULL);
 
   if (TARGET_64BIT)
-    return return_in_memory_64 (type, mode);
+    return return_in_memory_64 (type, mode, is_pnaclcall (fntype));
 
   if (mode == BLKmode)
     return 1;
@@ -6927,7 +6972,7 @@
       container = construct_container (nat_mode, TYPE_MODE (type),
 				       type, 0, X86_64_REGPARM_MAX,
 				       X86_64_SSE_REGPARM_MAX, intreg,
-				       0);
+				       0, ix86_cfun_pnaclcall());
       break;
     }
 
@@ -6945,7 +6990,8 @@
       lab_false = create_artificial_label ();
       lab_over = create_artificial_label ();
 
-      examine_argument (nat_mode, type, 0, &needed_intregs, &needed_sseregs);
+      examine_argument (nat_mode, type, 0, &needed_intregs, &needed_sseregs,
+				       ix86_cfun_pnaclcall());
 
       need_temp = (!REG_P (container)
 		   && ((needed_intregs && TYPE_ALIGN (type) > 64)
@@ -30159,6 +30205,8 @@
   { "fastcall",  0, 0, false, true,  true,  ix86_handle_cconv_attribute },
   /* Cdecl attribute says the callee is a normal C declaration */
   { "cdecl",     0, 0, false, true,  true,  ix86_handle_cconv_attribute },
+  /* pnaclcall attribute says we are using the PNaCl x86_64 calling conv. */
+  { "pnaclcall", 0, 0, false, true, true, ix86_handle_cconv_attribute },
   /* Regparm attribute specifies how many integer arguments are to be
      passed in registers.  */
   { "regparm",   1, 1, false, true,  true,  ix86_handle_cconv_attribute },
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 23c0d18..debcc27 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -1572,6 +1572,7 @@
   int nregs;			/* # registers available for passing */
   int regno;			/* next available register number */
   int fastcall;			/* fastcall calling convention is used */
+  int pnaclcall;		/* pnaclcall calling convention is used */
   int sse_words;		/* # sse words passed so far */
   int sse_nregs;		/* # sse registers available for passing */
   int warn_avx;			/* True when we want to warn about AVX ABI.  */
@@ -2404,6 +2405,8 @@
   /* This value is used for amd64 targets and specifies the current abi
      to be used. MS_ABI means ms abi. Otherwise SYSV_ABI means sysv abi.  */
   int call_abi;
+  /* Indicates whether or not the function was declared as pnaclcall.  */
+  int pnaclcall;
 };
 
 #define ix86_stack_locals (cfun->machine->stack_locals)
diff --git a/gcc/config/i386/nacl.opt b/gcc/config/i386/nacl.opt
index 2b91522..e0622f9 100644
--- a/gcc/config/i386/nacl.opt
+++ b/gcc/config/i386/nacl.opt
@@ -22,3 +22,8 @@
 mtls-use-call
 Target Var(TARGET_TLS_USE_CALL) Init(0)
 Use a function call rather than direct segment register access for TLS
+
+mpnacl-cconv
+Target Var(TARGET_PNACL_CCONV) Init(0)
+Use the PNaCl calling convention on x86-64 and pass by-value structures
+and unions on the stack instead of registers.
diff --git a/gcc/function.c b/gcc/function.c
index d582fea..329e75a 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -1826,6 +1826,27 @@
  }
 };
 
+/* HACK: If |maybe_call| is a call expression, grab additional type attributes
+   from the expression (e.g., from a function pointer type) which may not
+   have been present in a function decl.  This must NOT affect the original
+   function type/decl, so make a copy if it really changed.  We may be able
+   to narrow this hack to just propagate the "pnaclcall" attribute.  */
+static tree copy_attributes_if_call (const_tree maybe_call, const_tree fntype) {
+  if (TREE_CODE (maybe_call) == CALL_EXPR)
+    {
+      tree new_attributes = merge_type_attributes(fntype,
+		  TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (maybe_call))));
+      if (!attribute_list_equal (TYPE_ATTRIBUTES (fntype), new_attributes))
+		{
+          tree new_fntype = copy_node (fntype);
+          TYPE_ATTRIBUTES (new_fntype) = new_attributes;
+          return new_fntype;
+	    }
+      return fntype;
+    }
+  return fntype;
+}
+
 
 /* Return 1 if EXP is an aggregate type (or a value with aggregate type).
    This means a type for which function calls must pass an address to the
@@ -1853,10 +1874,14 @@
 	fntype = (fndecl
 		  ? TREE_TYPE (fndecl)
 		  : TREE_TYPE (TREE_TYPE (CALL_EXPR_FN (fntype))));
+	/* HACK to grab attributes from the callexp type as well as the decl.  */
+	fntype = copy_attributes_if_call (exp, fntype);
 	break;
       case FUNCTION_DECL:
 	fndecl = fntype;
 	fntype = TREE_TYPE (fndecl);
+	/* HACK to grab attributes from the callexp type as well as the decl.  */
+	fntype = copy_attributes_if_call (exp, fntype);
 	break;
       case FUNCTION_TYPE:
       case METHOD_TYPE: