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,
loopEndMarker = 1,
shouldHaveRegister = 2,
mustHaveRegister = 3,
firstValidKind = 1,
lastValidKind = 3
};
enum IntervalKind {
fixedKind = 0, // interval pre-colored by LIR_Generator
anyKind = 1, // no register/memory allocated by LIR_Generator
nofKinds,
firstKind = fixedKind
};
// during linear scan an interval is in one of four states in enum IntervalState {
unhandledState = 0, // unhandled state (not processed yet)
activeState = 1, // life and is in a physical register
inactiveState = 2, // in a life time hole and is in a physical register
handledState = 3, // spilled or not life again
invalidState = -1
};
enum IntervalSpillState {
noDefinitionFound, // starting state of calculation: no definition found yet
oneDefinitionFound, // one definition has already been found. // 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
oneMoveInserted, // one spill move has already been inserted.
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
};
class LinearScan : public CompilationResourceObj { // 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 friendclass Interval; friendclass IntervalWalker; friendclass LinearScanWalker; friendclass FpuStackAllocator; friendclass MoveResolver; friendclass LinearScanStatistic; friendclass LinearScanTimers; friendclass RegisterVerifier;
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 _num_calls; // total number of calls in this method int _max_spills; // number of stack slots used for intervals allocated to memory int _unused_spill_slot; // unused spill slot for a single-word value because of alignment of a double-word value
IntervalList _intervals; // mapping from register number to interval
IntervalList* _new_intervals_from_allocation; // list with all intervals created during allocation when an existing interval is split
IntervalArray* _sorted_intervals; // intervals sorted by Interval::from() bool _needs_full_resort; // set to true if an Interval::from() is changed and _sorted_intervals must be resorted
LIR_OpArray _lir_ops; // mapping from LIR_Op id to LIR_Op node
BlockBeginArray _block_of_op; // mapping from LIR_Op id to the BlockBegin containing this instruction
ResourceBitMap _has_info; // bit set for each LIR_Op id that has a CodeEmitInfo
ResourceBitMap _has_call; // bit set for each LIR_Op id that destroys all caller save registers
BitMap2D _interval_in_loop; // bit set for each virtual register that is contained in each loop
// cached debug info to prevent multiple creation of same object // TODO: cached scope values for registers could be static
ScopeValueArray _scope_value_cache;
// spill move optimization: eliminate moves from register to stack if // stack slot is known to be correct void change_spill_definition_pos(Interval* interval, int def_pos); void change_spill_state(Interval* interval, int spill_pos); staticbool 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 set_live_gen_kill(Value value, LIR_Op* op, BitMap& live_gen, BitMap& live_kill);
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() void add_use (Value value, int from, int to, IntervalUseKind use_kind);
void add_def (LIR_Opr opr, int def_pos, IntervalUseKind use_kind); void add_use (LIR_Opr opr, int from, int to, IntervalUseKind use_kind); void add_temp(LIR_Opr opr, int temp_pos, IntervalUseKind use_kind);
void add_def (int reg_num, int def_pos, IntervalUseKind use_kind, BasicType type); void add_use (int reg_num, int from, int to, IntervalUseKind use_kind, BasicType type); void add_temp(int reg_num, int temp_pos, IntervalUseKind use_kind, BasicType type);
// Add platform dependent kills for particular LIR ops. Can be used // to add platform dependent behaviour for some operations. void pd_add_temps(LIR_Op* op);
// Phase 6: resolve data flow // (insert moves at edges between blocks if intervals have been split) // // helper functions for resolve_data_flow()
Interval* split_child_at_op_id(Interval* interval, int op_id, LIR_OpVisitState::OprMode mode);
Interval* interval_at_block_begin(BlockBegin* block, int reg_num);
Interval* interval_at_block_end(BlockBegin* block, int reg_num);
Interval* interval_at_op_id(int reg_num, int op_id); 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_entry(BlockBegin* block, int reg_num, MoveResolver &move_resolver); void resolve_exception_entry(BlockBegin* block, MoveResolver &move_resolver); void resolve_exception_edge(XHandler* handler, int throwing_op_id, int reg_num, Phi* phi, MoveResolver &move_resolver); void resolve_exception_edge(XHandler* handler, int throwing_op_id, 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()
VMReg vm_reg_for_interval(Interval* interval);
VMReg vm_reg_for_operand(LIR_Opr opr);
int append_scope_value_for_constant(LIR_Opr opr, GrowableArray<ScopeValue*>* scope_values); int append_scope_value_for_operand(LIR_Opr opr, GrowableArray<ScopeValue*>* scope_values); int append_scope_value(int op_id, Value value, GrowableArray<ScopeValue*>* scope_values);
#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_no_oops_in_fixed_intervals(); void verify_constants(); void verify_registers(); #endif
public: // creation
LinearScan(IR* ir, LIRGenerator* gen, FrameMap* frame_map);
// main entry function: perform linear scan register allocation void do_linear_scan();
// accessors used by Compilation int max_spills() const { return _max_spills; } int num_calls() const { assert(_num_calls >= 0, "not set"); return _num_calls; }
// Used for debugging
Interval* find_interval_at(int reg_num) const; #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 class MoveResolver: public StackObj { private:
LinearScan* _allocator;
LIR_List* _insert_list; int _insert_idx;
LIR_InsertionBuffer _insertion_buffer; // buffer where moves are inserted
IntervalList _mapping_from;
LIR_OprList _mapping_from_opr;
IntervalList _mapping_to; bool _multiple_reads_allowed; int _register_blocked[LinearScan::nof_regs];
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; }
// for testing void print(outputStream* out = tty) const PRODUCT_RETURN;
};
// 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.
class Interval : public CompilationResourceObj { private: static Interval* _end; // sentinel (interval with only range Range::end())
int _reg_num;
BasicType _type; // valid only for virtual registers
Range* _first; // sorted list of Ranges
intStack _use_pos_and_kinds; // sorted list of use-positions and their according use-kinds
Range* _current; // interval iteration: the current Range
Interval* _next; // interval iteration: sorted list of Intervals (ends with sentinel)
IntervalState _state; // interval iteration: to which set belongs this interval
int _assigned_reg; int _assigned_regHi;
int _cached_to; // cached value: to of last range (-1: not cached)
LIR_Opr _cached_opr;
VMReg _cached_vm_reg;
Interval* _split_parent; // the original interval where this interval is derived from
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
IntervalSpillState _spill_state; // for spill move optimization int _spill_definition_pos; // position where the interval is defined (if defined only once)
Interval* _register_hint; // this interval should be in the same register as the hint interval
int calc_to();
Interval* new_split_child(); public:
Interval(int reg_num);
// for spill optimization
IntervalSpillState spill_state() const { return split_parent()->_spill_state; } int spill_definition_pos() const { return split_parent()->_spill_definition_pos; } 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
LIR_Opr cached_opr() const { return _cached_opr; }
VMReg cached_vm_reg() const { return _cached_vm_reg; } void set_cached_opr(LIR_Opr opr) { _cached_opr = opr; } void set_cached_vm_reg(VMReg reg) { _cached_vm_reg = reg; }
// 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 int next_usage_exact(IntervalUseKind exact_use_kind, int from) const; int previous_usage(IntervalUseKind min_use_kind, int from) const;
// printing #ifndef PRODUCT void print() const { print_on(tty); } void print_on(outputStream* out) const {
print_on(out, false);
} // Special version for compatibility with C1 Visualizer. void print_on(outputStream* out, bool is_cfg_printer) const;
// Used for debugging void print_parent() const; void print_children() const; #endif
};
class IntervalWalker : public CompilationResourceObj { protected:
Compilation* _compilation;
LinearScan* _allocator;
Interval* _unhandled_first[nofKinds]; // sorted list of intervals, not life before the current position
Interval* _active_first [nofKinds]; // sorted list of intervals, life at the current position
Interval* _inactive_first [nofKinds]; // sorted list of intervals, intervals in a life time hole at the current position
Interval* _current; // the current interval coming from unhandled list int _current_position; // the current position (intercept point through the intervals)
IntervalKind _current_kind; // and whether it is fixed_kind or any_kind.
// activate_current() is called when an unhandled interval becomes active (in current(), current_kind()). // Return false if current() should not be moved the active interval list. // It is safe to append current to any interval list but the unhandled list. virtualbool activate_current() { returntrue; }
// This method is called whenever an interval moves from one interval list to another to print some // information about it and its state change if TraceLinearScanLevel is set appropriately.
DEBUG_ONLY(void interval_moved(Interval* interval, IntervalKind kind, IntervalState from, IntervalState to);)
// active contains the intervals that are live after the lir_op void walk_to(int lir_op_id); // active contains the intervals that are live before the lir_op void walk_before(int lir_op_id) { walk_to(lir_op_id-1); } // walk through all intervals void walk() { walk_to(max_jint); }
int current_position() { return _current_position; }
};
// The actual linear scan register allocator class LinearScanWalker : public IntervalWalker { enum {
any_reg = LinearScan::any_reg
};
private: int _first_reg; // the reg. number of the first phys. register int _last_reg; // the reg. nmber of the last phys. register int _num_phys_regs; // required by current interval bool _adjacent_regs; // have lo/hi words of phys. regs be adjacent
int _use_pos[LinearScan::nof_regs]; int _block_pos[LinearScan::nof_regs];
IntervalList* _spill_intervals[LinearScan::nof_regs];
MoveResolver _move_resolver; // for ordering spill moves
// accessors mapped to same functions in class LinearScan int block_count() const { return allocator()->block_count(); }
BlockBegin* block_at(int idx) const { return allocator()->block_at(idx); }
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 set_use_pos(int reg, Interval* i, int use_pos, bool only_process_use_pos); void set_use_pos(Interval* i, int use_pos, bool only_process_use_pos); void set_block_pos(int reg, Interval* i, int block_pos); void set_block_pos(Interval* i, int block_pos);
void insert_move(int op_id, Interval* src_it, Interval* dst_it); int find_optimal_split_pos(BlockBegin* min_block, BlockBegin* max_block, int max_split_pos); int find_optimal_split_pos(Interval* it, int min_split_pos, int max_split_pos, bool do_loop_optimization); void split_before_usage(Interval* it, int min_split_pos, int max_split_pos); void split_for_spilling(Interval* it); void split_stack_interval(Interval* it); void split_when_partial_register_available(Interval* it, int register_available_until); void split_and_spill_interval(Interval* it);
int find_free_reg(int reg_needed_until, int interval_to, int hint_reg, int ignore_reg, bool* need_split); int find_free_double_reg(int reg_needed_until, int interval_to, int hint_reg, bool* need_split); bool alloc_free_reg(Interval* cur);
int find_locked_reg(int reg_needed_until, int interval_to, int ignore_reg, bool* need_split); int find_locked_double_reg(int reg_needed_until, int interval_to, bool* need_split); void split_and_spill_intersecting_intervals(int reg, int regHi); void alloc_locked_reg(Interval* cur);
Thisoptimizationaffectsallkindofmoves(reg->reg,reg->stackand stack->reg).Becausethisoptimizationworksbestwhenablockcontainsonly fewmoves,ithasahugeimpactonthenumberofblocksthataretotally empty.
*/ class EdgeMoveOptimizer : public StackObj { private: // the class maintains a list with all lir-instruction-list of the // successors (predecessors) and the current index into the lir-lists
LIR_OpListStack _edge_instructions;
intStack _edge_instructions_idx;
// counter for different types of moves
counter_move_total,
counter_move_reg_reg,
counter_move_reg_stack,
counter_move_stack_reg,
counter_move_stack_stack,
counter_move_reg_mem,
counter_move_mem_reg,
counter_move_const_any,
number_of_counters,
invalid_counter = -1
};
private: int _counters_sum[number_of_counters]; int _counters_max[number_of_counters];
void inc_counter(Counter idx, int value = 1) { _counters_sum[idx] += value; }
void begin_method(); // called for each method when register allocation starts void end_method(LinearScan* allocator); // called for each method when register allocation completed void print(double total_time); // called before termination of VM to print global summary
// Pick up platform-dependent implementation details #include CPU_HEADER(c1_LinearScan)
#endif// SHARE_C1_C1_LINEARSCAN_HPP
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.21Bemerkung:
(vorverarbeitet am 2026-06-10)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.