diff options
Diffstat (limited to 'gcc/lto-section-out.c')
-rw-r--r-- | gcc/lto-section-out.c | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/gcc/lto-section-out.c b/gcc/lto-section-out.c new file mode 100644 index 00000000000..33d9e2372bd --- /dev/null +++ b/gcc/lto-section-out.c @@ -0,0 +1,265 @@ +/* LTO output code. + Copyright (C) 2007, 2008 Free Software Foundation, Inc. + Contributed by Kenneth Zadeck <zadeck@naturalbridge.com> + +This file is part of GCC. + +GCC 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 3, or (at your option) any later +version. + +GCC 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 GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "toplev.h" +#include "tree.h" +#include "expr.h" +#include "flags.h" +#include "params.h" +#include "input.h" +#include "varray.h" +#include "hashtab.h" +#include "langhooks.h" +#include "basic-block.h" +#include "tree-iterator.h" +#include "tree-pass.h" +#include "tree-flow.h" +#include "cgraph.h" +#include "function.h" +#include "ggc.h" +#include "diagnostic.h" +#include "except.h" +#include "debug.h" +#include "vec.h" +#include "tree-vectorizer.h" +#include "timevar.h" +#include "dwarf2asm.h" +#include "dwarf2out.h" +#include "output.h" +#include "lto-tags.h" +#include "lto-section-out.h" +#include <ctype.h> + +/* Write all of the chars in OBS to the assembler. Recycle the blocks + in obs as this is being done. */ + +void +lto_write_stream (struct lto_output_stream *obs) +{ + unsigned int block_size = 1024; + unsigned int num_chars; + struct lto_char_ptr_base *block; + if (!obs->first_block) + return; + + block = obs->first_block; + while (block) + { + const char *base = ((char *)block) + sizeof (struct lto_char_ptr_base); + struct lto_char_ptr_base *old_block = block; + block = (struct lto_char_ptr_base *)block->ptr; + /* If there is a next block, then this one is full, if there is + not a next block, then the left_in_block field says how many + chars there are in this block. */ + num_chars = block_size - sizeof (struct lto_char_ptr_base); + if (!block) + num_chars = num_chars - obs->left_in_block; + + assemble_string (base, num_chars); + free (old_block); + block_size *= 2; + } +} + + +/* Write a character to the output block. */ + +void +lto_output_1_stream (struct lto_output_stream *obs, char c) +{ + /* No space left. */ + if (obs->left_in_block == 0) + { + struct lto_char_ptr_base *new_block; + + if (obs->first_block == NULL) + { + /* This is the first time the stream has been written + into. */ + obs->block_size = 1024; + new_block = (struct lto_char_ptr_base*) xmalloc (obs->block_size); + obs->first_block = new_block; + } + else + { + struct lto_char_ptr_base *tptr; + /* Get a new block that is twice as big as the last block + and link it into the list. */ + obs->block_size *= 2; + new_block = (struct lto_char_ptr_base*) xmalloc (obs->block_size); + /* The first bytes of the block are reserved as a pointer to + the next block. Set the chain of the full block to the + pointer to the new block. */ + tptr = obs->current_block; + tptr->ptr = (char *)new_block; + } + + /* Set the place for the next char at the first position after the + chain to the next block. */ + obs->current_pointer + = ((char *)new_block) + sizeof (struct lto_char_ptr_base); + obs->current_block = new_block; + /* Null out the newly allocated block's pointer to the next block. */ + new_block->ptr = NULL; + obs->left_in_block = obs->block_size - sizeof (struct lto_char_ptr_base); + } + + /* Write the actual character. */ + *obs->current_pointer = c; + obs->current_pointer++; + obs->total_size++; + obs->left_in_block--; +} + + +/* Output an unsigned LEB128 quantity to OBS. */ + +void +lto_output_uleb128_stream (struct lto_output_stream *obs, unsigned HOST_WIDE_INT work) +{ + LTO_DEBUG_WIDE ("U", work); + do + { + unsigned int byte = (work & 0x7f); + work >>= 7; + if (work != 0) + /* More bytes to follow. */ + byte |= 0x80; + + lto_output_1_stream (obs, byte); + } + while (work != 0); +} + +/* Identical to output_uleb128_stream above except using unsigned + HOST_WIDEST_INT type. For efficiency on host where unsigned HOST_WIDEST_INT + is not native, we only use this if we know that HOST_WIDE_INT is not wide + enough. */ + +void +lto_output_widest_uint_uleb128_stream (struct lto_output_stream *obs, + unsigned HOST_WIDEST_INT work) +{ + LTO_DEBUG_WIDE ("U", work); + do + { + unsigned int byte = (work & 0x7f); + work >>= 7; + if (work != 0) + /* More bytes to follow. */ + byte |= 0x80; + + lto_output_1_stream (obs, byte); + } + while (work != 0); +} + + +/* Output a signed LEB128 quantity. */ + +void +lto_output_sleb128_stream (struct lto_output_stream *obs, HOST_WIDE_INT work) +{ + int more, byte; + LTO_DEBUG_WIDE ("S", work); + do + { + byte = (work & 0x7f); + /* arithmetic shift */ + work >>= 7; + more = !((work == 0 && (byte & 0x40) == 0) + || (work == -1 && (byte & 0x40) != 0)); + if (more) + byte |= 0x80; + + lto_output_1_stream (obs, byte); + } + while (more); +} + + +/* Put out a integer constant. These are stored as two HOST_WIDE_INTS + so games may have to be played to shift the data from the high to + the low value. */ + +void +lto_output_integer_stream (struct lto_output_stream *obs, tree t) +{ + HOST_WIDE_INT low = TREE_INT_CST_LOW (t); + HOST_WIDE_INT high = TREE_INT_CST_HIGH (t); + int more, byte; + + /* Of course if the high value is just sign bits for the signed low + value, we can just punt and call lto_output_sleb128 and be done with + it. */ + if (((high == -1) && (low < 0)) + || ((high == 0) && (low >= 0))) + { + lto_output_sleb128_stream (obs, low); + return; + } + + LTO_DEBUG_INTEGER ("SS", high, low); + + /* This is just a copy of the lto_output_sleb128 code with extra + operations to transfer the low 7 bits of the high value to the + top 7 bits of the low value, shift the high down by 7 and then do + a slightly more complex exit test. */ + do + { + unsigned HOST_WIDE_INT transfer = (high & 0x7f); + high = ((unsigned HOST_WIDE_INT) high) >> 7; + transfer <<= (HOST_BITS_PER_WIDE_INT - 7); + + byte = (low & 0x7f); + + /* Logical shift. */ + low = ((unsigned HOST_WIDE_INT) low) >> 7; + low |= transfer; + more = !((high == 0 && low == 0 && (byte & 0x40) == 0) + || (high == -1 && low == -1 && (byte & 0x40) != 0)); + if (more) + byte |= 0x80; + + lto_output_1_stream (obs, byte); + } + while (more); +} + +#ifdef LTO_STREAM_DEBUGGING + +struct lto_debug_context lto_debug_context; + +/* The low level output routine to print a single character to the + debugging stream. */ + +void +lto_debug_out_fun (struct lto_debug_context *context, char c) +{ + struct lto_output_stream * stream + = (struct lto_output_stream *)context->current_data; + lto_output_1_stream (stream, c); +} + +#endif |