// icf.h --  Identical Code Folding

// Copyright (C) 2009-2018 Free Software Foundation, Inc.
// Written by Sriraman Tallam <tmsriram@google.com>.

// This file is part of gold.

// This program 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 of the License, or
// (at your option) any later version.

// This program 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 this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
// MA 02110-1301, USA.

#ifndef GOLD_ICF_H
#define GOLD_ICF_H

#include <vector>

#include "elfcpp.h"
#include "symtab.h"
#include "object.h"

namespace gold
{

class Object;
class Input_objects;
class Symbol_table;

class Icf
{
 public:
  typedef std::vector<Section_id> Sections_reachable_info;
  typedef std::vector<Symbol*> Symbol_info;
  typedef std::vector<std::pair<long long, long long> > Addend_info;
  typedef std::vector<uint64_t> Offset_info;
  typedef std::vector<unsigned int> Reloc_addend_size_info;
  typedef Unordered_map<Section_id,
                        unsigned int,
                        Section_id_hash> Uniq_secn_id_map;
  typedef Unordered_set<Section_id, Section_id_hash> Secn_fptr_taken_set;

  typedef struct
  {
    // This stores the section corresponding to the reloc.
    Sections_reachable_info section_info;
    // This stores the symbol corresponding to the reloc.
    Symbol_info symbol_info;
    // This stores the symbol value and the addend for a reloc.
    Addend_info addend_info;
    Offset_info offset_info;
    Reloc_addend_size_info reloc_addend_size_info;
  } Reloc_info;

  typedef Unordered_map<Section_id, Reloc_info,
                        Section_id_hash> Reloc_info_list;

  Icf()
  : id_section_(), section_id_(), kept_section_id_(),
    fptr_section_id_(),
    icf_ready_(false),
    reloc_info_list_()
  { }

  // Returns the kept folded identical section corresponding to
  // dup_obj and dup_shndx.
  Section_id
  get_folded_section(Relobj* dup_obj, unsigned int dup_shndx);

  // Forms groups of identical sections where the first member
  // of each group is the kept section during folding.
  void
  find_identical_sections(const Input_objects* input_objects,
                          Symbol_table* symtab);

  // This is set when ICF has been run and the groups of
  // identical sections have been formed.
  void
  icf_ready()
  { this->icf_ready_ = true; }

  // Returns true if ICF has been run.
  bool
  is_icf_ready()
  { return this->icf_ready_; }

  // Unfolds the section denoted by OBJ and SHNDX if folded.
  void
  unfold_section(Relobj* obj, unsigned int shndx);

  // Returns the kept section corresponding to the
  // given section.
  bool
  is_section_folded(Relobj* obj, unsigned int shndx);

  // Given an object and a section index, this returns true if the
  // pointer of the function defined in this section is taken.
  bool
  section_has_function_pointers(Relobj* obj, unsigned int shndx)
  {
    return (this->fptr_section_id_.find(Section_id(obj, shndx))
            != this->fptr_section_id_.end());
  }

  // Records that a pointer of the function defined in this section
  // is taken.
  void
  set_section_has_function_pointers(Relobj* obj, unsigned int shndx)
  {
    this->fptr_section_id_.insert(Section_id(obj, shndx));
  }

  // Checks if the section_name should be searched for relocs
  // corresponding to taken function pointers.  Ignores eh_frame
  // and vtable sections.
  inline bool
  check_section_for_function_pointers(const std::string& section_name,
                                      Target* target)
  {
    return (parameters->options().icf_safe_folding()
	    && target->can_check_for_function_pointers()
	    && target->section_may_have_icf_unsafe_pointers(
	        section_name.c_str()));
  }

  // Returns a map of a section to info (Reloc_info) about its relocations.
  Reloc_info_list&
  reloc_info_list()
  { return this->reloc_info_list_; }

  // Returns a mapping of each section to a unique integer.
  Uniq_secn_id_map&
  section_to_int_map()
  { return this->section_id_; }

 private:

  // Maps integers to sections.
  std::vector<Section_id> id_section_;
  // Does the reverse.
  Uniq_secn_id_map section_id_;
  // Given a section id, this maps it to the id of the kept
  // section.  If the id's are the same then this section is
  // not folded.
  std::vector<unsigned int> kept_section_id_;
  // Given a section id, this says if the pointer to this
  // function is taken in which case it is dangerous to fold
  // this function.
  Secn_fptr_taken_set fptr_section_id_;
  // Flag to indicate if ICF has been run.
  bool icf_ready_;
  // This list is populated by gc_process_relocs in gc.h.
  Reloc_info_list reloc_info_list_;
};

// This function returns true if this section corresponds to a function that
// should be considered by icf as a possible candidate for folding.  Some
// earlier gcc versions, like 4.0.3, put constructors and destructors in
// .gnu.linkonce.t sections and hence should be included too.
inline bool
is_section_foldable_candidate(const std::string& section_name)
{
  const char* section_name_cstr = section_name.c_str();
  return (is_prefix_of(".text", section_name_cstr)
          || is_prefix_of(".gnu.linkonce.t", section_name_cstr));
}

} // End of namespace gold.

#endif
