OSDN Git Service

* jcf-write.c (generate_bytecode_insns): Optimize binary operations
[pf3gnuchains/gcc-fork.git] / gcc / java / jcf-write.c
index 384cbee..aed6eb9 100644 (file)
@@ -40,16 +40,12 @@ The Free Software Foundation is independent of Sun Microsystems, Inc.  */
 #include "ggc.h"
 #include "tm_p.h"
 
-#ifndef DIR_SEPARATOR
-#define DIR_SEPARATOR '/'
-#endif
-
 extern struct obstack temporary_obstack;
 
 /* Base directory in which `.class' files should be written.
    NULL means to put the file into the same directory as the
    corresponding .java file.  */
-char *jcf_write_base_directory = NULL;
+const char *jcf_write_base_directory = NULL;
 
 /* Make sure bytecode.data is big enough for at least N more bytes. */
 
@@ -129,7 +125,7 @@ struct jcf_block
 
      If the label has been defined:
      Until perform_relocations is finished, this is the maximum possible
-     value of the bytecode offset at the begnning of this block.
+     value of the bytecode offset at the beginning of this block.
      After perform_relocations, it is the actual offset (pc). */
   int pc;
 
@@ -279,6 +275,9 @@ struct jcf_partial
 
   /* Information about the current switch statement. */
   struct jcf_switch_state *sw_state;
+
+  /* The count of jsr instructions that have been emmitted.  */
+  long num_jsrs;
 };
 
 static void generate_bytecode_insns (tree, int, struct jcf_partial *);
@@ -293,7 +292,7 @@ static void define_jcf_label (struct jcf_block *, struct jcf_partial *);
 static struct jcf_block * get_jcf_label_here (struct jcf_partial *);
 static void put_linenumber (int, struct jcf_partial *);
 static void localvar_alloc (tree, struct jcf_partial *);
-static void localvar_free (tree, struct jcf_partial *);
+static void maybe_free_localvar (tree, struct jcf_partial *, int);
 static int get_access_flags (tree);
 static void write_chunks (FILE *, struct chunk *);
 static int adjust_typed_op (tree, int);
@@ -341,6 +340,7 @@ static void emit_jsr (struct jcf_block *, struct jcf_partial *);
 static void call_cleanups (struct jcf_block *, struct jcf_partial *);
 static char *make_class_file_name (tree);
 static unsigned char *append_synthetic_attribute (struct jcf_partial *);
+static void append_deprecated_attribute (struct jcf_partial *);
 static void append_innerclasses_attribute (struct jcf_partial *, tree);
 static void append_innerclasses_attribute_entry (struct jcf_partial *, tree, tree);
 static void append_gcj_attribute (struct jcf_partial *, tree);
@@ -386,8 +386,7 @@ static struct chunk *
 alloc_chunk (struct chunk *last, unsigned char *data,
             int size, struct obstack *work)
 {
-  struct chunk *chunk = (struct chunk *)
-    obstack_alloc (work, sizeof(struct chunk));
+  struct chunk *chunk = obstack_alloc (work, sizeof(struct chunk));
 
   if (data == NULL && size > 0)
     data = obstack_alloc (work, size);
@@ -434,8 +433,8 @@ append_chunk_copy (unsigned char *data, int size, struct jcf_partial *state)
 static struct jcf_block *
 gen_jcf_label (struct jcf_partial *state)
 {
-  struct jcf_block *block = (struct jcf_block *)
-    obstack_alloc (state->chunk_obstack, sizeof (struct jcf_block));
+  struct jcf_block *block
+    obstack_alloc (state->chunk_obstack, sizeof (struct jcf_block));
   block->next =        NULL;
   block->linenumber = -1;
   block->pc = UNDEFINED_PC;
@@ -518,8 +517,8 @@ static struct jcf_handler *
 alloc_handler (struct jcf_block *start_label, struct jcf_block *end_label,
               struct jcf_partial *state)
 {
-  struct jcf_handler *handler = (struct jcf_handler *)
-    obstack_alloc (state->chunk_obstack, sizeof (struct jcf_handler));
+  struct jcf_handler *handler
+    obstack_alloc (state->chunk_obstack, sizeof (struct jcf_handler));
   handler->start_label = start_label;
   handler->end_label = end_label;
   handler->handler_label = get_jcf_label_here (state);
@@ -537,7 +536,7 @@ alloc_handler (struct jcf_block *start_label, struct jcf_block *end_label,
 /* The index of jvm local variable allocated for this DECL.
    This is assigned when generating .class files;
    contrast DECL_LOCAL_SLOT_NUMBER which is set when *reading* a .class file.
-   (We don't allocate DECL_LANG_SPECIFIC for locals from Java sourc code.) */
+   (We don't allocate DECL_LANG_SPECIFIC for locals from Java source code.) */
 
 #define DECL_LOCAL_INDEX(DECL) DECL_ALIGN(DECL)
 
@@ -576,8 +575,7 @@ localvar_alloc (tree decl, struct jcf_partial *state)
       ptr = (struct localvar_info**) state->localvars.data + index;
       state->localvars.ptr = (unsigned char *) (ptr + 1 + wide);
     }
-  info = (struct localvar_info *)
-    obstack_alloc (state->chunk_obstack, sizeof (struct localvar_info));
+  info = obstack_alloc (state->chunk_obstack, sizeof (struct localvar_info));
   ptr[0] = info;
   if (wide)
     ptr[1] = (struct localvar_info *)(~0);
@@ -600,7 +598,7 @@ localvar_alloc (tree decl, struct jcf_partial *state)
 }
 
 static void
-localvar_free (tree decl, struct jcf_partial *state)
+maybe_free_localvar (tree decl, struct jcf_partial *state, int really)
 {
   struct jcf_block *end_label = get_jcf_label_here (state);
   int index = DECL_LOCAL_INDEX (decl);
@@ -612,6 +610,8 @@ localvar_free (tree decl, struct jcf_partial *state)
 
   if (info->decl != decl)
     abort ();
+  if (! really)
+    return;
   ptr[0] = NULL;
   if (wide)
     {
@@ -1002,8 +1002,8 @@ static void
 emit_reloc (HOST_WIDE_INT value, int kind,
            struct jcf_block *target, struct jcf_partial *state)
 {
-  struct jcf_relocation *reloc = (struct jcf_relocation *)
-    obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation));
+  struct jcf_relocation *reloc
+    obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation));
   struct jcf_block *block = state->last_block;
   reloc->next = block->u.relocations;
   block->u.relocations = reloc;
@@ -1065,11 +1065,12 @@ emit_jsr (struct jcf_block *target, struct jcf_partial *state)
   OP1 (OPCODE_jsr);
   /* Value is 1 byte from reloc back to start of instruction.  */
   emit_reloc (RELOCATION_VALUE_1, OPCODE_jsr_w, target, state);
+  state->num_jsrs++;
 }
 
 /* Generate code to evaluate EXP.  If the result is true,
    branch to TRUE_LABEL; otherwise, branch to FALSE_LABEL.
-   TRUE_BRANCH_FIRST is a code geneation hint that the
+   TRUE_BRANCH_FIRST is a code generation hint that the
    TRUE_LABEL may follow right after this. (The idea is that we
    may be able to optimize away GOTO TRUE_LABEL; TRUE_LABEL:) */
 
@@ -1226,7 +1227,7 @@ generate_bytecode_conditional (tree exp,
              OP1 (OPCODE_lcmp);
              goto compare_1;
            }
-         /* FALLTHOUGH */
+         /* FALLTHROUGH */
        default:
          if (integer_zerop (exp1))
            {
@@ -1346,7 +1347,7 @@ generate_bytecode_return (tree exp, struct jcf_partial *state)
          emit_store (state->return_value_decl, state);
          call_cleanups (NULL, state);
          emit_load (state->return_value_decl, state);
-         /* If we call localvar_free (state->return_value_decl, state),
+         /* If we call maybe_free_localvar (state->return_value_decl, state, 1),
             then we risk the save decl erroneously re-used in the
             finalizer.  Instead, we keep the state->return_value_decl
             allocated through the rest of the method.  This is not
@@ -1383,6 +1384,7 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
        {
          tree local;
          tree body = BLOCK_EXPR_BODY (exp);
+         long jsrs = state->num_jsrs;
          for (local = BLOCK_EXPR_DECLS (exp); local; )
            {
              tree next = TREE_CHAIN (local);
@@ -1396,10 +1398,11 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
              body = TREE_OPERAND (body, 1);
            }
          generate_bytecode_insns (body, target, state);
+         
          for (local = BLOCK_EXPR_DECLS (exp); local; )
            {
              tree next = TREE_CHAIN (local);
-             localvar_free (local, state);
+             maybe_free_localvar (local, state, state->num_jsrs <= jsrs);
              local = next;
            }
        }
@@ -1416,17 +1419,17 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
       {
        const char *saved_input_filename = input_filename;
        tree body = EXPR_WFL_NODE (exp);
-       int saved_lineno = lineno;
+       int saved_lineno = input_line;
        if (body == empty_stmt_node)
          break;
        input_filename = EXPR_WFL_FILENAME (exp);
-       lineno = EXPR_WFL_LINENO (exp);
-       if (EXPR_WFL_EMIT_LINE_NOTE (exp) && lineno > 0
+       input_line = EXPR_WFL_LINENO (exp);
+       if (EXPR_WFL_EMIT_LINE_NOTE (exp) && input_line > 0
            && debug_info_level > DINFO_LEVEL_NONE)
-         put_linenumber (lineno, state);
+         put_linenumber (input_line, state);
        generate_bytecode_insns (body, target, state);
        input_filename = saved_input_filename;
-       lineno = saved_lineno;
+       input_line = saved_lineno;
       }
       break;
     case INTEGER_CST:
@@ -1459,8 +1462,9 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
          OP1 (prec == 1 ? OPCODE_fconst_0 : OPCODE_dconst_0);
        else if (real_onep (exp))
          OP1 (prec == 1 ? OPCODE_fconst_1 : OPCODE_dconst_1);
-       /* FIXME Should also use fconst_2 for 2.0f.
-          Also, should use iconst_2/ldc followed by i2f/i2d
+       else if (prec == 1 && real_twop (exp))
+         OP1 (OPCODE_fconst_2);
+       /* ??? We could also use iconst_3/ldc followed by i2f/i2d
           for other float/double when the value is a small integer. */
        else
          {
@@ -1577,8 +1581,8 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
     case CASE_EXPR:
       {
        struct jcf_switch_state *sw_state = state->sw_state;
-       struct jcf_relocation *reloc = (struct jcf_relocation *)
-         obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation));
+       struct jcf_relocation *reloc
+         obstack_alloc (state->chunk_obstack, sizeof (struct jcf_relocation));
        HOST_WIDE_INT case_value = TREE_INT_CST_LOW (TREE_OPERAND (exp, 0));
        reloc->kind = 0;
        reloc->label = get_jcf_label_here (state);
@@ -1658,8 +1662,8 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
            HOST_WIDE_INT i;
            unsigned HOST_WIDE_INT delta;
            /* Copy the chain of relocs into a sorted array. */
-           struct jcf_relocation **relocs = (struct jcf_relocation **)
-             xmalloc (sw_state.num_cases * sizeof (struct jcf_relocation *));
+           struct jcf_relocation **relocs
+             xmalloc (sw_state.num_cases * sizeof (struct jcf_relocation *));
            /* The relocs arrays is a buffer with a gap.
               The assumption is that cases will normally come in "runs". */
            int gap_start = 0;
@@ -2026,7 +2030,7 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
          emit_dup (TYPE_IS_WIDE (type) ? 2 : 1 , offset, state);
        exp = lhs;
       }
-      /* FALLTHOUGH */
+      /* FALLTHROUGH */
 
     finish_assignment:
       if (TREE_CODE (exp) == COMPONENT_REF)
@@ -2091,10 +2095,10 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
       tree arg0 = TREE_OPERAND (exp, 0);
       tree arg1 = TREE_OPERAND (exp, 1);
       jopcode += adjust_typed_op (type, 3);
-      if (arg0 == arg1 && TREE_CODE (arg0) == SAVE_EXPR)
+      if (arg0 != NULL_TREE && operand_equal_p (arg0, arg1, 0))
        {
          /* fold may (e.g) convert 2*x to x+x. */
-         generate_bytecode_insns (TREE_OPERAND (arg0, 0), target, state);
+         generate_bytecode_insns (arg0, target, state);
          emit_dup (TYPE_PRECISION (TREE_TYPE (arg0)) > 32 ? 2 : 1, 0, state);
        }
       else
@@ -2353,8 +2357,8 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
        if (CAN_COMPLETE_NORMALLY (finally))
          {
            maybe_wide (OPCODE_ret, DECL_LOCAL_INDEX (return_link), state);
-           localvar_free (exception_decl, state);
-           localvar_free (return_link, state);
+           maybe_free_localvar (exception_decl, state, 1);
+           maybe_free_localvar (return_link, state, 1);
            define_jcf_label (finished_label, state);
          }
       }
@@ -2410,6 +2414,23 @@ generate_bytecode_insns (tree exp, int target, struct jcf_partial *state)
     case JAVA_EXC_OBJ_EXPR:
       NOTE_PUSH (1);  /* Pushed by exception system. */
       break;
+    case MIN_EXPR:
+    case MAX_EXPR:
+      {
+       /* This copes with cases where fold() has created MIN or MAX
+          from a conditional expression.  */
+       enum tree_code code = TREE_CODE (exp) == MIN_EXPR ? LT_EXPR : GT_EXPR;
+       tree op0 = TREE_OPERAND (exp, 0);
+       tree op1 = TREE_OPERAND (exp, 1);
+       tree x;
+       if (TREE_SIDE_EFFECTS (op0) || TREE_SIDE_EFFECTS (op1))
+         abort ();
+       x = build (COND_EXPR, TREE_TYPE (exp), 
+                  build (code, boolean_type_node, op0, op1), 
+                  op0, op1);     
+       generate_bytecode_insns (x, target, state);
+       break;
+      }                                             
     case NEW_CLASS_EXPR:
       {
        tree class = TREE_TYPE (TREE_TYPE (exp));
@@ -2664,8 +2685,7 @@ perform_relocations (struct jcf_partial *state)
       unsigned char *old_ptr = old_buffer + old_size;
       if (new_size != old_size)
        {
-         chunk->data = (unsigned char *)
-           obstack_alloc (state->chunk_obstack, new_size);
+         chunk->data = obstack_alloc (state->chunk_obstack, new_size);
          chunk->size = new_size;
        }
       new_ptr = chunk->data + new_size;
@@ -2820,7 +2840,7 @@ generate_classfile (tree clas, struct jcf_partial *state)
   append_chunk (NULL, 0, state);
   cpool_chunk = state->chunk;
 
-  /* Next allocate the chunk containing acces_flags through fields_counr. */
+  /* Next allocate the chunk containing acces_flags through fields_count. */
   if (clas == object_type_node)
     i = 10;
   else
@@ -2871,8 +2891,11 @@ generate_classfile (tree clas, struct jcf_partial *state)
       if (have_value)
        attr_count++;
 
-      if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part) || FIELD_SYNTHETIC (part))
+      if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part)
+         || FIELD_SYNTHETIC (part))
        attr_count++;
+      if (FIELD_DEPRECATED (part))
+       attr_count++;
 
       PUT2 (attr_count);  /* attributes_count */
       if (have_value)
@@ -2894,6 +2917,8 @@ generate_classfile (tree clas, struct jcf_partial *state)
       if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part)
          || FIELD_SYNTHETIC (part))
        ptr = append_synthetic_attribute (state);
+      if (FIELD_DEPRECATED (part))
+       append_deprecated_attribute (state);
       fields_count++;
     }
   ptr = fields_count_ptr;  UNSAFE_PUT2 (fields_count);
@@ -2912,6 +2937,12 @@ generate_classfile (tree clas, struct jcf_partial *state)
       tree type = TREE_TYPE (part);
       tree save_function = current_function_decl;
       int synthetic_p = 0;
+
+      /* Invisible Miranda methods shouldn't end up in the .class
+        file.  */
+      if (METHOD_INVISIBLE (part))
+       continue;
+
       current_function_decl = part;
       ptr = append_chunk (NULL, 8, state);
       i = get_access_flags (part);  PUT2 (i);
@@ -2929,6 +2960,9 @@ generate_classfile (tree clas, struct jcf_partial *state)
          i++;
          synthetic_p = 1;
        }
+      /* Make room for Deprecated attribute.  */
+      if (METHOD_DEPRECATED (part))
+       i++;
 
       PUT2 (i);   /* attributes_count */
 
@@ -2951,6 +2985,7 @@ generate_classfile (tree clas, struct jcf_partial *state)
          get_jcf_label_here (state);  /* Force a first block. */
          for (t = DECL_ARGUMENTS (part);  t != NULL_TREE;  t = TREE_CHAIN (t))
            localvar_alloc (t, state);
+         state->num_jsrs = 0;
          generate_bytecode_insns (body, IGNORE_TARGET, state);
          if (CAN_COMPLETE_NORMALLY (body))
            {
@@ -2960,9 +2995,9 @@ generate_classfile (tree clas, struct jcf_partial *state)
              OP1 (OPCODE_return);
            }
          for (t = DECL_ARGUMENTS (part);  t != NULL_TREE;  t = TREE_CHAIN (t))
-           localvar_free (t, state);
+           maybe_free_localvar (t, state, 1);
          if (state->return_value_decl != NULL_TREE)
-           localvar_free (state->return_value_decl, state);
+           maybe_free_localvar (state->return_value_decl, state, 1);
          finish_jcf_block (state);
          perform_relocations (state);
 
@@ -3069,6 +3104,10 @@ generate_classfile (tree clas, struct jcf_partial *state)
              PUT2 (i);
            }
        }
+
+      if (METHOD_DEPRECATED (part))
+       append_deprecated_attribute (state);
       methods_count++;
       current_function_decl = save_function;
     }
@@ -3090,6 +3129,9 @@ generate_classfile (tree clas, struct jcf_partial *state)
     i++;
   if (clas == object_type_node)
     i++;
+  if (CLASS_DEPRECATED (TYPE_NAME (clas)))
+    i++;
+
   PUT2 (i);                    /* attributes_count */
 
   /* generate the SourceFile attribute. */
@@ -3105,6 +3147,8 @@ generate_classfile (tree clas, struct jcf_partial *state)
   PUT2 (i);
   append_gcj_attribute (state, clas);
   append_innerclasses_attribute (state, clas);
+  if (CLASS_DEPRECATED (TYPE_NAME (clas)))
+    append_deprecated_attribute (state);
 
   /* New finally generate the contents of the constant pool chunk. */
   i = count_constant_pool_bytes (&state->cpool);
@@ -3134,6 +3178,17 @@ append_synthetic_attribute (struct jcf_partial *state)
 }
 
 static void
+append_deprecated_attribute (struct jcf_partial *state)
+{
+  unsigned char *ptr = append_chunk (NULL, 6, state);
+  int i;
+  i = find_utf8_constant (&state->cpool, get_identifier ("Deprecated"));
+  PUT2 (i);            /* Attribute string index */
+  PUT4 (0);            /* Attribute length */
+}
+
+static void
 append_gcj_attribute (struct jcf_partial *state, tree class)
 {
   unsigned char *ptr;
@@ -3234,6 +3289,7 @@ make_class_file_name (tree clas)
   const char *dname, *cname, *slash;
   char *r;
   struct stat sb;
+  char sep;
 
   cname = IDENTIFIER_POINTER (identifier_subst (DECL_NAME (TYPE_NAME (clas)),
                                                "", '.', DIR_SEPARATOR,
@@ -3245,24 +3301,45 @@ make_class_file_name (tree clas)
       char *t;
       dname = DECL_SOURCE_FILE (TYPE_NAME (clas));
       slash = strrchr (dname, DIR_SEPARATOR);
+#ifdef DIR_SEPARATOR_2
       if (! slash)
-       {
-         dname = ".";
-         slash = dname + 1;
-       }
+        slash = strrchr (dname, DIR_SEPARATOR_2);
+#endif
+      if (! slash)
+        {
+          dname = ".";
+          slash = dname + 1;
+          sep = DIR_SEPARATOR;
+        }
+      else
+        sep = *slash;
+
       t = strrchr (cname, DIR_SEPARATOR);
       if (t)
        cname = t + 1;
     }
   else
     {
+      char *s;
+
       dname = jcf_write_base_directory;
+
+      s = strrchr (dname, DIR_SEPARATOR);
+#ifdef DIR_SEPARATOR_2
+      if (! s)
+        s = strrchr (dname, DIR_SEPARATOR_2);
+#endif
+      if (s)
+        sep = *s;
+      else
+        sep = DIR_SEPARATOR;
+
       slash = dname + strlen (dname);
     }
 
   r = xmalloc (slash - dname + strlen (cname) + 2);
   strncpy (r, dname, slash - dname);
-  r[slash - dname] = DIR_SEPARATOR;
+  r[slash - dname] = sep;
   strcpy (&r[slash - dname + 1], cname);
 
   /* We try to make new directories when we need them.  We only do
@@ -3274,25 +3351,25 @@ make_class_file_name (tree clas)
   dname = r + (slash - dname) + 1;
   while (1)
     {
-      char *s = strchr (dname, DIR_SEPARATOR);
+      char *s = strchr (dname, sep);
       if (s == NULL)
        break;
       *s = '\0';
       if (stat (r, &sb) == -1
          /* Try to make it.  */
          && mkdir (r, 0755) == -1)
-       fatal_io_error ("can't create directory %s", r);
+       fatal_error ("can't create directory %s: %m", r);
 
-      *s = DIR_SEPARATOR;
+      *s = sep;
       /* Skip consecutive separators.  */
-      for (dname = s + 1; *dname && *dname == DIR_SEPARATOR; ++dname)
+      for (dname = s + 1; *dname && *dname == sep; ++dname)
        ;
     }
 
   return r;
 }
 
-/* Write out the contens of a class (RECORD_TYPE) CLAS, as a .class file.
+/* Write out the contents of a class (RECORD_TYPE) CLAS, as a .class file.
    The output .class file name is make_class_file_name(CLAS). */
 
 void
@@ -3314,14 +3391,14 @@ write_classfile (tree clas)
       temporary_file_name = concat (class_file_name, ".tmp", NULL);
       stream = fopen (temporary_file_name, "wb");
       if (stream == NULL)
-       fatal_io_error ("can't open %s for writing", temporary_file_name);
+       fatal_error ("can't open %s for writing: %m", temporary_file_name);
 
       jcf_dependency_add_target (class_file_name);
       init_jcf_state (state, work);
       chunks = generate_classfile (clas, state);
       write_chunks (stream, chunks);
       if (fclose (stream))
-       fatal_io_error ("error closing %s", temporary_file_name);
+       fatal_error ("error closing %s: %m", temporary_file_name);
 
       /* If a file named by the string pointed to by `new' exists
          prior to the call to the `rename' function, the bahaviour
@@ -3334,7 +3411,7 @@ write_classfile (tree clas)
       if (rename (temporary_file_name, class_file_name) == -1)
        {
          remove (temporary_file_name);
-         fatal_io_error ("can't create %s", class_file_name);
+         fatal_error ("can't create %s: %m", class_file_name);
        }
       free (temporary_file_name);
       free (class_file_name);