/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_VM_C1_C1_LINEARSCAN_HPP
#define SHARE_VM_C1_C1_LINEARSCAN_HPP
#include "c1/c1_FpuStackSim.hpp"
#include "c1/c1_FrameMap.hpp"
#include "c1/c1_Instruction.hpp"
#include "c1/c1_LIR.hpp"
#include "c1/c1_LIRGenerator.hpp"
class DebugInfoCache;
class FpuStackAllocator;
class IRScopeDebugInfo;
class Interval;
class IntervalWalker;
class LIRGenerator;
class LinearScan;
class MoveResolver;
class Range;
enum IntervalUseKind {
// priority of use kinds must be ascending
noUse = 0,
};
enum IntervalKind {
};
// during linear scan an interval is in one of four states in
enum IntervalState {
};
enum IntervalSpillState {
// Note: two consecutive definitions are treated as one (e.g. consecutive move and add because of two-operand LIR form)
// the position of this definition is stored in _definition_pos
storeAtDefinition, // the interval should be stored immediately after its definition because otherwise
// there would be multiple redundant stores
startInMemory, // the interval starts in memory (e.g. method parameter), so a store is never necessary
noOptimization // the interval has more then one definition (e.g. resulting from phi moves), so stores to memory are not optimized
};
for (LIR_OpVisitState::OprMode mode = LIR_OpVisitState::firstMode; mode < LIR_OpVisitState::numModes; mode = (LIR_OpVisitState::OprMode)(mode + 1))
// declare classes used by LinearScan as friends because they
// need a wide variety of functions declared here
//
// Only the small interface to the rest of the compiler is public
friend class Interval;
friend class IntervalWalker;
friend class LinearScanWalker;
friend class FpuStackAllocator;
friend class MoveResolver;
friend class LinearScanStatistic;
friend class LinearScanTimers;
friend class RegisterVerifier;
public:
enum {
};
private:
BlockList _cached_blocks; // cached list with all blocks in linear-scan order (only correct if original list keeps unchanged)
int _num_virtual_regs; // number of virtual registers (without new registers introduced because of splitting intervals)
bool _has_fpu_registers; // true if this method uses any floating point registers (and so fpu stack allocation is necessary)
int _unused_spill_slot; // unused spill slot for a single-word value because of alignment of a double-word value
IntervalList* _new_intervals_from_allocation; // list with all intervals created during allocation when an existing interval is split
bool _needs_full_resort; // set to true if an Interval::from() is changed and _sorted_intervals must be resorted
BlockBeginArray _block_of_op; // mapping from LIR_Op id to the BlockBegin containing this instruction
// cached debug info to prevent multiple creation of same object
// TODO: cached scope values for registers could be static
// accessors
// unified bailout support
// access to block list (sorted in linear scan order)
int block_count() const { assert(_cached_blocks.length() == ir()->linear_scan_order()->length(), "invalid cached block list"); return _cached_blocks.length(); }
BlockBegin* block_at(int idx) const { assert(_cached_blocks.at(idx) == ir()->linear_scan_order()->at(idx), "invalid cached block list"); return _cached_blocks.at(idx); }
// size of live_in and live_out sets of BasicBlocks (BitMap needs rounded size for iteration)
bool is_interval_in_loop(int interval, int loop) const { return _interval_in_loop.at(interval, loop); }
// handling of fpu stack allocation (platform dependent, needed for debug information generation)
#ifdef X86
#else
bool use_fpu_stack_allocation() const { return false; }
#endif
// access to interval list
// access to LIR_Ops and Blocks indexed by op_id
int max_lir_op_id() const { assert(_lir_ops.length() > 0, "no operations"); return (_lir_ops.length() - 1) << 1; }
LIR_Op* lir_op_with_id(int op_id) const { assert(op_id >= 0 && op_id <= max_lir_op_id() && op_id % 2 == 0, "op_id out of range or not even"); return _lir_ops.at(op_id >> 1); }
BlockBegin* block_of_op_with_id(int op_id) const { assert(_block_of_op.length() > 0 && op_id >= 0 && op_id <= max_lir_op_id() + 1, "op_id out of range"); return _block_of_op.at(op_id >> 1); }
bool is_block_begin(int op_id) { return op_id == 0 || block_of_op_with_id(op_id) != block_of_op_with_id(op_id - 1); }
bool covers_block_begin(int op_id_1, int op_id_2) { return block_of_op_with_id(op_id_1) != block_of_op_with_id(op_id_2); }
bool has_call(int op_id) { assert(op_id % 2 == 0, "must be even"); return _has_call.at(op_id >> 1); }
bool has_info(int op_id) { assert(op_id % 2 == 0, "must be even"); return _has_info.at(op_id >> 1); }
// functions for converting LIR-Operands to register numbers
// functions for classification of intervals
static bool is_precolored_interval(const Interval* i);
static bool is_virtual_interval(const Interval* i);
static bool is_precolored_cpu_interval(const Interval* i);
static bool is_virtual_cpu_interval(const Interval* i);
static bool is_precolored_fpu_interval(const Interval* i);
static bool is_virtual_fpu_interval(const Interval* i);
static bool is_in_fpu_register(const Interval* i);
static bool is_oop_interval(const Interval* i);
// General helper functions
int allocate_spill_slot(bool double_word);
void propagate_spill_slots();
// platform dependent functions
static bool is_processed_reg_num(int reg_num);
static bool is_caller_save(int assigned_reg);
// spill move optimization: eliminate moves from register to stack if
// stack slot is known to be correct
static bool must_store_at_definition(const Interval* i);
void eliminate_spill_moves();
// Phase 1: number all instructions in all blocks
void number_instructions();
// Phase 2: compute local live sets separately for each block
// (sets live_gen and live_kill for each block)
//
// helper methods used by compute_local_live_sets()
void compute_local_live_sets();
// Phase 3: perform a backward dataflow analysis to compute global live sets
// (sets live_in and live_out for each block)
void compute_global_live_sets();
// Phase 4: build intervals
// (fills the list _intervals)
//
// helper methods used by build_intervals()
// Add platform dependent kills for particular LIR ops. Can be used
// to add platform dependent behaviour for some operations.
void build_intervals();
// Phase 5: actual register allocation
// (Uses LinearScanWalker)
//
// helper functions for building a sorted list of intervals
void create_unhandled_lists(Interval** list1, Interval** list2, bool (is_list1)(const Interval* i), bool (is_list2)(const Interval* i));
void sort_intervals_before_allocation();
void sort_intervals_after_allocation();
void allocate_registers();
// Phase 6: resolve data flow
// (insert moves at edges between blocks if intervals have been split)
//
// helper functions for resolve_data_flow()
void resolve_collect_mappings(BlockBegin* from_block, BlockBegin* to_block, MoveResolver &move_resolver);
void resolve_find_insert_pos(BlockBegin* from_block, BlockBegin* to_block, MoveResolver &move_resolver);
void resolve_data_flow();
void resolve_exception_edge(XHandler* handler, int throwing_op_id, int reg_num, Phi* phi, MoveResolver &move_resolver);
void resolve_exception_handlers();
// Phase 7: assign register numbers back to LIR
// (includes computation of debug information and oop maps)
//
// helper functions for assign_reg_num()
// methods used for oop map computation
// methods used for debug information computation
void init_compute_debug_info();
} else {
bailout("illegal oopMap register name");
}
}
IRScopeDebugInfo* compute_debug_info_for_scope(int op_id, IRScope* cur_scope, ValueStack* cur_state, ValueStack* innermost_state);
void assign_reg_num();
// Phase 8: fpu stack allocation
// (Used only on x86 when fpu operands are present)
void allocate_fpu_stack();
// helper functions for printing state
#ifndef PRODUCT
void print_intervals(const char* label);
#endif
#ifdef ASSERT
// verification functions for allocation
// (check that all intervals have a correct register and that no registers are overwritten)
void verify();
void verify_intervals();
void verify_constants();
void verify_registers();
#endif
public:
// creation
// main entry function: perform linear scan register allocation
void do_linear_scan();
// accessors used by Compilation
// entry functions for printing
#ifndef PRODUCT
static void print_statistics();
static void print_timers(double total);
#endif
};
// Helper class for ordering moves that are inserted at the same position in the LIR
// When moves between registers are inserted, it is important that the moves are
// ordered such that no register is overwritten. So moves from register to stack
// are processed prior to moves from stack to register. When moves have circular
// dependencies, a temporary stack slot is used to break the circle.
// The same logic is used in the LinearScanWalker and in LinearScan during resolve_data_flow
// and therefore factored out in a separate class
private:
int _insert_idx;
bool _multiple_reads_allowed;
int register_blocked(int reg) { assert(reg >= 0 && reg < LinearScan::nof_regs, "out of bounds"); return _register_blocked[reg]; }
void set_register_blocked(int reg, int direction) { assert(reg >= 0 && reg < LinearScan::nof_regs, "out of bounds"); assert(direction == 1 || direction == -1, "out of bounds"); _register_blocked[reg] += direction; }
void append_insertion_buffer();
DEBUG_ONLY(void verify_before_resolve();)
void resolve_mappings();
public:
DEBUG_ONLY(void check_empty();)
void resolve_and_append_moves();
};
friend class Interval;
private:
// used only by class Interval, so hide them
int intersects_at(Range* r) const;
public:
// for testing
};
// Interval is an ordered list of disjoint ranges.
// For pre-colored double word LIR_Oprs, one interval is created for
// the low word register and one is created for the hi word register.
// On Intel for FPU double registers only one interval is created. At
// all times assigned_reg contains the reg. number of the physical
// register.
// For LIR_Opr in virtual registers a single interval can represent
// single and double word values. When a physical register is
// assigned to the interval, assigned_reg contains the
// phys. reg. number and for double word values assigned_regHi the
// phys. reg. number of the hi word if there is any. For spilled
// intervals assigned_reg contains the stack index. assigned_regHi is
// always -1.
private:
int _reg_num;
int _assigned_reg;
int _assigned_regHi;
IntervalList _split_children; // list of all intervals that are split off from this interval (only available for split parents)
Interval* _current_split_child; // the current split child that has been active or inactive last (always stored in split parents)
int _canonical_spill_slot; // the stack slot where all split parts of this interval are spilled to (always stored in split parents)
bool _insert_move_when_activated; // true if move is inserted between _current_split_child and this interval when interval gets active the first time
int calc_to();
public:
// accessors
BasicType type() const { assert(_reg_num == -1 || _reg_num >= LIR_OprDesc::vreg_base, "cannot access type for fixed interval"); return _type; }
void set_type(BasicType type) { assert(_reg_num < LIR_OprDesc::vreg_base || _type == T_ILLEGAL || _type == type, "overwriting existing type"); _type = type; }
int to() { if (_cached_to == -1) _cached_to = calc_to(); assert(_cached_to == calc_to(), "invalid cached value"); return _cached_to; }
// access to split parent and split children
Interval* split_parent() const { assert(_split_parent->is_split_parent(), "must be"); return _split_parent; }
DEBUG_ONLY(void check_split_children();)
// information stored in split parent, but available for all children
void set_canonical_spill_slot(int slot) { assert(split_parent()->_canonical_spill_slot == -1, "overwriting existing value"); split_parent()->_canonical_spill_slot = slot; }
// for spill optimization
void set_spill_state(IntervalSpillState state) { assert(state >= spill_state(), "state cannot decrease"); split_parent()->_spill_state = state; }
void set_spill_definition_pos(int pos) { assert(spill_definition_pos() == -1, "cannot set the position twice"); split_parent()->_spill_definition_pos = pos; }
// returns true if this interval has a shadow copy on the stack that is always correct
bool always_in_memory() const { return split_parent()->_spill_state == storeAtDefinition || split_parent()->_spill_state == startInMemory; }
// caching of values that take time to compute and are used multiple times
// access to use positions
int first_usage(IntervalUseKind min_use_kind) const; // id of the first operation requiring this interval in a register
int next_usage(IntervalUseKind min_use_kind, int from) const; // id of next usage seen from the given position
// manipulating intervals
// test intersection
// range iteration
// printing
};
protected:
Interval* _unhandled_first[nofKinds]; // sorted list of intervals, not life before the current position
Interval* _inactive_first [nofKinds]; // sorted list of intervals, intervals in a life time hole at the current position
// unified bailout support
void check_bounds(IntervalKind kind) { assert(kind >= fixedKind && kind <= anyKind, "invalid interval_kind"); }
Interval** unhandled_first_addr(IntervalKind kind) { check_bounds(kind); return &_unhandled_first[kind]; }
Interval** active_first_addr(IntervalKind kind) { check_bounds(kind); return &_active_first[kind]; }
Interval** inactive_first_addr(IntervalKind kind) { check_bounds(kind); return &_inactive_first[kind]; }
void remove_from_list(Interval* i);
void next_interval();
// activate_current() is called when an unhandled interval becomes active (in current(), current_kind()).
// Return false if current() should not be moved the the active interval list.
// It is safe to append current to any interval list but the unhandled list.
virtual bool activate_current() { return true; }
// interval_moved() is called whenever an interval moves from one interval list to another.
// In the implementation of this method it is prohibited to move the interval to any list.
virtual void interval_moved(Interval* interval, IntervalKind kind, IntervalState from, IntervalState to);
public:
IntervalWalker(LinearScan* allocator, Interval* unhandled_fixed_first, Interval* unhandled_any_first);
// active contains the intervals that are live after the lir_op
// active contains the intervals that are live before the lir_op
// walk through all intervals
};
// The actual linear scan register allocator
enum {
};
private:
// accessors mapped to same functions in class LinearScan
BlockBegin* block_of_op_with_id(int op_id) const { return allocator()->block_of_op_with_id(op_id); }
void init_use_lists(bool only_process_use_pos);
void exclude_from_use(int reg);
void exclude_from_use(Interval* i);
void free_exclude_active_fixed();
void free_exclude_active_any();
void spill_exclude_active_fixed();
void spill_collect_active_any();
int find_optimal_split_pos(Interval* it, int min_split_pos, int max_split_pos, bool do_loop_optimization);
int find_free_reg(int reg_needed_until, int interval_to, int hint_reg, int ignore_reg, bool* need_split);
int find_locked_reg(int reg_needed_until, int interval_to, int hint_reg, int ignore_reg, bool* need_split);
void update_phys_reg_range(bool requires_cpu_register);
bool activate_current();
public:
LinearScanWalker(LinearScan* allocator, Interval* unhandled_fixed_first, Interval* unhandled_any_first);
// must be called when all intervals are allocated
};
/*
When a block has more than one predecessor, and all predecessors end with
the same sequence of move-instructions, than this moves can be placed once
at the beginning of the block instead of multiple times in the predecessors.
Similarly, when a block has more than one successor, then equal sequences of
moves at the beginning of the successors can be placed once at the end of
the block. But because the moves must be inserted before all branch
instructions, this works only when there is exactly one conditional branch
at the end of the block (because the moves must be inserted before all
branches, but after all compares).
This optimization affects all kind of moves (reg->reg, reg->stack and
stack->reg). Because this optimization works best when a block contains only
few moves, it has a huge impact on the number of blocks that are totally
empty.
*/
private:
// the class maintains a list with all lir-instruction-list of the
// successors (predecessors) and the current index into the lir-lists
void init_instructions();
public:
};
private:
enum {
};
public:
};
#ifndef PRODUCT
// Helper class for collecting statistics of LinearScan
public:
enum Counter {
// general counters
// counter for classes of lir instructions
// counter for different types of moves
};
private:
const char* counter_name(int counter_idx);
public:
};
// Helper class for collecting compilation time of LinearScan
public:
enum Timer {
};
private:
const char* timer_name(int idx);
public:
void begin_method(); // called for each method when register allocation starts
void end_method(LinearScan* allocator); // called for each method when register allocation completed
};
#endif // ifndef PRODUCT
// Pick up platform-dependent implementation details
#ifdef TARGET_ARCH_x86
# include "c1_LinearScan_x86.hpp"
#endif
#ifdef TARGET_ARCH_sparc
# include "c1_LinearScan_sparc.hpp"
#endif
#ifdef TARGET_ARCH_arm
# include "c1_LinearScan_arm.hpp"
#endif
#ifdef TARGET_ARCH_ppc
# include "c1_LinearScan_ppc.hpp"
#endif
#endif // SHARE_VM_C1_C1_LINEARSCAN_HPP