OSDN Git Service

* gcc-interface/trans.c (call_to_gnu): In the return-by-target-ptr case
[pf3gnuchains/gcc-fork.git] / gcc / ada / gcc-interface / trans.c
index 4b7946c..0f4cef0 100644 (file)
@@ -230,8 +230,9 @@ gigi (Node_Id gnat_root, int max_gnat_node, int number_name ATTRIBUTE_UNUSED,
       struct Elist_Header *elists_ptr, struct Elmt_Item *elmts_ptr,
       struct String_Entry *strings_ptr, Char_Code *string_chars_ptr,
       struct List_Header *list_headers_ptr, Nat number_file,
-      struct File_Info_Type *file_info_ptr, Entity_Id standard_boolean,
-      Entity_Id standard_integer, Entity_Id standard_long_long_float,
+      struct File_Info_Type *file_info_ptr,
+      Entity_Id standard_boolean, Entity_Id standard_integer,
+      Entity_Id standard_character, Entity_Id standard_long_long_float,
       Entity_Id standard_exception_type, Int gigi_operating_mode)
 {
   Entity_Id gnat_literal;
@@ -317,23 +318,26 @@ gigi (Node_Id gnat_root, int max_gnat_node, int number_name ATTRIBUTE_UNUSED,
   double_float_alignment = get_target_double_float_alignment ();
   double_scalar_alignment = get_target_double_scalar_alignment ();
 
-  /* Record the builtin types.  Define `integer' and `unsigned char' first so
-     that dbx will output them first.  */
+  /* Record the builtin types.  Define `integer' and `character' first so that
+     dbx will output them first.  */
   record_builtin_type ("integer", integer_type_node);
-  record_builtin_type ("unsigned char", char_type_node);
-  record_builtin_type ("long integer", long_integer_type_node);
-  unsigned_type_node = gnat_type_for_size (INT_TYPE_SIZE, 1);
-  record_builtin_type ("unsigned int", unsigned_type_node);
-  record_builtin_type (SIZE_TYPE, sizetype);
+  record_builtin_type ("character", unsigned_char_type_node);
   record_builtin_type ("boolean", boolean_type_node);
   record_builtin_type ("void", void_type_node);
 
   /* Save the type we made for integer as the type for Standard.Integer.  */
-  save_gnu_tree (Base_Type (standard_integer), TYPE_NAME (integer_type_node),
+  save_gnu_tree (Base_Type (standard_integer),
+                TYPE_NAME (integer_type_node),
                 false);
 
-  /* Save the type we made for boolean as the type for Standard.Boolean.  */
-  save_gnu_tree (Base_Type (standard_boolean), TYPE_NAME (boolean_type_node),
+  /* Likewise for character as the type for Standard.Character.  */
+  save_gnu_tree (Base_Type (standard_character),
+                TYPE_NAME (unsigned_char_type_node),
+                false);
+
+  /* Likewise for boolean as the type for Standard.Boolean.  */
+  save_gnu_tree (Base_Type (standard_boolean),
+                TYPE_NAME (boolean_type_node),
                 false);
   gnat_literal = First_Literal (Base_Type (standard_boolean));
   t = UI_To_gnu (Enumeration_Rep (gnat_literal), boolean_type_node);
@@ -397,6 +401,10 @@ gigi (Node_Id gnat_root, int max_gnat_node, int number_name ATTRIBUTE_UNUSED,
   /* Name of the _Parent field in tagged record types.  */
   parent_name_id = get_identifier (Get_Name_String (Name_uParent));
 
+  /* Name of the Exception_Data type defined in System.Standard_Library.  */
+  exception_data_name_id
+    = get_identifier ("system__standard_library__exception_data");
+
   /* Make the types and functions used for exception processing.  */
   jmpbuf_type
     = build_array_type (gnat_type_for_mode (Pmode, 0),
@@ -474,7 +482,8 @@ gigi (Node_Id gnat_root, int max_gnat_node, int number_name ATTRIBUTE_UNUSED,
          (get_identifier ("__gnat_last_chance_handler"), NULL_TREE,
           build_function_type (void_type_node,
                                tree_cons (NULL_TREE,
-                                          build_pointer_type (char_type_node),
+                                          build_pointer_type
+                                          (unsigned_char_type_node),
                                           tree_cons (NULL_TREE,
                                                      integer_type_node,
                                                      t))),
@@ -496,7 +505,7 @@ gigi (Node_Id gnat_root, int max_gnat_node, int number_name ATTRIBUTE_UNUSED,
             build_function_type (void_type_node,
                                  tree_cons (NULL_TREE,
                                             build_pointer_type
-                                            (char_type_node),
+                                            (unsigned_char_type_node),
                                             tree_cons (NULL_TREE,
                                                        integer_type_node,
                                                        t))),
@@ -512,9 +521,7 @@ gigi (Node_Id gnat_root, int max_gnat_node, int number_name ATTRIBUTE_UNUSED,
                                TYPE_QUAL_VOLATILE);
     }
 
-  /* Set the types that GCC and Gigi use from the front end.  We would
-     like to do this for char_type_node, but it needs to correspond to
-     the C char type.  */
+  /* Set the types that GCC and Gigi use from the front end.  */
   exception_type
     = gnat_to_gnu_entity (Base_Type (standard_exception_type),  NULL_TREE, 0);
   except_type_node = TREE_TYPE (exception_type);
@@ -556,8 +563,9 @@ gigi (Node_Id gnat_root, int max_gnat_node, int number_name ATTRIBUTE_UNUSED,
 
       for (j = 0; j < TARGET_VTABLE_USES_DESCRIPTORS; j++)
        {
-         tree field = create_field_decl (NULL_TREE, ptr_void_ftype,
-                                         fdesc_type_node, 0, 0, 0, 1);
+         tree field
+           = create_field_decl (NULL_TREE, ptr_void_ftype, fdesc_type_node,
+                                NULL_TREE, NULL_TREE, 0, 1);
          TREE_CHAIN (field) = field_list;
          field_list = field;
          null_list = tree_cons (field, null_node, null_list);
@@ -1354,7 +1362,8 @@ Attribute_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, int attribute)
        if (TREE_CODE (gnu_obj_type) == RECORD_TYPE
            && TYPE_CONTAINS_TEMPLATE_P (gnu_obj_type))
          {
-           tree gnu_char_ptr_type = build_pointer_type (char_type_node);
+           tree gnu_char_ptr_type
+             = build_pointer_type (unsigned_char_type_node);
            tree gnu_pos = byte_position (TYPE_FIELDS (gnu_obj_type));
            gnu_ptr = convert (gnu_char_ptr_type, gnu_ptr);
            gnu_ptr = build_binary_op (POINTER_PLUS_EXPR, gnu_char_ptr_type,
@@ -1442,7 +1451,8 @@ Attribute_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, int attribute)
                  gnu_type
                    = build_unc_object_type_from_ptr (gnu_ptr_type,
                                                      gnu_actual_obj_type,
-                                                     get_identifier ("SIZE"));
+                                                     get_identifier ("SIZE"),
+                                                     false);
                }
 
              gnu_result = TYPE_SIZE (gnu_type);
@@ -2613,7 +2623,7 @@ call_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, tree gnu_target)
   tree gnu_subprog_addr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_subprog);
   Entity_Id gnat_formal;
   Node_Id gnat_actual;
-  tree gnu_actual_list = NULL_TREE;
+  VEC(tree,gc) *gnu_actual_vec = NULL;
   tree gnu_name_list = NULL_TREE;
   tree gnu_before_list = NULL_TREE;
   tree gnu_after_list = NULL_TREE;
@@ -2636,8 +2646,28 @@ call_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, tree gnu_target)
 
       if (Nkind (gnat_node) == N_Function_Call && !gnu_target)
        {
-         *gnu_result_type_p = TREE_TYPE (gnu_subprog_type);
-         return build1 (NULL_EXPR, TREE_TYPE (gnu_subprog_type), call_expr);
+         tree gnu_obj_type;
+
+         if (CONTAINS_PLACEHOLDER_P (TYPE_SIZE (gnu_ret_type)))
+           gnu_obj_type
+             = maybe_pad_type (gnu_ret_type,
+                               max_size (TYPE_SIZE (gnu_ret_type), true),
+                               0, Etype (Name (gnat_node)), false, false,
+                               false, true);
+         else
+           gnu_obj_type = gnu_ret_type;
+
+         /* ??? We may be about to create a static temporary if we happen to
+            be at the global binding level.  That's a regression from what
+            the 3.x back-end would generate in the same situation, but we
+            don't have a mechanism in Gigi for creating automatic variables
+            in the elaboration routines.  */
+         gnu_target
+           = create_var_decl (create_tmp_var_name ("LR"), NULL, gnu_obj_type,
+                              NULL, false, false, false, false, NULL,
+                              gnat_node);
+
+         *gnu_result_type_p = gnu_ret_type;
        }
 
       return call_expr;
@@ -2963,17 +2993,39 @@ call_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, tree gnu_target)
            gnu_actual = convert (DECL_ARG_TYPE (gnu_formal), gnu_actual);
        }
 
-      gnu_actual_list = tree_cons (NULL_TREE, gnu_actual, gnu_actual_list);
+      VEC_safe_push (tree, gc, gnu_actual_vec, gnu_actual);
     }
 
-  gnu_call = build_call_list (TREE_TYPE (gnu_subprog_type), gnu_subprog_addr,
-                             nreverse (gnu_actual_list));
-  set_expr_location_from_node (gnu_call, gnat_node);
+  gnu_subprog_call = build_call_list (TREE_TYPE (gnu_subprog_type),
+                                     gnu_subprog_addr,
+                                     nreverse (gnu_actual_list));
+  set_expr_location_from_node (gnu_subprog_call, gnat_node);
 
-  /* If it's a function call, the result is the call expression unless a target
-     is specified, in which case we copy the result into the target and return
-     the assignment statement.  */
-  if (Nkind (gnat_node) == N_Function_Call)
+  /* If we return by passing a target, the result is the target after the
+     call.  We must not emit the call directly here because this might be
+     evaluated as part of an expression with conditions to control whether
+     the call should be emitted or not.  */
+  if (TYPE_RETURNS_BY_TARGET_PTR_P (gnu_subprog_type))
+    {
+      /* Conceptually, what we need is a COMPOUND_EXPR of the call followed by
+        the target object.  Doing so would potentially be inefficient though,
+        as this expression might be wrapped up into a SAVE_EXPR later, which
+        would incur a pointless temporary copy of the whole object.
+
+        What we do instead is build a COMPOUND_EXPR returning the address of
+        the target, and then dereference.  Wrapping up the COMPOUND_EXPR into
+        a SAVE_EXPR then only incurs a mere pointer copy.  */
+      tree gnu_target_addr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_target);
+      set_expr_location_from_node (gnu_target_addr, gnat_node);
+      gnu_result = build2 (COMPOUND_EXPR, TREE_TYPE (gnu_target_addr),
+                          gnu_subprog_call, gnu_target_addr);
+      return build_unary_op (INDIRECT_REF, NULL_TREE, gnu_result);
+    }
+
+  /* If it is a function call, the result is the call expression unless
+     a target is specified, in which case we copy the result into the target
+     and return the assignment statement.  */
+  else if (Nkind (gnat_node) == N_Function_Call)
     {
       tree gnu_result = gnu_call;
 
@@ -4793,10 +4845,12 @@ gnat_to_gnu (Node_Id gnat_node)
          gnu_result
            = build_binary_op (MODIFY_EXPR, NULL_TREE, gnu_lhs, gnu_rhs);
 
-         /* If the type being assigned is an array type and the two sides
-            are not completely disjoint, play safe and use memmove.  */
+         /* If the type being assigned is an array type and the two sides are
+            not completely disjoint, play safe and use memmove.  But don't do
+            it for a bit-packed array as it might not be byte-aligned.  */
          if (TREE_CODE (gnu_result) == MODIFY_EXPR
              && Is_Array_Type (Etype (Name (gnat_node)))
+             && !Is_Bit_Packed_Array (Etype (Name (gnat_node)))
              && !(Forwards_OK (gnat_node) && Backwards_OK (gnat_node)))
            {
              tree to, from, size, to_ptr, from_ptr, t;
@@ -5380,8 +5434,8 @@ gnat_to_gnu (Node_Id gnat_node)
                gnu_actual_obj_type
                  = build_unc_object_type_from_ptr (gnu_ptr_type,
                                                    gnu_actual_obj_type,
-                                                   get_identifier
-                                                   ("DEALLOC"));
+                                                   get_identifier ("DEALLOC"),
+                                                   false);
            }
          else
            gnu_actual_obj_type = gnu_obj_type;
@@ -5391,7 +5445,8 @@ gnat_to_gnu (Node_Id gnat_node)
          if (TREE_CODE (gnu_obj_type) == RECORD_TYPE
              && TYPE_CONTAINS_TEMPLATE_P (gnu_obj_type))
            {
-             tree gnu_char_ptr_type = build_pointer_type (char_type_node);
+             tree gnu_char_ptr_type
+               = build_pointer_type (unsigned_char_type_node);
              tree gnu_pos = byte_position (TYPE_FIELDS (gnu_obj_type));
              gnu_ptr = convert (gnu_char_ptr_type, gnu_ptr);
              gnu_ptr = build_binary_op (POINTER_PLUS_EXPR, gnu_char_ptr_type,
@@ -6029,16 +6084,8 @@ gnat_gimplify_expr (tree *expr_p, gimple_seq *pre_p,
             the reference is in an elaboration procedure.  */
          if (TREE_CONSTANT (op))
            {
-             tree new_var = create_tmp_var_raw (TREE_TYPE (op), "C");
-             TREE_ADDRESSABLE (new_var) = 1;
-             gimple_add_tmp_var (new_var);
-
-             TREE_READONLY (new_var) = 1;
-             TREE_STATIC (new_var) = 1;
-             DECL_INITIAL (new_var) = op;
-
-             TREE_OPERAND (expr, 0) = new_var;
-             recompute_tree_invariant_for_addr_expr (expr);
+             tree addr = build_fold_addr_expr (tree_output_constant_def (op));
+             *expr_p = fold_convert (TREE_TYPE (expr), addr);
            }
 
          /* Otherwise explicitly create the local temporary.  That's required
@@ -7462,6 +7509,263 @@ maybe_implicit_deref (tree exp)
   return exp;
 }
 \f
+/* Protect EXP from multiple evaluation.  This may make a SAVE_EXPR.  */
+
+tree
+protect_multiple_eval (tree exp)
+{
+  tree type = TREE_TYPE (exp);
+
+  /* If EXP has no side effects, we theoritically don't need to do anything.
+     However, we may be recursively passed more and more complex expressions
+     involving checks which will be reused multiple times and eventually be
+     unshared for gimplification; in order to avoid a complexity explosion
+     at that point, we protect any expressions more complex than a simple
+     arithmetic expression.  */
+  if (!TREE_SIDE_EFFECTS (exp)
+      && (CONSTANT_CLASS_P (exp)
+         || !EXPRESSION_CLASS_P (skip_simple_arithmetic (exp))))
+    return exp;
+
+  /* If this is a conversion, protect what's inside the conversion.
+     Similarly, if we're indirectly referencing something, we only
+     need to protect the address since the data itself can't change
+     in these situations.  */
+  if (TREE_CODE (exp) == NON_LVALUE_EXPR
+      || CONVERT_EXPR_P (exp)
+      || TREE_CODE (exp) == VIEW_CONVERT_EXPR
+      || TREE_CODE (exp) == INDIRECT_REF
+      || TREE_CODE (exp) == UNCONSTRAINED_ARRAY_REF)
+  return build1 (TREE_CODE (exp), type,
+                protect_multiple_eval (TREE_OPERAND (exp, 0)));
+
+  /* If this is a fat pointer or something that can be placed in a register,
+     just make a SAVE_EXPR.  Likewise for a CALL_EXPR as large objects are
+     returned via invisible reference in most ABIs so the temporary will
+     directly be filled by the callee.  */
+  if (TYPE_IS_FAT_POINTER_P (type)
+      || TYPE_MODE (type) != BLKmode
+      || TREE_CODE (exp) == CALL_EXPR)
+    return save_expr (exp);
+
+  /* Otherwise reference, protect the address and dereference.  */
+  return
+    build_unary_op (INDIRECT_REF, type,
+                   save_expr (build_unary_op (ADDR_EXPR,
+                                              build_reference_type (type),
+                                              exp)));
+}
+\f
+/* This is equivalent to stabilize_reference in tree.c, but we know how to
+   handle our own nodes and we take extra arguments.  FORCE says whether to
+   force evaluation of everything.  We set SUCCESS to true unless we walk
+   through something we don't know how to stabilize.  */
+
+tree
+maybe_stabilize_reference (tree ref, bool force, bool *success)
+{
+  tree type = TREE_TYPE (ref);
+  enum tree_code code = TREE_CODE (ref);
+  tree result;
+
+  /* Assume we'll success unless proven otherwise.  */
+  *success = true;
+
+  switch (code)
+    {
+    case CONST_DECL:
+    case VAR_DECL:
+    case PARM_DECL:
+    case RESULT_DECL:
+      /* No action is needed in this case.  */
+      return ref;
+
+    case ADDR_EXPR:
+    CASE_CONVERT:
+    case FLOAT_EXPR:
+    case FIX_TRUNC_EXPR:
+    case VIEW_CONVERT_EXPR:
+      result
+       = build1 (code, type,
+                 maybe_stabilize_reference (TREE_OPERAND (ref, 0), force,
+                                            success));
+      break;
+
+    case INDIRECT_REF:
+    case UNCONSTRAINED_ARRAY_REF:
+      result = build1 (code, type,
+                      gnat_stabilize_reference_1 (TREE_OPERAND (ref, 0),
+                                                  force));
+      break;
+
+    case COMPONENT_REF:
+     result = build3 (COMPONENT_REF, type,
+                     maybe_stabilize_reference (TREE_OPERAND (ref, 0), force,
+                                                success),
+                     TREE_OPERAND (ref, 1), NULL_TREE);
+      break;
+
+    case BIT_FIELD_REF:
+      result = build3 (BIT_FIELD_REF, type,
+                      maybe_stabilize_reference (TREE_OPERAND (ref, 0), force,
+                                                 success),
+                      gnat_stabilize_reference_1 (TREE_OPERAND (ref, 1),
+                                                  force),
+                      gnat_stabilize_reference_1 (TREE_OPERAND (ref, 2),
+                                                  force));
+      break;
+
+    case ARRAY_REF:
+    case ARRAY_RANGE_REF:
+      result = build4 (code, type,
+                      maybe_stabilize_reference (TREE_OPERAND (ref, 0), force,
+                                                 success),
+                      gnat_stabilize_reference_1 (TREE_OPERAND (ref, 1),
+                                                  force),
+                      NULL_TREE, NULL_TREE);
+      break;
+
+    case CALL_EXPR:
+    case COMPOUND_EXPR:
+      result = gnat_stabilize_reference_1 (ref, force);
+      break;
+
+    case CONSTRUCTOR:
+      /* Constructors with 1 element are used extensively to formally
+        convert objects to special wrapping types.  */
+      if (TREE_CODE (type) == RECORD_TYPE
+         && VEC_length (constructor_elt, CONSTRUCTOR_ELTS (ref)) == 1)
+       {
+         tree index
+           = VEC_index (constructor_elt, CONSTRUCTOR_ELTS (ref), 0)->index;
+         tree value
+           = VEC_index (constructor_elt, CONSTRUCTOR_ELTS (ref), 0)->value;
+         result
+           = build_constructor_single (type, index,
+                                       gnat_stabilize_reference_1 (value,
+                                                                   force));
+       }
+      else
+       {
+         *success = false;
+         return ref;
+       }
+      break;
+
+    case ERROR_MARK:
+      ref = error_mark_node;
+
+      /* ...  fall through to failure ... */
+
+      /* If arg isn't a kind of lvalue we recognize, make no change.
+        Caller should recognize the error for an invalid lvalue.  */
+    default:
+      *success = false;
+      return ref;
+    }
+
+  TREE_READONLY (result) = TREE_READONLY (ref);
+
+  /* TREE_THIS_VOLATILE and TREE_SIDE_EFFECTS attached to the initial
+     expression may not be sustained across some paths, such as the way via
+     build1 for INDIRECT_REF.  We re-populate those flags here for the general
+     case, which is consistent with the GCC version of this routine.
+
+     Special care should be taken regarding TREE_SIDE_EFFECTS, because some
+     paths introduce side effects where there was none initially (e.g. calls
+     to save_expr), and we also want to keep track of that.  */
+
+  TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (ref);
+  TREE_SIDE_EFFECTS (result) |= TREE_SIDE_EFFECTS (ref);
+
+  return result;
+}
+
+/* Wrapper around maybe_stabilize_reference, for common uses without
+   lvalue restrictions and without need to examine the success
+   indication.  */
+
+static tree
+gnat_stabilize_reference (tree ref, bool force)
+{
+  bool dummy;
+  return maybe_stabilize_reference (ref, force, &dummy);
+}
+
+/* Similar to stabilize_reference_1 in tree.c, but supports an extra
+   arg to force a SAVE_EXPR for everything.  */
+
+static tree
+gnat_stabilize_reference_1 (tree e, bool force)
+{
+  enum tree_code code = TREE_CODE (e);
+  tree type = TREE_TYPE (e);
+  tree result;
+
+  /* We cannot ignore const expressions because it might be a reference
+     to a const array but whose index contains side-effects.  But we can
+     ignore things that are actual constant or that already have been
+     handled by this function.  */
+
+  if (TREE_CONSTANT (e) || code == SAVE_EXPR)
+    return e;
+
+  switch (TREE_CODE_CLASS (code))
+    {
+    case tcc_exceptional:
+    case tcc_type:
+    case tcc_declaration:
+    case tcc_comparison:
+    case tcc_statement:
+    case tcc_expression:
+    case tcc_reference:
+    case tcc_vl_exp:
+      /* If this is a COMPONENT_REF of a fat pointer, save the entire
+        fat pointer.  This may be more efficient, but will also allow
+        us to more easily find the match for the PLACEHOLDER_EXPR.  */
+      if (code == COMPONENT_REF
+         && TYPE_IS_FAT_POINTER_P (TREE_TYPE (TREE_OPERAND (e, 0))))
+       result = build3 (COMPONENT_REF, type,
+                        gnat_stabilize_reference_1 (TREE_OPERAND (e, 0),
+                                                    force),
+                        TREE_OPERAND (e, 1), TREE_OPERAND (e, 2));
+      else if (TREE_SIDE_EFFECTS (e) || force)
+       return save_expr (e);
+      else
+       return e;
+      break;
+
+    case tcc_constant:
+      /* Constants need no processing.  In fact, we should never reach
+        here.  */
+      return e;
+
+    case tcc_binary:
+      /* Recursively stabilize each operand.  */
+      result = build2 (code, type,
+                      gnat_stabilize_reference_1 (TREE_OPERAND (e, 0), force),
+                      gnat_stabilize_reference_1 (TREE_OPERAND (e, 1),
+                                                  force));
+      break;
+
+    case tcc_unary:
+      /* Recursively stabilize each operand.  */
+      result = build1 (code, type,
+                      gnat_stabilize_reference_1 (TREE_OPERAND (e, 0),
+                                                  force));
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  TREE_READONLY (result) = TREE_READONLY (e);
+
+  TREE_THIS_VOLATILE (result) = TREE_THIS_VOLATILE (e);
+  TREE_SIDE_EFFECTS (result) |= TREE_SIDE_EFFECTS (e);
+  return result;
+}
+\f
 /* Convert SLOC into LOCUS.  Return true if SLOC corresponds to a source code
    location and false if it doesn't.  In the former case, set the Gigi global
    variable REF_FILENAME to the simple debug file name as given by sinput.  */