X-Git-Url: https://oss.titaniummirror.com/gitweb?a=blobdiff_plain;f=gcc%2Fjava%2Fjcf-write.c;fp=gcc%2Fjava%2Fjcf-write.c;h=0000000000000000000000000000000000000000;hb=6fed43773c9b0ce596dca5686f37ac3fc0fa11c0;hp=491b561ed050c1b6370587c3e5acffd10634e0e5;hpb=27b11d56b743098deb193d510b337ba22dc52e5c;p=msp430-gcc.git diff --git a/gcc/java/jcf-write.c b/gcc/java/jcf-write.c deleted file mode 100644 index 491b561e..00000000 --- a/gcc/java/jcf-write.c +++ /dev/null @@ -1,3408 +0,0 @@ -/* Write out a Java(TM) class file. - Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. - -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. - -Java and all Java-based marks are trademarks or registered trademarks -of Sun Microsystems, Inc. in the United States and other countries. -The Free Software Foundation is independent of Sun Microsystems, Inc. */ - -#include "config.h" -#include "system.h" -#include "jcf.h" -#include "tree.h" -#include "real.h" -#include "java-tree.h" -#include "obstack.h" -#undef AND -#include "rtl.h" -#include "flags.h" -#include "java-opcodes.h" -#include "parse.h" /* for BLOCK_EXPR_BODY */ -#include "buffer.h" -#include "toplev.h" -#include "ggc.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; - -/* Make sure bytecode.data is big enough for at least N more bytes. */ - -#define RESERVE(N) \ - do { CHECK_OP(state); \ - if (state->bytecode.ptr + (N) > state->bytecode.limit) \ - buffer_grow (&state->bytecode, N); } while (0) - -/* Add a 1-byte instruction/operand I to bytecode.data, - assuming space has already been RESERVE'd. */ - -#define OP1(I) (*state->bytecode.ptr++ = (I), CHECK_OP(state)) - -/* Like OP1, but I is a 2-byte big endian integer. */ - -#define OP2(I) \ - do { int _i = (I); OP1 (_i >> 8); OP1 (_i); CHECK_OP(state); } while (0) - -/* Like OP1, but I is a 4-byte big endian integer. */ - -#define OP4(I) \ - do { int _i = (I); OP1 (_i >> 24); OP1 (_i >> 16); \ - OP1 (_i >> 8); OP1 (_i); CHECK_OP(state); } while (0) - -/* Macro to call each time we push I words on the JVM stack. */ - -#define NOTE_PUSH(I) \ - do { state->code_SP += (I); \ - if (state->code_SP > state->code_SP_max) \ - state->code_SP_max = state->code_SP; } while (0) - -/* Macro to call each time we pop I words from the JVM stack. */ - -#define NOTE_POP(I) \ - do { state->code_SP -= (I); if (state->code_SP < 0) abort(); } while (0) - -/* A chunk or segment of a .class file. */ - -struct chunk -{ - /* The next segment of this .class file. */ - struct chunk *next; - - /* The actual data in this segment to be written to the .class file. */ - unsigned char *data; - - /* The size of the segment to be written to the .class file. */ - int size; -}; - -#define PENDING_CLEANUP_PC (-3) -#define PENDING_EXIT_PC (-2) -#define UNDEFINED_PC (-1) - -/* Each "block" represents a label plus the bytecode instructions following. - There may be branches out of the block, but no incoming jumps, except - to the beginning of the block. - - If (pc < 0), the jcf_block is not an actual block (i.e. it has no - associated code yet), but it is an undefined label. -*/ - -struct jcf_block -{ - /* For blocks that that are defined, the next block (in pc order). - For blocks that are not-yet-defined the end label of a LABELED_BLOCK_EXPR - or a cleanup expression (from a TRY_FINALLY_EXPR), - this is the next (outer) such end label, in a stack headed by - labeled_blocks in jcf_partial. */ - struct jcf_block *next; - - /* In the not-yet-defined end label for an unfinished EXIT_BLOCK_EXPR. - pc is PENDING_EXIT_PC. - In the not-yet-defined end label for pending cleanup subroutine, - pc is PENDING_CLEANUP_PC. - For other not-yet-defined labels, pc is UNDEFINED_PC. - - 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. - After perform_relocations, it is the actual offset (pc). */ - int pc; - - int linenumber; - - /* After finish_jcf_block is called, the actual instructions - contained in this block. Before that NULL, and the instructions - are in state->bytecode. */ - union { - struct chunk *chunk; - - /* If pc==PENDING_CLEANUP_PC, start_label is the start of the region - covered by the cleanup. */ - struct jcf_block *start_label; - } v; - - union { - /* Set of relocations (in reverse offset order) for this block. */ - struct jcf_relocation *relocations; - - /* If this block is that of the not-yet-defined end label of - a LABELED_BLOCK_EXPR, where LABELED_BLOCK is that LABELED_BLOCK_EXPR. - If pc==PENDING_CLEANUP_PC, the cleanup that needs to be run. */ - tree labeled_block; - } u; -}; - -/* A "relocation" type for the 0-3 bytes of padding at the start - of a tableswitch or a lookupswitch. */ -#define SWITCH_ALIGN_RELOC 4 - -/* A relocation type for the labels in a tableswitch or a lookupswitch; - these are relative to the start of the instruction, but (due to - th 0-3 bytes of padding), we don't know the offset before relocation. */ -#define BLOCK_START_RELOC 1 - -struct jcf_relocation -{ - /* Next relocation for the current jcf_block. */ - struct jcf_relocation *next; - - /* The (byte) offset within the current block that needs to be relocated. */ - HOST_WIDE_INT offset; - - /* 0 if offset is a 4-byte relative offset. - 4 (SWITCH_ALIGN_RELOC) if offset points to 0-3 padding bytes inserted - for proper alignment in tableswitch/lookupswitch instructions. - 1 (BLOCK_START_RELOC) if offset points to a 4-byte offset relative - to the start of the containing block. - -1 if offset is a 2-byte relative offset. - < -1 if offset is the address of an instruction with a 2-byte offset - that does not have a corresponding 4-byte offset version, in which - case the absolute value of kind is the inverted opcode. - > 4 if offset is the address of an instruction (such as jsr) with a - 2-byte offset that does have a corresponding 4-byte offset version, - in which case kind is the opcode of the 4-byte version (such as jsr_w). */ - int kind; - - /* The label the relocation wants to actually transfer to. */ - struct jcf_block *label; -}; - -#define RELOCATION_VALUE_0 ((HOST_WIDE_INT)0) -#define RELOCATION_VALUE_1 ((HOST_WIDE_INT)1) - -/* State for single catch clause. */ - -struct jcf_handler -{ - struct jcf_handler *next; - - struct jcf_block *start_label; - struct jcf_block *end_label; - struct jcf_block *handler_label; - - /* The sub-class of Throwable handled, or NULL_TREE (for finally). */ - tree type; -}; - -/* State for the current switch statement. */ - -struct jcf_switch_state -{ - struct jcf_switch_state *prev; - struct jcf_block *default_label; - - struct jcf_relocation *cases; - int num_cases; - HOST_WIDE_INT min_case, max_case; -}; - -/* This structure is used to contain the various pieces that will - become a .class file. */ - -struct jcf_partial -{ - struct chunk *first; - struct chunk *chunk; - struct obstack *chunk_obstack; - tree current_method; - - /* List of basic blocks for the current method. */ - struct jcf_block *blocks; - struct jcf_block *last_block; - - struct localvar_info *first_lvar; - struct localvar_info *last_lvar; - int lvar_count; - - CPool cpool; - - int linenumber_count; - - /* Until perform_relocations, this is a upper bound on the number - of bytes (so far) in the instructions for the current method. */ - int code_length; - - /* Stack of undefined ending labels for LABELED_BLOCK_EXPR. */ - struct jcf_block *labeled_blocks; - - /* The current stack size (stack pointer) in the current method. */ - int code_SP; - - /* The largest extent of stack size (stack pointer) in the current method. */ - int code_SP_max; - - /* Contains a mapping from local var slot number to localvar_info. */ - struct buffer localvars; - - /* The buffer allocated for bytecode for the current jcf_block. */ - struct buffer bytecode; - - /* Chain of exception handlers for the current method. */ - struct jcf_handler *handlers; - - /* Last element in handlers chain. */ - struct jcf_handler *last_handler; - - /* Number of exception handlers for the current method. */ - int num_handlers; - - /* Number of finalizers we are currently nested within. */ - int num_finalizers; - - /* If non-NULL, use this for the return value. */ - tree return_value_decl; - - /* Information about the current switch statement. */ - struct jcf_switch_state *sw_state; -}; - -static void generate_bytecode_insns PARAMS ((tree, int, struct jcf_partial *)); -static struct chunk * alloc_chunk PARAMS ((struct chunk *, unsigned char *, - int, struct obstack *)); -static unsigned char * append_chunk PARAMS ((unsigned char *, int, - struct jcf_partial *)); -static void append_chunk_copy PARAMS ((unsigned char *, int, - struct jcf_partial *)); -static struct jcf_block * gen_jcf_label PARAMS ((struct jcf_partial *)); -static void finish_jcf_block PARAMS ((struct jcf_partial *)); -static void define_jcf_label PARAMS ((struct jcf_block *, - struct jcf_partial *)); -static struct jcf_block * get_jcf_label_here PARAMS ((struct jcf_partial *)); -static void put_linenumber PARAMS ((int, struct jcf_partial *)); -static void localvar_alloc PARAMS ((tree, struct jcf_partial *)); -static void localvar_free PARAMS ((tree, struct jcf_partial *)); -static int get_access_flags PARAMS ((tree)); -static void write_chunks PARAMS ((FILE *, struct chunk *)); -static int adjust_typed_op PARAMS ((tree, int)); -static void generate_bytecode_conditional PARAMS ((tree, struct jcf_block *, - struct jcf_block *, int, - struct jcf_partial *)); -static void generate_bytecode_return PARAMS ((tree, struct jcf_partial *)); -static void perform_relocations PARAMS ((struct jcf_partial *)); -static void init_jcf_state PARAMS ((struct jcf_partial *, struct obstack *)); -static void init_jcf_method PARAMS ((struct jcf_partial *, tree)); -static void release_jcf_state PARAMS ((struct jcf_partial *)); -static struct chunk * generate_classfile PARAMS ((tree, struct jcf_partial *)); -static struct jcf_handler *alloc_handler PARAMS ((struct jcf_block *, - struct jcf_block *, - struct jcf_partial *)); -static void emit_iinc PARAMS ((tree, HOST_WIDE_INT, struct jcf_partial *)); -static void emit_reloc PARAMS ((HOST_WIDE_INT, int, struct jcf_block *, - struct jcf_partial *)); -static void push_constant1 PARAMS ((HOST_WIDE_INT, struct jcf_partial *)); -static void push_constant2 PARAMS ((HOST_WIDE_INT, struct jcf_partial *)); -static void push_int_const PARAMS ((HOST_WIDE_INT, struct jcf_partial *)); -static int find_constant_wide PARAMS ((HOST_WIDE_INT, HOST_WIDE_INT, - struct jcf_partial *)); -static void push_long_const PARAMS ((HOST_WIDE_INT, HOST_WIDE_INT, - struct jcf_partial *)); -static int find_constant_index PARAMS ((tree, struct jcf_partial *)); -static void push_long_const PARAMS ((HOST_WIDE_INT, HOST_WIDE_INT, - struct jcf_partial *)); -static void field_op PARAMS ((tree, int, struct jcf_partial *)); -static void maybe_wide PARAMS ((int, int, struct jcf_partial *)); -static void emit_dup PARAMS ((int, int, struct jcf_partial *)); -static void emit_pop PARAMS ((int, struct jcf_partial *)); -static void emit_load_or_store PARAMS ((tree, int, struct jcf_partial *)); -static void emit_load PARAMS ((tree, struct jcf_partial *)); -static void emit_store PARAMS ((tree, struct jcf_partial *)); -static void emit_unop PARAMS ((enum java_opcode, tree, struct jcf_partial *)); -static void emit_binop PARAMS ((enum java_opcode, tree, struct jcf_partial *)); -static void emit_reloc PARAMS ((HOST_WIDE_INT, int, struct jcf_block *, - struct jcf_partial *)); -static void emit_switch_reloc PARAMS ((struct jcf_block *, - struct jcf_partial *)); -static void emit_case_reloc PARAMS ((struct jcf_relocation *, - struct jcf_partial *)); -static void emit_if PARAMS ((struct jcf_block *, int, int, - struct jcf_partial *)); -static void emit_goto PARAMS ((struct jcf_block *, struct jcf_partial *)); -static void emit_jsr PARAMS ((struct jcf_block *, struct jcf_partial *)); -static void call_cleanups PARAMS ((struct jcf_block *, struct jcf_partial *)); -static char *make_class_file_name PARAMS ((tree)); -static unsigned char *append_synthetic_attribute PARAMS ((struct jcf_partial *)); -static void append_innerclasses_attribute PARAMS ((struct jcf_partial *, tree)); -static void append_innerclasses_attribute_entry PARAMS ((struct jcf_partial *, tree, tree)); -static void append_gcj_attribute PARAMS ((struct jcf_partial *, tree)); - -/* Utility macros for appending (big-endian) data to a buffer. - We assume a local variable 'ptr' points into where we want to - write next, and we assume enough space has been allocated. */ - -#ifdef ENABLE_JC1_CHECKING -static int CHECK_PUT PARAMS ((void *, struct jcf_partial *, int)); - -static int -CHECK_PUT (ptr, state, i) - void *ptr; - struct jcf_partial *state; - int i; -{ - if ((unsigned char *) ptr < state->chunk->data - || (unsigned char *) ptr + i > state->chunk->data + state->chunk->size) - abort (); - - return 0; -} -#else -#define CHECK_PUT(PTR, STATE, I) ((void)0) -#endif - -#define PUT1(X) (CHECK_PUT(ptr, state, 1), *ptr++ = (X)) -#define PUT2(X) (PUT1((X) >> 8), PUT1((X) & 0xFF)) -#define PUT4(X) (PUT2((X) >> 16), PUT2((X) & 0xFFFF)) -#define PUTN(P, N) (CHECK_PUT(ptr, state, N), memcpy(ptr, P, N), ptr += (N)) - -/* There are some cases below where CHECK_PUT is guaranteed to fail. - Use the following macros in those specific cases. */ -#define UNSAFE_PUT1(X) (*ptr++ = (X)) -#define UNSAFE_PUT2(X) (UNSAFE_PUT1((X) >> 8), UNSAFE_PUT1((X) & 0xFF)) -#define UNSAFE_PUT4(X) (UNSAFE_PUT2((X) >> 16), UNSAFE_PUT2((X) & 0xFFFF)) -#define UNSAFE_PUTN(P, N) (memcpy(ptr, P, N), ptr += (N)) - - -/* Allocate a new chunk on obstack WORK, and link it in after LAST. - Set the data and size fields to DATA and SIZE, respectively. - However, if DATA is NULL and SIZE>0, allocate a buffer as well. */ - -static struct chunk * -alloc_chunk (last, data, size, work) - struct chunk *last; - unsigned char *data; - int size; - struct obstack *work; -{ - struct chunk *chunk = (struct chunk *) - obstack_alloc (work, sizeof(struct chunk)); - - if (data == NULL && size > 0) - data = obstack_alloc (work, size); - - chunk->next = NULL; - chunk->data = data; - chunk->size = size; - if (last != NULL) - last->next = chunk; - return chunk; -} - -#ifdef ENABLE_JC1_CHECKING -static int CHECK_OP PARAMS ((struct jcf_partial *)); - -static int -CHECK_OP (state) - struct jcf_partial *state; -{ - if (state->bytecode.ptr > state->bytecode.limit) - abort (); - - return 0; -} -#else -#define CHECK_OP(STATE) ((void) 0) -#endif - -static unsigned char * -append_chunk (data, size, state) - unsigned char *data; - int size; - struct jcf_partial *state; -{ - state->chunk = alloc_chunk (state->chunk, data, size, state->chunk_obstack); - if (state->first == NULL) - state->first = state->chunk; - return state->chunk->data; -} - -static void -append_chunk_copy (data, size, state) - unsigned char *data; - int size; - struct jcf_partial *state; -{ - unsigned char *ptr = append_chunk (NULL, size, state); - memcpy (ptr, data, size); -} - -static struct jcf_block * -gen_jcf_label (state) - struct jcf_partial *state; -{ - struct jcf_block *block = (struct jcf_block *) - obstack_alloc (state->chunk_obstack, sizeof (struct jcf_block)); - block->next = NULL; - block->linenumber = -1; - block->pc = UNDEFINED_PC; - return block; -} - -static void -finish_jcf_block (state) - struct jcf_partial *state; -{ - struct jcf_block *block = state->last_block; - struct jcf_relocation *reloc; - int code_length = BUFFER_LENGTH (&state->bytecode); - int pc = state->code_length; - append_chunk_copy (state->bytecode.data, code_length, state); - BUFFER_RESET (&state->bytecode); - block->v.chunk = state->chunk; - - /* Calculate code_length to the maximum value it can have. */ - pc += block->v.chunk->size; - for (reloc = block->u.relocations; reloc != NULL; reloc = reloc->next) - { - int kind = reloc->kind; - if (kind == SWITCH_ALIGN_RELOC) - pc += 3; - else if (kind > BLOCK_START_RELOC) - pc += 2; /* 2-byte offset may grow to 4-byte offset */ - else if (kind < -1) - pc += 5; /* May need to add a goto_w. */ - } - state->code_length = pc; -} - -static void -define_jcf_label (label, state) - struct jcf_block *label; - struct jcf_partial *state; -{ - if (state->last_block != NULL) - finish_jcf_block (state); - label->pc = state->code_length; - if (state->blocks == NULL) - state->blocks = label; - else - state->last_block->next = label; - state->last_block = label; - label->next = NULL; - label->u.relocations = NULL; -} - -static struct jcf_block * -get_jcf_label_here (state) - struct jcf_partial *state; -{ - if (state->last_block != NULL && BUFFER_LENGTH (&state->bytecode) == 0) - return state->last_block; - else - { - struct jcf_block *label = gen_jcf_label (state); - define_jcf_label (label, state); - return label; - } -} - -/* Note a line number entry for the current PC and given LINE. */ - -static void -put_linenumber (line, state) - int line; - struct jcf_partial *state; -{ - struct jcf_block *label = get_jcf_label_here (state); - if (label->linenumber > 0) - { - label = gen_jcf_label (state); - define_jcf_label (label, state); - } - label->linenumber = line; - state->linenumber_count++; -} - -/* Allocate a new jcf_handler, for a catch clause that catches exceptions - in the range (START_LABEL, END_LABEL). */ - -static struct jcf_handler * -alloc_handler (start_label, end_label, state) - 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)); - handler->start_label = start_label; - handler->end_label = end_label; - handler->handler_label = get_jcf_label_here (state); - if (state->handlers == NULL) - state->handlers = handler; - else - state->last_handler->next = handler; - state->last_handler = handler; - handler->next = NULL; - state->num_handlers++; - return handler; -} - - -/* 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.) */ - -#define DECL_LOCAL_INDEX(DECL) DECL_ALIGN(DECL) - -struct localvar_info -{ - struct localvar_info *next; - - tree decl; - struct jcf_block *start_label; - struct jcf_block *end_label; -}; - -#define localvar_buffer ((struct localvar_info**) state->localvars.data) -#define localvar_max \ - ((struct localvar_info**) state->localvars.ptr - localvar_buffer) - -static void -localvar_alloc (decl, state) - tree decl; - struct jcf_partial *state; -{ - struct jcf_block *start_label = get_jcf_label_here (state); - int wide = TYPE_IS_WIDE (TREE_TYPE (decl)); - int index; - register struct localvar_info *info; - register struct localvar_info **ptr = localvar_buffer; - register struct localvar_info **limit - = (struct localvar_info**) state->localvars.ptr; - for (index = 0; ptr < limit; index++, ptr++) - { - if (ptr[0] == NULL - && (! wide || ((ptr+1) < limit && ptr[1] == NULL))) - break; - } - if (ptr == limit) - { - buffer_grow (&state->localvars, 2 * sizeof (struct localvar_info*)); - 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)); - ptr[0] = info; - if (wide) - ptr[1] = (struct localvar_info *)(~0); - DECL_LOCAL_INDEX (decl) = index; - info->decl = decl; - info->start_label = start_label; - - if (debug_info_level > DINFO_LEVEL_TERSE - && DECL_NAME (decl) != NULL_TREE) - { - /* Generate debugging info. */ - info->next = NULL; - if (state->last_lvar != NULL) - state->last_lvar->next = info; - else - state->first_lvar = info; - state->last_lvar = info; - state->lvar_count++; - } -} - -static void -localvar_free (decl, state) - tree decl; - struct jcf_partial *state; -{ - struct jcf_block *end_label = get_jcf_label_here (state); - int index = DECL_LOCAL_INDEX (decl); - register struct localvar_info **ptr = &localvar_buffer [index]; - register struct localvar_info *info = *ptr; - int wide = TYPE_IS_WIDE (TREE_TYPE (decl)); - - info->end_label = end_label; - - if (info->decl != decl) - abort (); - ptr[0] = NULL; - if (wide) - { - if (ptr[1] != (struct localvar_info *)(~0)) - abort (); - ptr[1] = NULL; - } -} - - -#define STACK_TARGET 1 -#define IGNORE_TARGET 2 - -/* Get the access flags of a class (TYPE_DECL), a method (FUNCTION_DECL), or - a field (FIELD_DECL or VAR_DECL, if static), as encoded in a .class file. */ - -static int -get_access_flags (decl) - tree decl; -{ - int flags = 0; - int isfield = TREE_CODE (decl) == FIELD_DECL || TREE_CODE (decl) == VAR_DECL; - if (CLASS_PUBLIC (decl)) /* same as FIELD_PUBLIC and METHOD_PUBLIC */ - flags |= ACC_PUBLIC; - if (CLASS_FINAL (decl)) /* same as FIELD_FINAL and METHOD_FINAL */ - flags |= ACC_FINAL; - if (isfield || TREE_CODE (decl) == FUNCTION_DECL) - { - if (TREE_PROTECTED (decl)) - flags |= ACC_PROTECTED; - if (TREE_PRIVATE (decl)) - flags |= ACC_PRIVATE; - } - else if (TREE_CODE (decl) == TYPE_DECL) - { - if (CLASS_SUPER (decl)) - flags |= ACC_SUPER; - if (CLASS_ABSTRACT (decl)) - flags |= ACC_ABSTRACT; - if (CLASS_INTERFACE (decl)) - flags |= ACC_INTERFACE; - if (CLASS_STATIC (decl)) - flags |= ACC_STATIC; - if (ANONYMOUS_CLASS_P (TREE_TYPE (decl)) - || LOCAL_CLASS_P (TREE_TYPE (decl))) - flags |= ACC_PRIVATE; - if (CLASS_STRICTFP (decl)) - flags |= ACC_STRICT; - } - else - abort (); - - if (TREE_CODE (decl) == FUNCTION_DECL) - { - if (METHOD_NATIVE (decl)) - flags |= ACC_NATIVE; - if (METHOD_STATIC (decl)) - flags |= ACC_STATIC; - if (METHOD_SYNCHRONIZED (decl)) - flags |= ACC_SYNCHRONIZED; - if (METHOD_ABSTRACT (decl)) - flags |= ACC_ABSTRACT; - if (METHOD_STRICTFP (decl)) - flags |= ACC_STRICT; - } - if (isfield) - { - if (FIELD_STATIC (decl)) - flags |= ACC_STATIC; - if (FIELD_VOLATILE (decl)) - flags |= ACC_VOLATILE; - if (FIELD_TRANSIENT (decl)) - flags |= ACC_TRANSIENT; - } - return flags; -} - -/* Write the list of segments starting at CHUNKS to STREAM. */ - -static void -write_chunks (stream, chunks) - FILE* stream; - struct chunk *chunks; -{ - for (; chunks != NULL; chunks = chunks->next) - fwrite (chunks->data, chunks->size, 1, stream); -} - -/* Push a 1-word constant in the constant pool at the given INDEX. - (Caller is responsible for doing NOTE_PUSH.) */ - -static void -push_constant1 (index, state) - HOST_WIDE_INT index; - struct jcf_partial *state; -{ - RESERVE (3); - if (index < 256) - { - OP1 (OPCODE_ldc); - OP1 (index); - } - else - { - OP1 (OPCODE_ldc_w); - OP2 (index); - } -} - -/* Push a 2-word constant in the constant pool at the given INDEX. - (Caller is responsible for doing NOTE_PUSH.) */ - -static void -push_constant2 (index, state) - HOST_WIDE_INT index; - struct jcf_partial *state; -{ - RESERVE (3); - OP1 (OPCODE_ldc2_w); - OP2 (index); -} - -/* Push 32-bit integer constant on VM stack. - Caller is responsible for doing NOTE_PUSH. */ - -static void -push_int_const (i, state) - HOST_WIDE_INT i; - struct jcf_partial *state; -{ - RESERVE(3); - if (i >= -1 && i <= 5) - OP1(OPCODE_iconst_0 + i); - else if (i >= -128 && i < 128) - { - OP1(OPCODE_bipush); - OP1(i); - } - else if (i >= -32768 && i < 32768) - { - OP1(OPCODE_sipush); - OP2(i); - } - else - { - i = find_constant1 (&state->cpool, CONSTANT_Integer, - (jword)(i & 0xFFFFFFFF)); - push_constant1 (i, state); - } -} - -static int -find_constant_wide (lo, hi, state) - HOST_WIDE_INT lo, hi; - struct jcf_partial *state; -{ - HOST_WIDE_INT w1, w2; - lshift_double (lo, hi, -32, 64, &w1, &w2, 1); - return find_constant2 (&state->cpool, CONSTANT_Long, - (jword)(w1 & 0xFFFFFFFF), (jword)(lo & 0xFFFFFFFF)); -} - -/* Find or allocate a constant pool entry for the given VALUE. - Return the index in the constant pool. */ - -static int -find_constant_index (value, state) - tree value; - struct jcf_partial *state; -{ - if (TREE_CODE (value) == INTEGER_CST) - { - if (TYPE_PRECISION (TREE_TYPE (value)) <= 32) - return find_constant1 (&state->cpool, CONSTANT_Integer, - (jword)(TREE_INT_CST_LOW (value) & 0xFFFFFFFF)); - else - return find_constant_wide (TREE_INT_CST_LOW (value), - TREE_INT_CST_HIGH (value), state); - } - else if (TREE_CODE (value) == REAL_CST) - { - long words[2]; - if (TYPE_PRECISION (TREE_TYPE (value)) == 32) - { - words[0] = etarsingle (TREE_REAL_CST (value)) & 0xFFFFFFFF; - return find_constant1 (&state->cpool, CONSTANT_Float, - (jword)words[0]); - } - else - { - etardouble (TREE_REAL_CST (value), words); - return find_constant2 (&state->cpool, CONSTANT_Double, - (jword)(words[1-FLOAT_WORDS_BIG_ENDIAN] & - 0xFFFFFFFF), - (jword)(words[FLOAT_WORDS_BIG_ENDIAN] & - 0xFFFFFFFF)); - } - } - else if (TREE_CODE (value) == STRING_CST) - return find_string_constant (&state->cpool, value); - - else - abort (); -} - -/* Push 64-bit long constant on VM stack. - Caller is responsible for doing NOTE_PUSH. */ - -static void -push_long_const (lo, hi, state) - HOST_WIDE_INT lo, hi; - struct jcf_partial *state; -{ - HOST_WIDE_INT highpart, dummy; - jint lowpart = WORD_TO_INT (lo); - - rshift_double (lo, hi, 32, 64, &highpart, &dummy, 1); - - if (highpart == 0 && (lowpart == 0 || lowpart == 1)) - { - RESERVE(1); - OP1(OPCODE_lconst_0 + lowpart); - } - else if ((highpart == 0 && lowpart > 0 && lowpart < 32768) - || (highpart == -1 && lowpart < 0 && lowpart >= -32768)) - { - push_int_const (lowpart, state); - RESERVE (1); - OP1 (OPCODE_i2l); - } - else - push_constant2 (find_constant_wide (lo, hi, state), state); -} - -static void -field_op (field, opcode, state) - tree field; - int opcode; - struct jcf_partial *state; -{ - int index = find_fieldref_index (&state->cpool, field); - RESERVE (3); - OP1 (opcode); - OP2 (index); -} - -/* Returns an integer in the range 0 (for 'int') through 4 (for object - reference) to 7 (for 'short') which matches the pattern of how JVM - opcodes typically depend on the operand type. */ - -static int -adjust_typed_op (type, max) - tree type; - int max; -{ - switch (TREE_CODE (type)) - { - case POINTER_TYPE: - case RECORD_TYPE: return 4; - case BOOLEAN_TYPE: - return TYPE_PRECISION (type) == 32 || max < 5 ? 0 : 5; - case CHAR_TYPE: - return TYPE_PRECISION (type) == 32 || max < 6 ? 0 : 6; - case INTEGER_TYPE: - switch (TYPE_PRECISION (type)) - { - case 8: return max < 5 ? 0 : 5; - case 16: return max < 7 ? 0 : 7; - case 32: return 0; - case 64: return 1; - } - break; - case REAL_TYPE: - switch (TYPE_PRECISION (type)) - { - case 32: return 2; - case 64: return 3; - } - break; - default: - break; - } - abort (); -} - -static void -maybe_wide (opcode, index, state) - int opcode, index; - struct jcf_partial *state; -{ - if (index >= 256) - { - RESERVE (4); - OP1 (OPCODE_wide); - OP1 (opcode); - OP2 (index); - } - else - { - RESERVE (2); - OP1 (opcode); - OP1 (index); - } -} - -/* Compile code to duplicate with offset, where - SIZE is the size of the stack item to duplicate (1 or 2), abd - OFFSET is where to insert the result (must be 0, 1, or 2). - (The new words get inserted at stack[SP-size-offset].) */ - -static void -emit_dup (size, offset, state) - int size, offset; - struct jcf_partial *state; -{ - int kind; - if (size == 0) - return; - RESERVE(1); - if (offset == 0) - kind = size == 1 ? OPCODE_dup : OPCODE_dup2; - else if (offset == 1) - kind = size == 1 ? OPCODE_dup_x1 : OPCODE_dup2_x1; - else if (offset == 2) - kind = size == 1 ? OPCODE_dup_x2 : OPCODE_dup2_x2; - else - abort(); - OP1 (kind); - NOTE_PUSH (size); -} - -static void -emit_pop (size, state) - int size; - struct jcf_partial *state; -{ - RESERVE (1); - OP1 (OPCODE_pop - 1 + size); -} - -static void -emit_iinc (var, value, state) - tree var; - HOST_WIDE_INT value; - struct jcf_partial *state; -{ - int slot = DECL_LOCAL_INDEX (var); - - if (value < -128 || value > 127 || slot >= 256) - { - RESERVE (6); - OP1 (OPCODE_wide); - OP1 (OPCODE_iinc); - OP2 (slot); - OP2 (value); - } - else - { - RESERVE (3); - OP1 (OPCODE_iinc); - OP1 (slot); - OP1 (value); - } -} - -static void -emit_load_or_store (var, opcode, state) - tree var; /* Variable to load from or store into. */ - int opcode; /* Either OPCODE_iload or OPCODE_istore. */ - struct jcf_partial *state; -{ - tree type = TREE_TYPE (var); - int kind = adjust_typed_op (type, 4); - int index = DECL_LOCAL_INDEX (var); - if (index <= 3) - { - RESERVE (1); - OP1 (opcode + 5 + 4 * kind + index); /* [ilfda]{load,store}_[0123] */ - } - else - maybe_wide (opcode + kind, index, state); /* [ilfda]{load,store} */ -} - -static void -emit_load (var, state) - tree var; - struct jcf_partial *state; -{ - emit_load_or_store (var, OPCODE_iload, state); - NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (var)) ? 2 : 1); -} - -static void -emit_store (var, state) - tree var; - struct jcf_partial *state; -{ - emit_load_or_store (var, OPCODE_istore, state); - NOTE_POP (TYPE_IS_WIDE (TREE_TYPE (var)) ? 2 : 1); -} - -static void -emit_unop (opcode, type, state) - enum java_opcode opcode; - tree type ATTRIBUTE_UNUSED; - struct jcf_partial *state; -{ - RESERVE(1); - OP1 (opcode); -} - -static void -emit_binop (opcode, type, state) - enum java_opcode opcode; - tree type; - struct jcf_partial *state; -{ - int size = TYPE_IS_WIDE (type) ? 2 : 1; - RESERVE(1); - OP1 (opcode); - NOTE_POP (size); -} - -static void -emit_reloc (value, kind, target, state) - 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_block *block = state->last_block; - reloc->next = block->u.relocations; - block->u.relocations = reloc; - reloc->offset = BUFFER_LENGTH (&state->bytecode); - reloc->label = target; - reloc->kind = kind; - if (kind == 0 || kind == BLOCK_START_RELOC) - OP4 (value); - else if (kind != SWITCH_ALIGN_RELOC) - OP2 (value); -} - -static void -emit_switch_reloc (label, state) - struct jcf_block *label; - struct jcf_partial *state; -{ - emit_reloc (RELOCATION_VALUE_0, BLOCK_START_RELOC, label, state); -} - -/* Similar to emit_switch_reloc, - but re-uses an existing case reloc. */ - -static void -emit_case_reloc (reloc, state) - struct jcf_relocation *reloc; - struct jcf_partial *state; -{ - struct jcf_block *block = state->last_block; - reloc->next = block->u.relocations; - block->u.relocations = reloc; - reloc->offset = BUFFER_LENGTH (&state->bytecode); - reloc->kind = BLOCK_START_RELOC; - OP4 (0); -} - -/* Emit a conditional jump to TARGET with a 2-byte relative jump offset - The opcode is OPCODE, the inverted opcode is INV_OPCODE. */ - -static void -emit_if (target, opcode, inv_opcode, state) - struct jcf_block *target; - int opcode, inv_opcode; - struct jcf_partial *state; -{ - RESERVE(3); - OP1 (opcode); - /* value is 1 byte from reloc back to start of instruction. */ - emit_reloc (RELOCATION_VALUE_1, - inv_opcode, target, state); -} - -static void -emit_goto (target, state) - struct jcf_block *target; - struct jcf_partial *state; -{ - RESERVE(3); - OP1 (OPCODE_goto); - /* Value is 1 byte from reloc back to start of instruction. */ - emit_reloc (RELOCATION_VALUE_1, OPCODE_goto_w, target, state); -} - -static void -emit_jsr (target, state) - struct jcf_block *target; - struct jcf_partial *state; -{ - RESERVE(3); - OP1 (OPCODE_jsr); - /* Value is 1 byte from reloc back to start of instruction. */ - emit_reloc (RELOCATION_VALUE_1, OPCODE_jsr_w, target, state); -} - -/* 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_LABEL may follow right after this. (The idea is that we - may be able to optimize away GOTO TRUE_LABEL; TRUE_LABEL:) */ - -static void -generate_bytecode_conditional (exp, true_label, false_label, - true_branch_first, state) - tree exp; - struct jcf_block *true_label; - struct jcf_block *false_label; - int true_branch_first; - struct jcf_partial *state; -{ - tree exp0, exp1, type; - int save_SP = state->code_SP; - enum java_opcode op, negop; - switch (TREE_CODE (exp)) - { - case INTEGER_CST: - emit_goto (integer_zerop (exp) ? false_label : true_label, state); - break; - case COND_EXPR: - { - struct jcf_block *then_label = gen_jcf_label (state); - struct jcf_block *else_label = gen_jcf_label (state); - int save_SP_before, save_SP_after; - generate_bytecode_conditional (TREE_OPERAND (exp, 0), - then_label, else_label, 1, state); - define_jcf_label (then_label, state); - save_SP_before = state->code_SP; - generate_bytecode_conditional (TREE_OPERAND (exp, 1), - true_label, false_label, 1, state); - save_SP_after = state->code_SP; - state->code_SP = save_SP_before; - define_jcf_label (else_label, state); - generate_bytecode_conditional (TREE_OPERAND (exp, 2), - true_label, false_label, - true_branch_first, state); - if (state->code_SP != save_SP_after) - abort (); - } - break; - case TRUTH_NOT_EXPR: - generate_bytecode_conditional (TREE_OPERAND (exp, 0), false_label, - true_label, ! true_branch_first, state); - break; - case TRUTH_ANDIF_EXPR: - { - struct jcf_block *next_label = gen_jcf_label (state); - generate_bytecode_conditional (TREE_OPERAND (exp, 0), - next_label, false_label, 1, state); - define_jcf_label (next_label, state); - generate_bytecode_conditional (TREE_OPERAND (exp, 1), - true_label, false_label, 1, state); - } - break; - case TRUTH_ORIF_EXPR: - { - struct jcf_block *next_label = gen_jcf_label (state); - generate_bytecode_conditional (TREE_OPERAND (exp, 0), - true_label, next_label, 1, state); - define_jcf_label (next_label, state); - generate_bytecode_conditional (TREE_OPERAND (exp, 1), - true_label, false_label, 1, state); - } - break; - compare_1: - /* Assuming op is one of the 2-operand if_icmp instructions, - set it to the corresponding 1-operand if instructions. */ - op = op - 6; - /* FALLTHROUGH */ - compare_2: - /* The opcodes with their inverses are allocated in pairs. - E.g. The inverse of if_icmplt (161) is if_icmpge (162). */ - negop = (op & 1) ? op + 1 : op - 1; - compare_2_ptr: - if (true_branch_first) - { - emit_if (false_label, negop, op, state); - emit_goto (true_label, state); - } - else - { - emit_if (true_label, op, negop, state); - emit_goto (false_label, state); - } - break; - case EQ_EXPR: - op = OPCODE_if_icmpeq; - goto compare; - case NE_EXPR: - op = OPCODE_if_icmpne; - goto compare; - case GT_EXPR: - op = OPCODE_if_icmpgt; - goto compare; - case LT_EXPR: - op = OPCODE_if_icmplt; - goto compare; - case GE_EXPR: - op = OPCODE_if_icmpge; - goto compare; - case LE_EXPR: - op = OPCODE_if_icmple; - goto compare; - compare: - exp0 = TREE_OPERAND (exp, 0); - exp1 = TREE_OPERAND (exp, 1); - type = TREE_TYPE (exp0); - switch (TREE_CODE (type)) - { - int opf; - case POINTER_TYPE: case RECORD_TYPE: - switch (TREE_CODE (exp)) - { - case EQ_EXPR: op = OPCODE_if_acmpeq; break; - case NE_EXPR: op = OPCODE_if_acmpne; break; - default: abort(); - } - if (integer_zerop (exp1) || integer_zerop (exp0)) - { - generate_bytecode_insns (integer_zerop (exp0) ? exp1 : exp0, - STACK_TARGET, state); - op = op + (OPCODE_ifnull - OPCODE_if_acmpeq); - negop = (op & 1) ? op - 1 : op + 1; - NOTE_POP (1); - goto compare_2_ptr; - } - generate_bytecode_insns (exp0, STACK_TARGET, state); - generate_bytecode_insns (exp1, STACK_TARGET, state); - NOTE_POP (2); - goto compare_2; - case REAL_TYPE: - generate_bytecode_insns (exp0, STACK_TARGET, state); - generate_bytecode_insns (exp1, STACK_TARGET, state); - if (op == OPCODE_if_icmplt || op == OPCODE_if_icmple) - opf = OPCODE_fcmpg; - else - opf = OPCODE_fcmpl; - if (TYPE_PRECISION (type) > 32) - { - opf += 2; - NOTE_POP (4); - } - else - NOTE_POP (2); - RESERVE (1); - OP1 (opf); - goto compare_1; - case INTEGER_TYPE: - if (TYPE_PRECISION (type) > 32) - { - generate_bytecode_insns (exp0, STACK_TARGET, state); - generate_bytecode_insns (exp1, STACK_TARGET, state); - NOTE_POP (4); - RESERVE (1); - OP1 (OPCODE_lcmp); - goto compare_1; - } - /* FALLTHOUGH */ - default: - if (integer_zerop (exp1)) - { - generate_bytecode_insns (exp0, STACK_TARGET, state); - NOTE_POP (1); - goto compare_1; - } - if (integer_zerop (exp0)) - { - switch (op) - { - case OPCODE_if_icmplt: - case OPCODE_if_icmpge: - op += 2; - break; - case OPCODE_if_icmpgt: - case OPCODE_if_icmple: - op -= 2; - break; - default: - break; - } - generate_bytecode_insns (exp1, STACK_TARGET, state); - NOTE_POP (1); - goto compare_1; - } - generate_bytecode_insns (exp0, STACK_TARGET, state); - generate_bytecode_insns (exp1, STACK_TARGET, state); - NOTE_POP (2); - goto compare_2; - } - - default: - generate_bytecode_insns (exp, STACK_TARGET, state); - NOTE_POP (1); - if (true_branch_first) - { - emit_if (false_label, OPCODE_ifeq, OPCODE_ifne, state); - emit_goto (true_label, state); - } - else - { - emit_if (true_label, OPCODE_ifne, OPCODE_ifeq, state); - emit_goto (false_label, state); - } - break; - } - if (save_SP != state->code_SP) - abort (); -} - -/* Call pending cleanups i.e. those for surrounding TRY_FINALLY_EXPRs. - but only as far out as LIMIT (since we are about to jump to the - emit label that is LIMIT). */ - -static void -call_cleanups (limit, state) - struct jcf_block *limit; - struct jcf_partial *state; -{ - struct jcf_block *block = state->labeled_blocks; - for (; block != limit; block = block->next) - { - if (block->pc == PENDING_CLEANUP_PC) - emit_jsr (block, state); - } -} - -static void -generate_bytecode_return (exp, state) - tree exp; - struct jcf_partial *state; -{ - tree return_type = TREE_TYPE (TREE_TYPE (state->current_method)); - int returns_void = TREE_CODE (return_type) == VOID_TYPE; - int op; - again: - if (exp != NULL) - { - switch (TREE_CODE (exp)) - { - case COMPOUND_EXPR: - generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, - state); - exp = TREE_OPERAND (exp, 1); - goto again; - case COND_EXPR: - { - struct jcf_block *then_label = gen_jcf_label (state); - struct jcf_block *else_label = gen_jcf_label (state); - generate_bytecode_conditional (TREE_OPERAND (exp, 0), - then_label, else_label, 1, state); - define_jcf_label (then_label, state); - generate_bytecode_return (TREE_OPERAND (exp, 1), state); - define_jcf_label (else_label, state); - generate_bytecode_return (TREE_OPERAND (exp, 2), state); - } - return; - default: - generate_bytecode_insns (exp, - returns_void ? IGNORE_TARGET - : STACK_TARGET, state); - } - } - if (returns_void) - { - op = OPCODE_return; - call_cleanups (NULL, state); - } - else - { - op = OPCODE_ireturn + adjust_typed_op (return_type, 4); - if (state->num_finalizers > 0) - { - if (state->return_value_decl == NULL_TREE) - { - state->return_value_decl - = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (exp)); - localvar_alloc (state->return_value_decl, 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), - 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 - the greatest solution, but it is at least simple and safe. */ - } - } - RESERVE (1); - OP1 (op); -} - -/* Generate bytecode for sub-expression EXP of METHOD. - TARGET is one of STACK_TARGET or IGNORE_TARGET. */ - -static void -generate_bytecode_insns (exp, target, state) - tree exp; - int target; - struct jcf_partial *state; -{ - tree type, arg; - enum java_opcode jopcode; - int op; - HOST_WIDE_INT value; - int post_op; - int size; - int offset; - - if (exp == NULL && target == IGNORE_TARGET) - return; - - type = TREE_TYPE (exp); - - switch (TREE_CODE (exp)) - { - case BLOCK: - if (BLOCK_EXPR_BODY (exp)) - { - tree local; - tree body = BLOCK_EXPR_BODY (exp); - for (local = BLOCK_EXPR_DECLS (exp); local; ) - { - tree next = TREE_CHAIN (local); - localvar_alloc (local, state); - local = next; - } - /* Avoid deep recursion for long blocks. */ - while (TREE_CODE (body) == COMPOUND_EXPR) - { - generate_bytecode_insns (TREE_OPERAND (body, 0), target, 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); - local = next; - } - } - break; - case COMPOUND_EXPR: - generate_bytecode_insns (TREE_OPERAND (exp, 0), IGNORE_TARGET, state); - /* Normally the first operand to a COMPOUND_EXPR must complete - normally. However, in the special case of a do-while - statement this is not necessarily the case. */ - if (CAN_COMPLETE_NORMALLY (TREE_OPERAND (exp, 0))) - generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); - break; - case EXPR_WITH_FILE_LOCATION: - { - const char *saved_input_filename = input_filename; - tree body = EXPR_WFL_NODE (exp); - int saved_lineno = lineno; - 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 - && debug_info_level > DINFO_LEVEL_NONE) - put_linenumber (lineno, state); - generate_bytecode_insns (body, target, state); - input_filename = saved_input_filename; - lineno = saved_lineno; - } - break; - case INTEGER_CST: - if (target == IGNORE_TARGET) ; /* do nothing */ - else if (TREE_CODE (type) == POINTER_TYPE) - { - if (! integer_zerop (exp)) - abort(); - RESERVE(1); - OP1 (OPCODE_aconst_null); - NOTE_PUSH (1); - } - else if (TYPE_PRECISION (type) <= 32) - { - push_int_const (TREE_INT_CST_LOW (exp), state); - NOTE_PUSH (1); - } - else - { - push_long_const (TREE_INT_CST_LOW (exp), TREE_INT_CST_HIGH (exp), - state); - NOTE_PUSH (2); - } - break; - case REAL_CST: - { - int prec = TYPE_PRECISION (type) >> 5; - RESERVE(1); - if (real_zerop (exp) && ! REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (exp))) - 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 - for other float/double when the value is a small integer. */ - else - { - offset = find_constant_index (exp, state); - if (prec == 1) - push_constant1 (offset, state); - else - push_constant2 (offset, state); - } - NOTE_PUSH (prec); - } - break; - case STRING_CST: - push_constant1 (find_string_constant (&state->cpool, exp), state); - NOTE_PUSH (1); - break; - case VAR_DECL: - if (TREE_STATIC (exp)) - { - field_op (exp, OPCODE_getstatic, state); - NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 2 : 1); - break; - } - /* ... fall through ... */ - case PARM_DECL: - emit_load (exp, state); - break; - case NON_LVALUE_EXPR: - case INDIRECT_REF: - generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); - break; - case ARRAY_REF: - generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); - generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); - if (target != IGNORE_TARGET) - { - jopcode = OPCODE_iaload + adjust_typed_op (type, 7); - RESERVE(1); - OP1 (jopcode); - if (! TYPE_IS_WIDE (type)) - NOTE_POP (1); - } - break; - case COMPONENT_REF: - { - tree obj = TREE_OPERAND (exp, 0); - tree field = TREE_OPERAND (exp, 1); - int is_static = FIELD_STATIC (field); - generate_bytecode_insns (obj, - is_static ? IGNORE_TARGET : target, state); - if (target != IGNORE_TARGET) - { - if (DECL_NAME (field) == length_identifier_node && !is_static - && TYPE_ARRAY_P (TREE_TYPE (obj))) - { - RESERVE (1); - OP1 (OPCODE_arraylength); - } - else - { - field_op (field, is_static ? OPCODE_getstatic : OPCODE_getfield, - state); - if (! is_static) - NOTE_POP (1); - NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (field)) ? 2 : 1); - } - } - } - break; - case TRUTH_ANDIF_EXPR: - case TRUTH_ORIF_EXPR: - case EQ_EXPR: - case NE_EXPR: - case GT_EXPR: - case LT_EXPR: - case GE_EXPR: - case LE_EXPR: - { - struct jcf_block *then_label = gen_jcf_label (state); - struct jcf_block *else_label = gen_jcf_label (state); - struct jcf_block *end_label = gen_jcf_label (state); - generate_bytecode_conditional (exp, - then_label, else_label, 1, state); - define_jcf_label (then_label, state); - push_int_const (1, state); - emit_goto (end_label, state); - define_jcf_label (else_label, state); - push_int_const (0, state); - define_jcf_label (end_label, state); - NOTE_PUSH (1); - } - break; - case COND_EXPR: - { - struct jcf_block *then_label = gen_jcf_label (state); - struct jcf_block *else_label = gen_jcf_label (state); - struct jcf_block *end_label = gen_jcf_label (state); - generate_bytecode_conditional (TREE_OPERAND (exp, 0), - then_label, else_label, 1, state); - define_jcf_label (then_label, state); - generate_bytecode_insns (TREE_OPERAND (exp, 1), target, state); - if (CAN_COMPLETE_NORMALLY (TREE_OPERAND (exp, 1)) - /* Not all expressions have CAN_COMPLETE_NORMALLY set properly. */ - || TREE_CODE (TREE_TYPE (exp)) != VOID_TYPE) - emit_goto (end_label, state); - define_jcf_label (else_label, state); - generate_bytecode_insns (TREE_OPERAND (exp, 2), target, state); - define_jcf_label (end_label, state); - /* COND_EXPR can be used in a binop. The stack must be adjusted. */ - if (TREE_TYPE (exp) != void_type_node) - NOTE_POP (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 2 : 1); - } - break; - 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)); - HOST_WIDE_INT case_value = TREE_INT_CST_LOW (TREE_OPERAND (exp, 0)); - reloc->kind = 0; - reloc->label = get_jcf_label_here (state); - reloc->offset = case_value; - reloc->next = sw_state->cases; - sw_state->cases = reloc; - if (sw_state->num_cases == 0) - { - sw_state->min_case = case_value; - sw_state->max_case = case_value; - } - else - { - if (case_value < sw_state->min_case) - sw_state->min_case = case_value; - if (case_value > sw_state->max_case) - sw_state->max_case = case_value; - } - sw_state->num_cases++; - } - break; - case DEFAULT_EXPR: - state->sw_state->default_label = get_jcf_label_here (state); - break; - - case SWITCH_EXPR: - { - /* The SWITCH_EXPR has three parts, generated in the following order: - 1. the switch_expression (the value used to select the correct case); - 2. the switch_body; - 3. the switch_instruction (the tableswitch/loopupswitch instruction.). - After code generation, we will re-order them in the order 1, 3, 2. - This is to avoid any extra GOTOs. */ - struct jcf_switch_state sw_state; - struct jcf_block *expression_last; /* Last block of the switch_expression. */ - struct jcf_block *body_last; /* Last block of the switch_body. */ - struct jcf_block *switch_instruction; /* First block of switch_instruction. */ - struct jcf_block *instruction_last; /* Last block of the switch_instruction. */ - struct jcf_block *body_block; - int switch_length; - sw_state.prev = state->sw_state; - state->sw_state = &sw_state; - sw_state.cases = NULL; - sw_state.num_cases = 0; - sw_state.default_label = NULL; - generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state); - expression_last = state->last_block; - /* Force a new block here. */ - body_block = gen_jcf_label (state); - define_jcf_label (body_block, state); - generate_bytecode_insns (TREE_OPERAND (exp, 1), IGNORE_TARGET, state); - body_last = state->last_block; - - switch_instruction = gen_jcf_label (state); - define_jcf_label (switch_instruction, state); - if (sw_state.default_label == NULL) - sw_state.default_label = gen_jcf_label (state); - - if (sw_state.num_cases <= 1) - { - if (sw_state.num_cases == 0) - { - emit_pop (1, state); - NOTE_POP (1); - } - else - { - push_int_const (sw_state.cases->offset, state); - NOTE_PUSH (1); - emit_if (sw_state.cases->label, - OPCODE_if_icmpeq, OPCODE_if_icmpne, state); - } - emit_goto (sw_state.default_label, state); - } - else - { - HOST_WIDE_INT i; - /* 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 *)); - /* The relocs arrays is a buffer with a gap. - The assumption is that cases will normally come in "runs". */ - int gap_start = 0; - int gap_end = sw_state.num_cases; - struct jcf_relocation *reloc; - for (reloc = sw_state.cases; reloc != NULL; reloc = reloc->next) - { - HOST_WIDE_INT case_value = reloc->offset; - while (gap_end < sw_state.num_cases) - { - struct jcf_relocation *end = relocs[gap_end]; - if (case_value <= end->offset) - break; - relocs[gap_start++] = end; - gap_end++; - } - while (gap_start > 0) - { - struct jcf_relocation *before = relocs[gap_start-1]; - if (case_value >= before->offset) - break; - relocs[--gap_end] = before; - gap_start--; - } - relocs[gap_start++] = reloc; - /* Note we don't check for duplicates. This is - handled by the parser. */ - } - - if (2 * sw_state.num_cases - >= sw_state.max_case - sw_state.min_case) - { /* Use tableswitch. */ - int index = 0; - RESERVE (13 + 4 * (sw_state.max_case - sw_state.min_case + 1)); - OP1 (OPCODE_tableswitch); - emit_reloc (RELOCATION_VALUE_0, - SWITCH_ALIGN_RELOC, NULL, state); - emit_switch_reloc (sw_state.default_label, state); - OP4 (sw_state.min_case); - OP4 (sw_state.max_case); - for (i = sw_state.min_case; ; ) - { - reloc = relocs[index]; - if (i == reloc->offset) - { - emit_case_reloc (reloc, state); - if (i == sw_state.max_case) - break; - index++; - } - else - emit_switch_reloc (sw_state.default_label, state); - i++; - } - } - else - { /* Use lookupswitch. */ - RESERVE(9 + 8 * sw_state.num_cases); - OP1 (OPCODE_lookupswitch); - emit_reloc (RELOCATION_VALUE_0, - SWITCH_ALIGN_RELOC, NULL, state); - emit_switch_reloc (sw_state.default_label, state); - OP4 (sw_state.num_cases); - for (i = 0; i < sw_state.num_cases; i++) - { - struct jcf_relocation *reloc = relocs[i]; - OP4 (reloc->offset); - emit_case_reloc (reloc, state); - } - } - free (relocs); - } - - instruction_last = state->last_block; - if (sw_state.default_label->pc < 0) - define_jcf_label (sw_state.default_label, state); - else /* Force a new block. */ - sw_state.default_label = get_jcf_label_here (state); - /* Now re-arrange the blocks so the switch_instruction - comes before the switch_body. */ - switch_length = state->code_length - switch_instruction->pc; - switch_instruction->pc = body_block->pc; - instruction_last->next = body_block; - instruction_last->v.chunk->next = body_block->v.chunk; - expression_last->next = switch_instruction; - expression_last->v.chunk->next = switch_instruction->v.chunk; - body_last->next = sw_state.default_label; - body_last->v.chunk->next = NULL; - state->chunk = body_last->v.chunk; - for (; body_block != sw_state.default_label; body_block = body_block->next) - body_block->pc += switch_length; - - state->sw_state = sw_state.prev; - break; - } - - case RETURN_EXPR: - exp = TREE_OPERAND (exp, 0); - if (exp == NULL_TREE) - exp = empty_stmt_node; - else if (TREE_CODE (exp) != MODIFY_EXPR) - abort (); - else - exp = TREE_OPERAND (exp, 1); - generate_bytecode_return (exp, state); - break; - case LABELED_BLOCK_EXPR: - { - struct jcf_block *end_label = gen_jcf_label (state); - end_label->next = state->labeled_blocks; - state->labeled_blocks = end_label; - end_label->pc = PENDING_EXIT_PC; - end_label->u.labeled_block = exp; - if (LABELED_BLOCK_BODY (exp)) - generate_bytecode_insns (LABELED_BLOCK_BODY (exp), target, state); - if (state->labeled_blocks != end_label) - abort(); - state->labeled_blocks = end_label->next; - define_jcf_label (end_label, state); - } - break; - case LOOP_EXPR: - { - tree body = TREE_OPERAND (exp, 0); -#if 0 - if (TREE_CODE (body) == COMPOUND_EXPR - && TREE_CODE (TREE_OPERAND (body, 0)) == EXIT_EXPR) - { - /* Optimize: H: if (TEST) GOTO L; BODY; GOTO H; L: - to: GOTO L; BODY; L: if (!TEST) GOTO L; */ - struct jcf_block *head_label; - struct jcf_block *body_label; - struct jcf_block *end_label = gen_jcf_label (state); - struct jcf_block *exit_label = state->labeled_blocks; - head_label = gen_jcf_label (state); - emit_goto (head_label, state); - body_label = get_jcf_label_here (state); - generate_bytecode_insns (TREE_OPERAND (body, 1), target, state); - define_jcf_label (head_label, state); - generate_bytecode_conditional (TREE_OPERAND (body, 0), - end_label, body_label, 1, state); - define_jcf_label (end_label, state); - } - else -#endif - { - struct jcf_block *head_label = get_jcf_label_here (state); - generate_bytecode_insns (body, IGNORE_TARGET, state); - if (CAN_COMPLETE_NORMALLY (body)) - emit_goto (head_label, state); - } - } - break; - case EXIT_EXPR: - { - struct jcf_block *label = state->labeled_blocks; - struct jcf_block *end_label = gen_jcf_label (state); - generate_bytecode_conditional (TREE_OPERAND (exp, 0), - label, end_label, 0, state); - define_jcf_label (end_label, state); - } - break; - case EXIT_BLOCK_EXPR: - { - struct jcf_block *label = state->labeled_blocks; - if (TREE_OPERAND (exp, 1) != NULL) goto notimpl; - while (label->u.labeled_block != TREE_OPERAND (exp, 0)) - label = label->next; - call_cleanups (label, state); - emit_goto (label, state); - } - break; - - case PREDECREMENT_EXPR: value = -1; post_op = 0; goto increment; - case PREINCREMENT_EXPR: value = 1; post_op = 0; goto increment; - case POSTDECREMENT_EXPR: value = -1; post_op = 1; goto increment; - case POSTINCREMENT_EXPR: value = 1; post_op = 1; goto increment; - increment: - - arg = TREE_OPERAND (exp, 1); - exp = TREE_OPERAND (exp, 0); - type = TREE_TYPE (exp); - size = TYPE_IS_WIDE (type) ? 2 : 1; - if ((TREE_CODE (exp) == VAR_DECL || TREE_CODE (exp) == PARM_DECL) - && ! TREE_STATIC (exp) - && TREE_CODE (type) == INTEGER_TYPE - && TYPE_PRECISION (type) == 32) - { - if (target != IGNORE_TARGET && post_op) - emit_load (exp, state); - emit_iinc (exp, value, state); - if (target != IGNORE_TARGET && ! post_op) - emit_load (exp, state); - break; - } - if (TREE_CODE (exp) == COMPONENT_REF) - { - generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state); - emit_dup (1, 0, state); - /* Stack: ..., objectref, objectref. */ - field_op (TREE_OPERAND (exp, 1), OPCODE_getfield, state); - NOTE_PUSH (size-1); - /* Stack: ..., objectref, oldvalue. */ - offset = 1; - } - else if (TREE_CODE (exp) == ARRAY_REF) - { - generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state); - generate_bytecode_insns (TREE_OPERAND (exp, 1), STACK_TARGET, state); - emit_dup (2, 0, state); - /* Stack: ..., array, index, array, index. */ - jopcode = OPCODE_iaload + adjust_typed_op (TREE_TYPE (exp), 7); - RESERVE(1); - OP1 (jopcode); - NOTE_POP (2-size); - /* Stack: ..., array, index, oldvalue. */ - offset = 2; - } - else if (TREE_CODE (exp) == VAR_DECL || TREE_CODE (exp) == PARM_DECL) - { - generate_bytecode_insns (exp, STACK_TARGET, state); - /* Stack: ..., oldvalue. */ - offset = 0; - } - else - abort (); - - if (target != IGNORE_TARGET && post_op) - emit_dup (size, offset, state); - /* Stack, if ARRAY_REF: ..., [result, ] array, index, oldvalue. */ - /* Stack, if COMPONENT_REF: ..., [result, ] objectref, oldvalue. */ - /* Stack, otherwise: ..., [result, ] oldvalue. */ - generate_bytecode_insns (arg, STACK_TARGET, state); - emit_binop ((value >= 0 ? OPCODE_iadd : OPCODE_isub) - + adjust_typed_op (type, 3), - type, state); - if (target != IGNORE_TARGET && ! post_op) - emit_dup (size, offset, state); - /* Stack, if ARRAY_REF: ..., [result, ] array, index, newvalue. */ - /* Stack, if COMPONENT_REF: ..., [result, ] objectref, newvalue. */ - /* Stack, otherwise: ..., [result, ] newvalue. */ - goto finish_assignment; - - case MODIFY_EXPR: - { - tree lhs = TREE_OPERAND (exp, 0); - tree rhs = TREE_OPERAND (exp, 1); - int offset = 0; - - /* See if we can use the iinc instruction. */ - if ((TREE_CODE (lhs) == VAR_DECL || TREE_CODE (lhs) == PARM_DECL) - && ! TREE_STATIC (lhs) - && TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE - && TYPE_PRECISION (TREE_TYPE (lhs)) == 32 - && (TREE_CODE (rhs) == PLUS_EXPR || TREE_CODE (rhs) == MINUS_EXPR)) - { - tree arg0 = TREE_OPERAND (rhs, 0); - tree arg1 = TREE_OPERAND (rhs, 1); - HOST_WIDE_INT min_value = -32768; - HOST_WIDE_INT max_value = 32767; - if (TREE_CODE (rhs) == MINUS_EXPR) - { - min_value++; - max_value++; - } - else if (arg1 == lhs) - { - arg0 = arg1; - arg1 = TREE_OPERAND (rhs, 0); - } - if (lhs == arg0 && TREE_CODE (arg1) == INTEGER_CST) - { - HOST_WIDE_INT hi_value = TREE_INT_CST_HIGH (arg1); - value = TREE_INT_CST_LOW (arg1); - if ((hi_value == 0 && value <= max_value) - || (hi_value == -1 && value >= min_value)) - { - if (TREE_CODE (rhs) == MINUS_EXPR) - value = -value; - emit_iinc (lhs, value, state); - if (target != IGNORE_TARGET) - emit_load (lhs, state); - break; - } - } - } - - if (TREE_CODE (lhs) == COMPONENT_REF) - { - generate_bytecode_insns (TREE_OPERAND (lhs, 0), - STACK_TARGET, state); - offset = 1; - } - else if (TREE_CODE (lhs) == ARRAY_REF) - { - generate_bytecode_insns (TREE_OPERAND(lhs, 0), - STACK_TARGET, state); - generate_bytecode_insns (TREE_OPERAND(lhs, 1), - STACK_TARGET, state); - offset = 2; - } - else - offset = 0; - - /* If the rhs is a binary expression and the left operand is - `==' to the lhs then we have an OP= expression. In this - case we must do some special processing. */ - if (TREE_CODE_CLASS (TREE_CODE (rhs)) == '2' - && lhs == TREE_OPERAND (rhs, 0)) - { - if (TREE_CODE (lhs) == COMPONENT_REF) - { - tree field = TREE_OPERAND (lhs, 1); - if (! FIELD_STATIC (field)) - { - /* Duplicate the object reference so we can get - the field. */ - emit_dup (TYPE_IS_WIDE (field) ? 2 : 1, 0, state); - NOTE_POP (1); - } - field_op (field, (FIELD_STATIC (field) - ? OPCODE_getstatic - : OPCODE_getfield), - state); - - NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (field)) ? 2 : 1); - } - else if (TREE_CODE (lhs) == VAR_DECL - || TREE_CODE (lhs) == PARM_DECL) - { - if (FIELD_STATIC (lhs)) - { - field_op (lhs, OPCODE_getstatic, state); - NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (lhs)) ? 2 : 1); - } - else - emit_load (lhs, state); - } - else if (TREE_CODE (lhs) == ARRAY_REF) - { - /* Duplicate the array and index, which are on the - stack, so that we can load the old value. */ - emit_dup (2, 0, state); - NOTE_POP (2); - jopcode = OPCODE_iaload + adjust_typed_op (TREE_TYPE (lhs), 7); - RESERVE (1); - OP1 (jopcode); - NOTE_PUSH (TYPE_IS_WIDE (TREE_TYPE (lhs)) ? 2 : 1); - } - else - abort (); - - /* This function correctly handles the case where the LHS - of a binary expression is NULL_TREE. */ - rhs = build (TREE_CODE (rhs), TREE_TYPE (rhs), - NULL_TREE, TREE_OPERAND (rhs, 1)); - } - - generate_bytecode_insns (rhs, STACK_TARGET, state); - if (target != IGNORE_TARGET) - emit_dup (TYPE_IS_WIDE (type) ? 2 : 1 , offset, state); - exp = lhs; - } - /* FALLTHOUGH */ - - finish_assignment: - if (TREE_CODE (exp) == COMPONENT_REF) - { - tree field = TREE_OPERAND (exp, 1); - if (! FIELD_STATIC (field)) - NOTE_POP (1); - field_op (field, - FIELD_STATIC (field) ? OPCODE_putstatic : OPCODE_putfield, - state); - - NOTE_POP (TYPE_IS_WIDE (TREE_TYPE (field)) ? 2 : 1); - } - else if (TREE_CODE (exp) == VAR_DECL - || TREE_CODE (exp) == PARM_DECL) - { - if (FIELD_STATIC (exp)) - { - field_op (exp, OPCODE_putstatic, state); - NOTE_POP (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 2 : 1); - } - else - emit_store (exp, state); - } - else if (TREE_CODE (exp) == ARRAY_REF) - { - jopcode = OPCODE_iastore + adjust_typed_op (TREE_TYPE (exp), 7); - RESERVE (1); - OP1 (jopcode); - NOTE_POP (TYPE_IS_WIDE (TREE_TYPE (exp)) ? 4 : 3); - } - else - abort (); - break; - case PLUS_EXPR: - jopcode = OPCODE_iadd; - goto binop; - case MINUS_EXPR: - jopcode = OPCODE_isub; - goto binop; - case MULT_EXPR: - jopcode = OPCODE_imul; - goto binop; - case TRUNC_DIV_EXPR: - case RDIV_EXPR: - jopcode = OPCODE_idiv; - goto binop; - case TRUNC_MOD_EXPR: - jopcode = OPCODE_irem; - goto binop; - case LSHIFT_EXPR: jopcode = OPCODE_ishl; goto binop; - case RSHIFT_EXPR: jopcode = OPCODE_ishr; goto binop; - case URSHIFT_EXPR: jopcode = OPCODE_iushr; goto binop; - case TRUTH_AND_EXPR: - case BIT_AND_EXPR: jopcode = OPCODE_iand; goto binop; - case TRUTH_OR_EXPR: - case BIT_IOR_EXPR: jopcode = OPCODE_ior; goto binop; - case TRUTH_XOR_EXPR: - case BIT_XOR_EXPR: jopcode = OPCODE_ixor; goto binop; - binop: - { - 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) - { - /* fold may (e.g) convert 2*x to x+x. */ - generate_bytecode_insns (TREE_OPERAND (arg0, 0), target, state); - emit_dup (TYPE_PRECISION (TREE_TYPE (arg0)) > 32 ? 2 : 1, 0, state); - } - else - { - /* ARG0 will be NULL_TREE if we're handling an `OP=' - expression. In this case the stack already holds the - LHS. See the MODIFY_EXPR case. */ - if (arg0 != NULL_TREE) - generate_bytecode_insns (arg0, target, state); - if (jopcode >= OPCODE_lshl && jopcode <= OPCODE_lushr) - arg1 = convert (int_type_node, arg1); - generate_bytecode_insns (arg1, target, state); - } - /* For most binary operations, both operands and the result have the - same type. Shift operations are different. Using arg1's type - gets us the correct SP adjustment in all cases. */ - if (target == STACK_TARGET) - emit_binop (jopcode, TREE_TYPE (arg1), state); - break; - } - case TRUTH_NOT_EXPR: - case BIT_NOT_EXPR: - generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); - if (target == STACK_TARGET) - { - int is_long = TYPE_PRECISION (TREE_TYPE (exp)) > 32; - push_int_const (TREE_CODE (exp) == BIT_NOT_EXPR ? -1 : 1, state); - RESERVE (2); - if (is_long) - OP1 (OPCODE_i2l); - NOTE_PUSH (1 + is_long); - OP1 (OPCODE_ixor + is_long); - NOTE_POP (1 + is_long); - } - break; - case NEGATE_EXPR: - jopcode = OPCODE_ineg; - jopcode += adjust_typed_op (type, 3); - generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); - if (target == STACK_TARGET) - emit_unop (jopcode, type, state); - break; - case INSTANCEOF_EXPR: - { - int index = find_class_constant (&state->cpool, TREE_OPERAND (exp, 1)); - generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); - RESERVE (3); - OP1 (OPCODE_instanceof); - OP2 (index); - } - break; - case SAVE_EXPR: - generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state); - break; - case CONVERT_EXPR: - case NOP_EXPR: - case FLOAT_EXPR: - case FIX_TRUNC_EXPR: - { - tree src = TREE_OPERAND (exp, 0); - tree src_type = TREE_TYPE (src); - tree dst_type = TREE_TYPE (exp); - generate_bytecode_insns (TREE_OPERAND (exp, 0), target, state); - if (target == IGNORE_TARGET || src_type == dst_type) - break; - if (TREE_CODE (dst_type) == POINTER_TYPE) - { - if (TREE_CODE (exp) == CONVERT_EXPR) - { - int index = find_class_constant (&state->cpool, - TREE_TYPE (dst_type)); - RESERVE (3); - OP1 (OPCODE_checkcast); - OP2 (index); - } - } - else /* Convert numeric types. */ - { - int wide_src = TYPE_PRECISION (src_type) > 32; - int wide_dst = TYPE_PRECISION (dst_type) > 32; - NOTE_POP (1 + wide_src); - RESERVE (1); - if (TREE_CODE (dst_type) == REAL_TYPE) - { - if (TREE_CODE (src_type) == REAL_TYPE) - OP1 (wide_dst ? OPCODE_f2d : OPCODE_d2f); - else if (TYPE_PRECISION (src_type) == 64) - OP1 (OPCODE_l2f + wide_dst); - else - OP1 (OPCODE_i2f + wide_dst); - } - else /* Convert to integral type. */ - { - if (TREE_CODE (src_type) == REAL_TYPE) - OP1 (OPCODE_f2i + wide_dst + 3 * wide_src); - else if (wide_dst) - OP1 (OPCODE_i2l); - else if (wide_src) - OP1 (OPCODE_l2i); - if (TYPE_PRECISION (dst_type) < 32) - { - RESERVE (1); - /* Already converted to int, if needed. */ - if (TYPE_PRECISION (dst_type) <= 8) - OP1 (OPCODE_i2b); - else if (TREE_UNSIGNED (dst_type)) - OP1 (OPCODE_i2c); - else - OP1 (OPCODE_i2s); - } - } - NOTE_PUSH (1 + wide_dst); - } - } - break; - - case TRY_EXPR: - { - tree try_clause = TREE_OPERAND (exp, 0); - struct jcf_block *start_label = get_jcf_label_here (state); - struct jcf_block *end_label; /* End of try clause. */ - struct jcf_block *finished_label = gen_jcf_label (state); - tree clause = TREE_OPERAND (exp, 1); - if (target != IGNORE_TARGET) - abort (); - generate_bytecode_insns (try_clause, IGNORE_TARGET, state); - end_label = get_jcf_label_here (state); - if (end_label == start_label) - break; - if (CAN_COMPLETE_NORMALLY (try_clause)) - emit_goto (finished_label, state); - while (clause != NULL_TREE) - { - tree catch_clause = TREE_OPERAND (clause, 0); - tree exception_decl = BLOCK_EXPR_DECLS (catch_clause); - struct jcf_handler *handler = alloc_handler (start_label, - end_label, state); - if (exception_decl == NULL_TREE) - handler->type = NULL_TREE; - else - handler->type = TREE_TYPE (TREE_TYPE (exception_decl)); - generate_bytecode_insns (catch_clause, IGNORE_TARGET, state); - clause = TREE_CHAIN (clause); - if (CAN_COMPLETE_NORMALLY (catch_clause) && clause != NULL_TREE) - emit_goto (finished_label, state); - } - define_jcf_label (finished_label, state); - } - break; - - case TRY_FINALLY_EXPR: - { - struct jcf_block *finished_label = NULL; - struct jcf_block *finally_label, *start_label, *end_label; - struct jcf_handler *handler; - tree try_block = TREE_OPERAND (exp, 0); - tree finally = TREE_OPERAND (exp, 1); - tree return_link = NULL_TREE, exception_decl = NULL_TREE; - - tree exception_type; - - finally_label = gen_jcf_label (state); - start_label = get_jcf_label_here (state); - /* If the `finally' clause can complete normally, we emit it - as a subroutine and let the other clauses call it via - `jsr'. If it can't complete normally, then we simply emit - `goto's directly to it. */ - if (CAN_COMPLETE_NORMALLY (finally)) - { - finally_label->pc = PENDING_CLEANUP_PC; - finally_label->next = state->labeled_blocks; - state->labeled_blocks = finally_label; - state->num_finalizers++; - } - - generate_bytecode_insns (try_block, target, state); - - if (CAN_COMPLETE_NORMALLY (finally)) - { - if (state->labeled_blocks != finally_label) - abort(); - state->labeled_blocks = finally_label->next; - } - end_label = get_jcf_label_here (state); - - if (end_label == start_label) - { - state->num_finalizers--; - define_jcf_label (finally_label, state); - generate_bytecode_insns (finally, IGNORE_TARGET, state); - break; - } - - if (CAN_COMPLETE_NORMALLY (finally)) - { - return_link = build_decl (VAR_DECL, NULL_TREE, - return_address_type_node); - finished_label = gen_jcf_label (state); - } - - if (CAN_COMPLETE_NORMALLY (try_block)) - { - if (CAN_COMPLETE_NORMALLY (finally)) - { - emit_jsr (finally_label, state); - emit_goto (finished_label, state); - } - else - emit_goto (finally_label, state); - } - - /* Handle exceptions. */ - - exception_type = build_pointer_type (throwable_type_node); - if (CAN_COMPLETE_NORMALLY (finally)) - { - /* We're going to generate a subroutine, so we'll need to - save and restore the exception around the `jsr'. */ - exception_decl = build_decl (VAR_DECL, NULL_TREE, exception_type); - localvar_alloc (return_link, state); - } - handler = alloc_handler (start_label, end_label, state); - handler->type = NULL_TREE; - if (CAN_COMPLETE_NORMALLY (finally)) - { - localvar_alloc (exception_decl, state); - NOTE_PUSH (1); - emit_store (exception_decl, state); - emit_jsr (finally_label, state); - emit_load (exception_decl, state); - RESERVE (1); - OP1 (OPCODE_athrow); - NOTE_POP (1); - } - else - { - /* We're not generating a subroutine. In this case we can - simply have the exception handler pop the exception and - then fall through to the `finally' block. */ - NOTE_PUSH (1); - emit_pop (1, state); - NOTE_POP (1); - } - - /* The finally block. If we're generating a subroutine, first - save return PC into return_link. Otherwise, just generate - the code for the `finally' block. */ - define_jcf_label (finally_label, state); - if (CAN_COMPLETE_NORMALLY (finally)) - { - NOTE_PUSH (1); - emit_store (return_link, state); - } - - generate_bytecode_insns (finally, IGNORE_TARGET, 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); - define_jcf_label (finished_label, state); - } - } - break; - case THROW_EXPR: - generate_bytecode_insns (TREE_OPERAND (exp, 0), STACK_TARGET, state); - RESERVE (1); - OP1 (OPCODE_athrow); - break; - case NEW_ARRAY_INIT: - { - tree values = CONSTRUCTOR_ELTS (TREE_OPERAND (exp, 0)); - tree array_type = TREE_TYPE (TREE_TYPE (exp)); - tree element_type = TYPE_ARRAY_ELEMENT (array_type); - HOST_WIDE_INT length = java_array_type_length (array_type); - if (target == IGNORE_TARGET) - { - for ( ; values != NULL_TREE; values = TREE_CHAIN (values)) - generate_bytecode_insns (TREE_VALUE (values), target, state); - break; - } - push_int_const (length, state); - NOTE_PUSH (1); - RESERVE (3); - if (JPRIMITIVE_TYPE_P (element_type)) - { - int atype = encode_newarray_type (element_type); - OP1 (OPCODE_newarray); - OP1 (atype); - } - else - { - int index = find_class_constant (&state->cpool, - TREE_TYPE (element_type)); - OP1 (OPCODE_anewarray); - OP2 (index); - } - offset = 0; - jopcode = OPCODE_iastore + adjust_typed_op (element_type, 7); - for ( ; values != NULL_TREE; values = TREE_CHAIN (values), offset++) - { - int save_SP = state->code_SP; - emit_dup (1, 0, state); - push_int_const (offset, state); - NOTE_PUSH (1); - generate_bytecode_insns (TREE_VALUE (values), STACK_TARGET, state); - RESERVE (1); - OP1 (jopcode); - state->code_SP = save_SP; - } - } - break; - case JAVA_EXC_OBJ_EXPR: - NOTE_PUSH (1); /* Pushed by exception system. */ - break; - case NEW_CLASS_EXPR: - { - tree class = TREE_TYPE (TREE_TYPE (exp)); - int need_result = target != IGNORE_TARGET; - int index = find_class_constant (&state->cpool, class); - RESERVE (4); - OP1 (OPCODE_new); - OP2 (index); - if (need_result) - OP1 (OPCODE_dup); - NOTE_PUSH (1 + need_result); - } - /* ... fall though ... */ - case CALL_EXPR: - { - tree f = TREE_OPERAND (exp, 0); - tree x = TREE_OPERAND (exp, 1); - int save_SP = state->code_SP; - int nargs; - if (TREE_CODE (f) == ADDR_EXPR) - f = TREE_OPERAND (f, 0); - if (f == soft_newarray_node) - { - int type_code = TREE_INT_CST_LOW (TREE_VALUE (x)); - generate_bytecode_insns (TREE_VALUE (TREE_CHAIN (x)), - STACK_TARGET, state); - RESERVE (2); - OP1 (OPCODE_newarray); - OP1 (type_code); - break; - } - else if (f == soft_multianewarray_node) - { - int ndims; - int idim; - int index = find_class_constant (&state->cpool, - TREE_TYPE (TREE_TYPE (exp))); - x = TREE_CHAIN (x); /* Skip class argument. */ - ndims = TREE_INT_CST_LOW (TREE_VALUE (x)); - for (idim = ndims; --idim >= 0; ) - { - x = TREE_CHAIN (x); - generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state); - } - RESERVE (4); - OP1 (OPCODE_multianewarray); - OP2 (index); - OP1 (ndims); - break; - } - else if (f == soft_anewarray_node) - { - tree cl = TYPE_ARRAY_ELEMENT (TREE_TYPE (TREE_TYPE (exp))); - int index = find_class_constant (&state->cpool, TREE_TYPE (cl)); - generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state); - RESERVE (3); - OP1 (OPCODE_anewarray); - OP2 (index); - break; - } - else if (f == soft_monitorenter_node - || f == soft_monitorexit_node - || f == throw_node) - { - if (f == soft_monitorenter_node) - op = OPCODE_monitorenter; - else if (f == soft_monitorexit_node) - op = OPCODE_monitorexit; - else - op = OPCODE_athrow; - generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state); - RESERVE (1); - OP1 (op); - NOTE_POP (1); - break; - } - for ( ; x != NULL_TREE; x = TREE_CHAIN (x)) - { - generate_bytecode_insns (TREE_VALUE (x), STACK_TARGET, state); - } - nargs = state->code_SP - save_SP; - state->code_SP = save_SP; - if (f == soft_fmod_node) - { - RESERVE (1); - OP1 (OPCODE_drem); - NOTE_PUSH (2); - break; - } - if (TREE_CODE (exp) == NEW_CLASS_EXPR) - NOTE_POP (1); /* Pop implicit this. */ - if (TREE_CODE (f) == FUNCTION_DECL && DECL_CONTEXT (f) != NULL_TREE) - { - tree context = DECL_CONTEXT (f); - int index, interface = 0; - RESERVE (5); - if (METHOD_STATIC (f)) - OP1 (OPCODE_invokestatic); - else if (DECL_CONSTRUCTOR_P (f) || CALL_USING_SUPER (exp) - || METHOD_PRIVATE (f)) - OP1 (OPCODE_invokespecial); - else - { - if (CLASS_INTERFACE (TYPE_NAME (context))) - { - tree arg1 = TREE_VALUE (TREE_OPERAND (exp, 1)); - context = TREE_TYPE (TREE_TYPE (arg1)); - if (CLASS_INTERFACE (TYPE_NAME (context))) - interface = 1; - } - if (interface) - OP1 (OPCODE_invokeinterface); - else - OP1 (OPCODE_invokevirtual); - } - index = find_methodref_with_class_index (&state->cpool, f, context); - OP2 (index); - if (interface) - { - if (nargs <= 0) - abort (); - - OP1 (nargs); - OP1 (0); - } - f = TREE_TYPE (TREE_TYPE (f)); - if (TREE_CODE (f) != VOID_TYPE) - { - int size = TYPE_IS_WIDE (f) ? 2 : 1; - if (target == IGNORE_TARGET) - emit_pop (size, state); - else - NOTE_PUSH (size); - } - break; - } - } - /* fall through */ - notimpl: - default: - error("internal error in generate_bytecode_insn - tree code not implemented: %s", - tree_code_name [(int) TREE_CODE (exp)]); - } -} - -static void -perform_relocations (state) - struct jcf_partial *state; -{ - struct jcf_block *block; - struct jcf_relocation *reloc; - int pc; - int shrink; - - /* Before we start, the pc field of each block is an upper bound on - the block's start pc (it may be less, if previous blocks need less - than their maximum). - - The minimum size of each block is in the block's chunk->size. */ - - /* First, figure out the actual locations of each block. */ - pc = 0; - shrink = 0; - for (block = state->blocks; block != NULL; block = block->next) - { - int block_size = block->v.chunk->size; - - block->pc = pc; - - /* Optimize GOTO L; L: by getting rid of the redundant goto. - Assumes relocations are in reverse order. */ - reloc = block->u.relocations; - while (reloc != NULL - && reloc->kind == OPCODE_goto_w - && reloc->label->pc == block->next->pc - && reloc->offset + 2 == block_size) - { - reloc = reloc->next; - block->u.relocations = reloc; - block->v.chunk->size -= 3; - block_size -= 3; - shrink += 3; - } - - for (reloc = block->u.relocations; reloc != NULL; reloc = reloc->next) - { - if (reloc->kind == SWITCH_ALIGN_RELOC) - { - /* We assume this is the first relocation in this block, - so we know its final pc. */ - int where = pc + reloc->offset; - int pad = ((where + 3) & ~3) - where; - block_size += pad; - } - else if (reloc->kind < -1 || reloc->kind > BLOCK_START_RELOC) - { - int delta = reloc->label->pc - (pc + reloc->offset - 1); - int expand = reloc->kind > 0 ? 2 : 5; - - if (delta > 0) - delta -= shrink; - if (delta >= -32768 && delta <= 32767) - { - shrink += expand; - reloc->kind = -1; - } - else - block_size += expand; - } - } - pc += block_size; - } - - for (block = state->blocks; block != NULL; block = block->next) - { - struct chunk *chunk = block->v.chunk; - int old_size = chunk->size; - int next_pc = block->next == NULL ? pc : block->next->pc; - int new_size = next_pc - block->pc; - unsigned char *new_ptr; - unsigned char *old_buffer = chunk->data; - 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->size = new_size; - } - new_ptr = chunk->data + new_size; - - /* We do the relocations from back to front, because - the relocations are in reverse order. */ - for (reloc = block->u.relocations; ; reloc = reloc->next) - { - /* new_ptr and old_ptr point into the old and new buffers, - respectively. (If no relocations cause the buffer to - grow, the buffer will be the same buffer, and new_ptr==old_ptr.) - The bytes at higher address have been copied and relocations - handled; those at lower addresses remain to process. */ - - /* Lower old index of piece to be copied with no relocation. - I.e. high index of the first piece that does need relocation. */ - int start = reloc == NULL ? 0 - : reloc->kind == SWITCH_ALIGN_RELOC ? reloc->offset - : (reloc->kind == 0 || reloc->kind == BLOCK_START_RELOC) - ? reloc->offset + 4 - : reloc->offset + 2; - int32 value; - int new_offset; - int n = (old_ptr - old_buffer) - start; - new_ptr -= n; - old_ptr -= n; - if (n > 0) - memcpy (new_ptr, old_ptr, n); - if (old_ptr == old_buffer) - break; - - new_offset = new_ptr - chunk->data; - new_offset -= (reloc->kind == -1 ? 2 : 4); - if (reloc->kind == 0) - { - old_ptr -= 4; - value = GET_u4 (old_ptr); - } - else if (reloc->kind == BLOCK_START_RELOC) - { - old_ptr -= 4; - value = 0; - new_offset = 0; - } - else if (reloc->kind == SWITCH_ALIGN_RELOC) - { - int where = block->pc + reloc->offset; - int pad = ((where + 3) & ~3) - where; - while (--pad >= 0) - *--new_ptr = 0; - continue; - } - else - { - old_ptr -= 2; - value = GET_u2 (old_ptr); - } - value += reloc->label->pc - (block->pc + new_offset); - *--new_ptr = (unsigned char) value; value >>= 8; - *--new_ptr = (unsigned char) value; value >>= 8; - if (reloc->kind != -1) - { - *--new_ptr = (unsigned char) value; value >>= 8; - *--new_ptr = (unsigned char) value; - } - if (reloc->kind > BLOCK_START_RELOC) - { - /* Convert: OP TARGET to: OP_w TARGET; (OP is goto or jsr). */ - --old_ptr; - *--new_ptr = reloc->kind; - } - else if (reloc->kind < -1) - { - /* Convert: ifCOND TARGET to: ifNCOND T; goto_w TARGET; T: */ - --old_ptr; - *--new_ptr = OPCODE_goto_w; - *--new_ptr = 3; - *--new_ptr = 0; - *--new_ptr = - reloc->kind; - } - } - if (new_ptr != chunk->data) - abort (); - } - state->code_length = pc; -} - -static void -init_jcf_state (state, work) - struct jcf_partial *state; - struct obstack *work; -{ - state->chunk_obstack = work; - state->first = state->chunk = NULL; - CPOOL_INIT (&state->cpool); - BUFFER_INIT (&state->localvars); - BUFFER_INIT (&state->bytecode); -} - -static void -init_jcf_method (state, method) - struct jcf_partial *state; - tree method; -{ - state->current_method = method; - state->blocks = state->last_block = NULL; - state->linenumber_count = 0; - state->first_lvar = state->last_lvar = NULL; - state->lvar_count = 0; - state->labeled_blocks = NULL; - state->code_length = 0; - BUFFER_RESET (&state->bytecode); - BUFFER_RESET (&state->localvars); - state->code_SP = 0; - state->code_SP_max = 0; - state->handlers = NULL; - state->last_handler = NULL; - state->num_handlers = 0; - state->num_finalizers = 0; - state->return_value_decl = NULL_TREE; -} - -static void -release_jcf_state (state) - struct jcf_partial *state; -{ - CPOOL_FINISH (&state->cpool); - obstack_free (state->chunk_obstack, state->first); -} - -/* Generate and return a list of chunks containing the class CLAS - in the .class file representation. The list can be written to a - .class file using write_chunks. Allocate chunks from obstack WORK. */ - -static struct chunk * -generate_classfile (clas, state) - tree clas; - struct jcf_partial *state; -{ - struct chunk *cpool_chunk; - const char *source_file, *s; - char *ptr; - int i; - char *fields_count_ptr; - int fields_count = 0; - char *methods_count_ptr; - int methods_count = 0; - static tree SourceFile_node = NULL_TREE; - tree part; - int total_supers - = clas == object_type_node ? 0 - : TREE_VEC_LENGTH (TYPE_BINFO_BASETYPES (clas)); - - ptr = append_chunk (NULL, 8, state); - PUT4 (0xCafeBabe); /* Magic number */ - PUT2 (3); /* Minor version */ - PUT2 (45); /* Major version */ - - append_chunk (NULL, 0, state); - cpool_chunk = state->chunk; - - /* Next allocate the chunk containing acces_flags through fields_counr. */ - if (clas == object_type_node) - i = 10; - else - i = 8 + 2 * total_supers; - ptr = append_chunk (NULL, i, state); - i = get_access_flags (TYPE_NAME (clas)); - if (! (i & ACC_INTERFACE)) - i |= ACC_SUPER; - PUT2 (i); /* acces_flags */ - i = find_class_constant (&state->cpool, clas); PUT2 (i); /* this_class */ - if (clas == object_type_node) - { - PUT2(0); /* super_class */ - PUT2(0); /* interfaces_count */ - } - else - { - tree basetypes = TYPE_BINFO_BASETYPES (clas); - tree base = BINFO_TYPE (TREE_VEC_ELT (basetypes, 0)); - int j = find_class_constant (&state->cpool, base); - PUT2 (j); /* super_class */ - PUT2 (total_supers - 1); /* interfaces_count */ - for (i = 1; i < total_supers; i++) - { - base = BINFO_TYPE (TREE_VEC_ELT (basetypes, i)); - j = find_class_constant (&state->cpool, base); - PUT2 (j); - } - } - fields_count_ptr = ptr; - - for (part = TYPE_FIELDS (clas); part; part = TREE_CHAIN (part)) - { - int have_value, attr_count = 0; - if (DECL_NAME (part) == NULL_TREE || DECL_ARTIFICIAL (part)) - continue; - ptr = append_chunk (NULL, 8, state); - i = get_access_flags (part); PUT2 (i); - i = find_utf8_constant (&state->cpool, DECL_NAME (part)); PUT2 (i); - i = find_utf8_constant (&state->cpool, - build_java_signature (TREE_TYPE (part))); - PUT2(i); - have_value = DECL_INITIAL (part) != NULL_TREE - && FIELD_STATIC (part) && CONSTANT_VALUE_P (DECL_INITIAL (part)) - && FIELD_FINAL (part) - && (JPRIMITIVE_TYPE_P (TREE_TYPE (part)) - || TREE_TYPE (part) == string_ptr_type_node); - if (have_value) - attr_count++; - - if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part)) - attr_count++; - - PUT2 (attr_count); /* attributes_count */ - if (have_value) - { - tree init = DECL_INITIAL (part); - static tree ConstantValue_node = NULL_TREE; - if (TREE_TYPE (part) != TREE_TYPE (init)) - fatal_error ("field initializer type mismatch"); - ptr = append_chunk (NULL, 8, state); - if (ConstantValue_node == NULL_TREE) - ConstantValue_node = get_identifier ("ConstantValue"); - i = find_utf8_constant (&state->cpool, ConstantValue_node); - PUT2 (i); /* attribute_name_index */ - PUT4 (2); /* attribute_length */ - i = find_constant_index (init, state); PUT2 (i); - } - /* Emit the "Synthetic" attribute for val$ and this$ fields. */ - if (FIELD_THISN (part) || FIELD_LOCAL_ALIAS (part)) - ptr = append_synthetic_attribute (state); - fields_count++; - } - ptr = fields_count_ptr; UNSAFE_PUT2 (fields_count); - - ptr = methods_count_ptr = append_chunk (NULL, 2, state); - PUT2 (0); - - for (part = TYPE_METHODS (clas); part; part = TREE_CHAIN (part)) - { - struct jcf_block *block; - tree function_body = DECL_FUNCTION_BODY (part); - tree body = function_body == NULL_TREE ? NULL_TREE - : BLOCK_EXPR_BODY (function_body); - tree name = DECL_CONSTRUCTOR_P (part) ? init_identifier_node - : DECL_NAME (part); - tree type = TREE_TYPE (part); - tree save_function = current_function_decl; - int synthetic_p = 0; - current_function_decl = part; - ptr = append_chunk (NULL, 8, state); - i = get_access_flags (part); PUT2 (i); - i = find_utf8_constant (&state->cpool, name); PUT2 (i); - i = find_utf8_constant (&state->cpool, build_java_signature (type)); - PUT2 (i); - i = (body != NULL_TREE) + (DECL_FUNCTION_THROWS (part) != NULL_TREE); - - /* Make room for the Synthetic attribute (of zero length.) */ - if (DECL_FINIT_P (part) - || DECL_INSTINIT_P (part) - || OUTER_FIELD_ACCESS_IDENTIFIER_P (DECL_NAME (part)) - || TYPE_DOT_CLASS (clas) == part) - { - i++; - synthetic_p = 1; - } - - PUT2 (i); /* attributes_count */ - - if (synthetic_p) - ptr = append_synthetic_attribute (state); - - if (body != NULL_TREE) - { - int code_attributes_count = 0; - static tree Code_node = NULL_TREE; - tree t; - char *attr_len_ptr; - struct jcf_handler *handler; - if (Code_node == NULL_TREE) - Code_node = get_identifier ("Code"); - ptr = append_chunk (NULL, 14, state); - i = find_utf8_constant (&state->cpool, Code_node); PUT2 (i); - attr_len_ptr = ptr; - init_jcf_method (state, part); - 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); - generate_bytecode_insns (body, IGNORE_TARGET, state); - if (CAN_COMPLETE_NORMALLY (body)) - { - if (TREE_CODE (TREE_TYPE (type)) != VOID_TYPE) - abort(); - RESERVE (1); - OP1 (OPCODE_return); - } - for (t = DECL_ARGUMENTS (part); t != NULL_TREE; t = TREE_CHAIN (t)) - localvar_free (t, state); - if (state->return_value_decl != NULL_TREE) - localvar_free (state->return_value_decl, state); - finish_jcf_block (state); - perform_relocations (state); - - ptr = attr_len_ptr; - i = 8 + state->code_length + 4 + 8 * state->num_handlers; - if (state->linenumber_count > 0) - { - code_attributes_count++; - i += 8 + 4 * state->linenumber_count; - } - if (state->lvar_count > 0) - { - code_attributes_count++; - i += 8 + 10 * state->lvar_count; - } - UNSAFE_PUT4 (i); /* attribute_length */ - UNSAFE_PUT2 (state->code_SP_max); /* max_stack */ - UNSAFE_PUT2 (localvar_max); /* max_locals */ - UNSAFE_PUT4 (state->code_length); - - /* Emit the exception table. */ - ptr = append_chunk (NULL, 2 + 8 * state->num_handlers, state); - PUT2 (state->num_handlers); /* exception_table_length */ - handler = state->handlers; - for (; handler != NULL; handler = handler->next) - { - int type_index; - PUT2 (handler->start_label->pc); - PUT2 (handler->end_label->pc); - PUT2 (handler->handler_label->pc); - if (handler->type == NULL_TREE) - type_index = 0; - else - type_index = find_class_constant (&state->cpool, - handler->type); - PUT2 (type_index); - } - - ptr = append_chunk (NULL, 2, state); - PUT2 (code_attributes_count); - - /* Write the LineNumberTable attribute. */ - if (state->linenumber_count > 0) - { - static tree LineNumberTable_node = NULL_TREE; - ptr = append_chunk (NULL, - 8 + 4 * state->linenumber_count, state); - if (LineNumberTable_node == NULL_TREE) - LineNumberTable_node = get_identifier ("LineNumberTable"); - i = find_utf8_constant (&state->cpool, LineNumberTable_node); - PUT2 (i); /* attribute_name_index */ - i = 2+4*state->linenumber_count; PUT4(i); /* attribute_length */ - i = state->linenumber_count; PUT2 (i); - for (block = state->blocks; block != NULL; block = block->next) - { - int line = block->linenumber; - if (line > 0) - { - PUT2 (block->pc); - PUT2 (line); - } - } - } - - /* Write the LocalVariableTable attribute. */ - if (state->lvar_count > 0) - { - static tree LocalVariableTable_node = NULL_TREE; - struct localvar_info *lvar = state->first_lvar; - ptr = append_chunk (NULL, 8 + 10 * state->lvar_count, state); - if (LocalVariableTable_node == NULL_TREE) - LocalVariableTable_node = get_identifier("LocalVariableTable"); - i = find_utf8_constant (&state->cpool, LocalVariableTable_node); - PUT2 (i); /* attribute_name_index */ - i = 2 + 10 * state->lvar_count; PUT4 (i); /* attribute_length */ - i = state->lvar_count; PUT2 (i); - for ( ; lvar != NULL; lvar = lvar->next) - { - tree name = DECL_NAME (lvar->decl); - tree sig = build_java_signature (TREE_TYPE (lvar->decl)); - i = lvar->start_label->pc; PUT2 (i); - i = lvar->end_label->pc - i; PUT2 (i); - i = find_utf8_constant (&state->cpool, name); PUT2 (i); - i = find_utf8_constant (&state->cpool, sig); PUT2 (i); - i = DECL_LOCAL_INDEX (lvar->decl); PUT2 (i); - } - } - } - if (DECL_FUNCTION_THROWS (part) != NULL_TREE) - { - tree t = DECL_FUNCTION_THROWS (part); - int throws_count = list_length (t); - static tree Exceptions_node = NULL_TREE; - if (Exceptions_node == NULL_TREE) - Exceptions_node = get_identifier ("Exceptions"); - ptr = append_chunk (NULL, 8 + 2 * throws_count, state); - i = find_utf8_constant (&state->cpool, Exceptions_node); - PUT2 (i); /* attribute_name_index */ - i = 2 + 2 * throws_count; PUT4(i); /* attribute_length */ - i = throws_count; PUT2 (i); - for (; t != NULL_TREE; t = TREE_CHAIN (t)) - { - i = find_class_constant (&state->cpool, TREE_VALUE (t)); - PUT2 (i); - } - } - methods_count++; - current_function_decl = save_function; - } - ptr = methods_count_ptr; UNSAFE_PUT2 (methods_count); - - source_file = DECL_SOURCE_FILE (TYPE_NAME (clas)); - for (s = source_file; ; s++) - { - char ch = *s; - if (ch == '\0') - break; - if (ch == '/' || ch == '\\') - source_file = s+1; - } - ptr = append_chunk (NULL, 10, state); - - i = 1; /* Source file always exists as an attribute */ - if (INNER_CLASS_TYPE_P (clas) || DECL_INNER_CLASS_LIST (TYPE_NAME (clas))) - i++; - if (clas == object_type_node) - i++; - PUT2 (i); /* attributes_count */ - - /* generate the SourceFile attribute. */ - if (SourceFile_node == NULL_TREE) - { - SourceFile_node = get_identifier ("SourceFile"); - ggc_add_tree_root (&SourceFile_node, 1); - } - - i = find_utf8_constant (&state->cpool, SourceFile_node); - PUT2 (i); /* attribute_name_index */ - PUT4 (2); - i = find_utf8_constant (&state->cpool, get_identifier (source_file)); - PUT2 (i); - append_gcj_attribute (state, clas); - append_innerclasses_attribute (state, clas); - - /* New finally generate the contents of the constant pool chunk. */ - i = count_constant_pool_bytes (&state->cpool); - ptr = obstack_alloc (state->chunk_obstack, i); - cpool_chunk->data = ptr; - cpool_chunk->size = i; - write_constant_pool (&state->cpool, ptr, i); - return state->first; -} - -static unsigned char * -append_synthetic_attribute (state) - struct jcf_partial *state; -{ - static tree Synthetic_node = NULL_TREE; - unsigned char *ptr = append_chunk (NULL, 6, state); - int i; - - if (Synthetic_node == NULL_TREE) - { - Synthetic_node = get_identifier ("Synthetic"); - ggc_add_tree_root (&Synthetic_node, 1); - } - i = find_utf8_constant (&state->cpool, Synthetic_node); - PUT2 (i); /* Attribute string index */ - PUT4 (0); /* Attribute length */ - - return ptr; -} - -static void -append_gcj_attribute (state, class) - struct jcf_partial *state; - tree class; -{ - unsigned char *ptr; - int i; - - if (class != object_type_node) - return; - - ptr = append_chunk (NULL, 6, state); /* 2+4 */ - i = find_utf8_constant (&state->cpool, - get_identifier ("gnu.gcj.gcj-compiled")); - PUT2 (i); /* Attribute string index */ - PUT4 (0); /* Attribute length */ -} - -static void -append_innerclasses_attribute (state, class) - struct jcf_partial *state; - tree class; -{ - static tree InnerClasses_node = NULL_TREE; - tree orig_decl = TYPE_NAME (class); - tree current, decl; - int length = 0, i; - unsigned char *ptr, *length_marker, *number_marker; - - if (!INNER_CLASS_TYPE_P (class) && !DECL_INNER_CLASS_LIST (orig_decl)) - return; - - ptr = append_chunk (NULL, 8, state); /* 2+4+2 */ - - if (InnerClasses_node == NULL_TREE) - { - InnerClasses_node = get_identifier ("InnerClasses"); - ggc_add_tree_root (&InnerClasses_node, 1); - } - i = find_utf8_constant (&state->cpool, InnerClasses_node); - PUT2 (i); - length_marker = ptr; PUT4 (0); /* length, to be later patched */ - number_marker = ptr; PUT2 (0); /* number of classes, tblp */ - - /* Generate the entries: all inner classes visible from the one we - process: itself, up and down. */ - while (class && INNER_CLASS_TYPE_P (class)) - { - const char *n; - - decl = TYPE_NAME (class); - n = IDENTIFIER_POINTER (DECL_NAME (decl)) + - IDENTIFIER_LENGTH (DECL_NAME (decl)); - - while (n[-1] != '$') - n--; - append_innerclasses_attribute_entry (state, decl, get_identifier (n)); - length++; - - class = TREE_TYPE (DECL_CONTEXT (TYPE_NAME (class))); - } - - decl = orig_decl; - for (current = DECL_INNER_CLASS_LIST (decl); - current; current = TREE_CHAIN (current)) - { - append_innerclasses_attribute_entry (state, TREE_PURPOSE (current), - TREE_VALUE (current)); - length++; - } - - ptr = length_marker; PUT4 (8*length+2); - ptr = number_marker; PUT2 (length); -} - -static void -append_innerclasses_attribute_entry (state, decl, name) - struct jcf_partial *state; - tree decl, name; -{ - int icii, icaf; - int ocii = 0, ini = 0; - unsigned char *ptr = append_chunk (NULL, 8, state); - - icii = find_class_constant (&state->cpool, TREE_TYPE (decl)); - - /* Sun's implementation seems to generate ocii to 0 for inner - classes (which aren't considered members of the class they're - in.) The specs are saying that if the class is anonymous, - inner_name_index must be zero. */ - if (!ANONYMOUS_CLASS_P (TREE_TYPE (decl))) - { - ocii = find_class_constant (&state->cpool, - TREE_TYPE (DECL_CONTEXT (decl))); - ini = find_utf8_constant (&state->cpool, name); - } - icaf = get_access_flags (decl); - - PUT2 (icii); PUT2 (ocii); PUT2 (ini); PUT2 (icaf); -} - -static char * -make_class_file_name (clas) - tree clas; -{ - const char *dname, *cname, *slash; - char *r; - struct stat sb; - - cname = IDENTIFIER_POINTER (identifier_subst (DECL_NAME (TYPE_NAME (clas)), - "", '.', DIR_SEPARATOR, - ".class")); - if (jcf_write_base_directory == NULL) - { - /* Make sure we put the class file into the .java file's - directory, and not into some subdirectory thereof. */ - char *t; - dname = DECL_SOURCE_FILE (TYPE_NAME (clas)); - slash = strrchr (dname, DIR_SEPARATOR); - if (! slash) - { - dname = "."; - slash = dname + 1; - } - t = strrchr (cname, DIR_SEPARATOR); - if (t) - cname = t + 1; - } - else - { - dname = jcf_write_base_directory; - slash = dname + strlen (dname); - } - - r = xmalloc (slash - dname + strlen (cname) + 2); - strncpy (r, dname, slash - dname); - r[slash - dname] = DIR_SEPARATOR; - strcpy (&r[slash - dname + 1], cname); - - /* We try to make new directories when we need them. We only do - this for directories which "might not" exist. For instance, we - assume the `-d' directory exists, but we don't assume that any - subdirectory below it exists. It might be worthwhile to keep - track of which directories we've created to avoid gratuitous - stat()s. */ - dname = r + (slash - dname) + 1; - while (1) - { - char *s = strchr (dname, DIR_SEPARATOR); - 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); - - *s = DIR_SEPARATOR; - /* Skip consecutive separators. */ - for (dname = s + 1; *dname && *dname == DIR_SEPARATOR; ++dname) - ; - } - - return r; -} - -/* Write out the contens of a class (RECORD_TYPE) CLAS, as a .class file. - The output .class file name is make_class_file_name(CLAS). */ - -void -write_classfile (clas) - tree clas; -{ - struct obstack *work = &temporary_obstack; - struct jcf_partial state[1]; - char *class_file_name = make_class_file_name (clas); - struct chunk *chunks; - - if (class_file_name != NULL) - { - FILE *stream; - char *temporary_file_name; - - /* The .class file is initially written to a ".tmp" file so that - if multiple instances of the compiler are running at once - they do not see partially formed class files. */ - 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); - - 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); - if (rename (temporary_file_name, class_file_name) == -1) - { - remove (temporary_file_name); - fatal_io_error ("can't create %s", class_file_name); - } - free (temporary_file_name); - free (class_file_name); - } - release_jcf_state (state); -} - -/* TODO: - string concatenation - synchronized statement - */