8224675: Late GC barrier insertion for ZGC

Reviewed-by: roland, eosterlund, pliden
This commit is contained in:
Nils Eliasson 2019-02-14 14:54:05 +01:00
parent b34b2d993c
commit 75e9d0a290
28 changed files with 1256 additions and 1217 deletions

@ -21,6 +21,12 @@
// questions.
//
source_hpp %{
#include "gc/z/c2/zBarrierSetC2.hpp"
%}
source %{
#include "gc/z/zBarrierSetAssembler.hpp"
@ -45,7 +51,7 @@ instruct zLoadBarrierSlowRegXmmAndYmm(rRegP dst, memory src, rFlagsReg cr,
rxmm12 x12, rxmm13 x13, rxmm14 x14, rxmm15 x15) %{
match(Set dst (LoadBarrierSlowReg src));
predicate(UseAVX <= 2);
predicate((UseAVX <= 2) && !n->as_LoadBarrierSlowReg()->is_weak());
effect(DEF dst, KILL cr,
KILL x0, KILL x1, KILL x2, KILL x3,
@ -74,7 +80,7 @@ instruct zLoadBarrierSlowRegZmm(rRegP dst, memory src, rFlagsReg cr,
rxmm28 x28, rxmm29 x29, rxmm30 x30, rxmm31 x31) %{
match(Set dst (LoadBarrierSlowReg src));
predicate(UseAVX == 3);
predicate((UseAVX == 3) && !n->as_LoadBarrierSlowReg()->is_weak());
effect(DEF dst, KILL cr,
KILL x0, KILL x1, KILL x2, KILL x3,
@ -102,8 +108,8 @@ instruct zLoadBarrierWeakSlowRegXmmAndYmm(rRegP dst, memory src, rFlagsReg cr,
rxmm8 x8, rxmm9 x9, rxmm10 x10, rxmm11 x11,
rxmm12 x12, rxmm13 x13, rxmm14 x14, rxmm15 x15) %{
match(Set dst (LoadBarrierWeakSlowReg src));
predicate(UseAVX <= 2);
match(Set dst (LoadBarrierSlowReg src));
predicate((UseAVX <= 2) && n->as_LoadBarrierSlowReg()->is_weak());
effect(DEF dst, KILL cr,
KILL x0, KILL x1, KILL x2, KILL x3,
@ -131,8 +137,8 @@ instruct zLoadBarrierWeakSlowRegZmm(rRegP dst, memory src, rFlagsReg cr,
rxmm24 x24, rxmm25 x25, rxmm26 x26, rxmm27 x27,
rxmm28 x28, rxmm29 x29, rxmm30 x30, rxmm31 x31) %{
match(Set dst (LoadBarrierWeakSlowReg src));
predicate(UseAVX == 3);
match(Set dst (LoadBarrierSlowReg src));
predicate((UseAVX == 3) && n->as_LoadBarrierSlowReg()->is_weak());
effect(DEF dst, KILL cr,
KILL x0, KILL x1, KILL x2, KILL x3,
@ -152,3 +158,58 @@ instruct zLoadBarrierWeakSlowRegZmm(rRegP dst, memory src, rFlagsReg cr,
ins_pipe(pipe_slow);
%}
// Specialized versions of compareAndExchangeP that adds a keepalive that is consumed
// but doesn't affect output.
instruct z_compareAndExchangeP(
memory mem_ptr,
rax_RegP oldval, rRegP newval, rRegP keepalive,
rFlagsReg cr) %{
predicate(VM_Version::supports_cx8());
match(Set oldval (ZCompareAndExchangeP (Binary mem_ptr keepalive) (Binary oldval newval)));
effect(KILL cr);
format %{ "cmpxchgq $mem_ptr,$newval\t# "
"If rax == $mem_ptr then store $newval into $mem_ptr\n\t" %}
opcode(0x0F, 0xB1);
ins_encode(lock_prefix,
REX_reg_mem_wide(newval, mem_ptr),
OpcP, OpcS,
reg_mem(newval, mem_ptr) // lock cmpxchg
);
ins_pipe( pipe_cmpxchg );
%}
instruct z_compareAndSwapP(rRegI res,
memory mem_ptr,
rax_RegP oldval, rRegP newval, rRegP keepalive,
rFlagsReg cr) %{
predicate(VM_Version::supports_cx8());
match(Set res (ZCompareAndSwapP (Binary mem_ptr keepalive) (Binary oldval newval)));
match(Set res (ZWeakCompareAndSwapP (Binary mem_ptr keepalive) (Binary oldval newval)));
effect(KILL cr, KILL oldval);
format %{ "cmpxchgq $mem_ptr,$newval\t# "
"If rax == $mem_ptr then store $newval into $mem_ptr\n\t"
"sete $res\n\t"
"movzbl $res, $res" %}
opcode(0x0F, 0xB1);
ins_encode(lock_prefix,
REX_reg_mem_wide(newval, mem_ptr),
OpcP, OpcS,
reg_mem(newval, mem_ptr),
REX_breg(res), Opcode(0x0F), Opcode(0x94), reg(res), // sete
REX_reg_breg(res, res), // movzbl
Opcode(0xF), Opcode(0xB6), reg_reg(res, res));
ins_pipe( pipe_cmpxchg );
%}
instruct z_xchgP( memory mem, rRegP newval, rRegP keepalive) %{
match(Set newval (ZGetAndSetP mem (Binary newval keepalive)));
format %{ "XCHGQ $newval,[$mem]" %}
ins_encode %{
__ xchgq($newval$$Register, $mem$$Address);
%}
ins_pipe( pipe_cmpxchg );
%}

@ -757,7 +757,6 @@ int InstructForm::memory_operand(FormDict &globals) const {
return NO_MEMORY_OPERAND;
}
// This instruction captures the machine-independent bottom_type
// Expected use is for pointer vs oop determination for LoadP
bool InstructForm::captures_bottom_type(FormDict &globals) const {
@ -775,8 +774,9 @@ bool InstructForm::captures_bottom_type(FormDict &globals) const {
!strcmp(_matrule->_rChild->_opType,"GetAndSetP") ||
!strcmp(_matrule->_rChild->_opType,"GetAndSetN") ||
#if INCLUDE_ZGC
!strcmp(_matrule->_rChild->_opType,"ZGetAndSetP") ||
!strcmp(_matrule->_rChild->_opType,"ZCompareAndExchangeP") ||
!strcmp(_matrule->_rChild->_opType,"LoadBarrierSlowReg") ||
!strcmp(_matrule->_rChild->_opType,"LoadBarrierWeakSlowReg") ||
#endif
#if INCLUDE_SHENANDOAHGC
!strcmp(_matrule->_rChild->_opType,"ShenandoahCompareAndExchangeP") ||
@ -3506,12 +3506,16 @@ int MatchNode::needs_ideal_memory_edge(FormDict &globals) const {
"CompareAndSwapB", "CompareAndSwapS", "CompareAndSwapI", "CompareAndSwapL", "CompareAndSwapP", "CompareAndSwapN",
"WeakCompareAndSwapB", "WeakCompareAndSwapS", "WeakCompareAndSwapI", "WeakCompareAndSwapL", "WeakCompareAndSwapP", "WeakCompareAndSwapN",
"CompareAndExchangeB", "CompareAndExchangeS", "CompareAndExchangeI", "CompareAndExchangeL", "CompareAndExchangeP", "CompareAndExchangeN",
#if INCLUDE_SHENANDOAHGC
"ShenandoahCompareAndSwapN", "ShenandoahCompareAndSwapP", "ShenandoahWeakCompareAndSwapP", "ShenandoahWeakCompareAndSwapN", "ShenandoahCompareAndExchangeP", "ShenandoahCompareAndExchangeN",
#endif
"StoreCM",
"ClearArray",
"GetAndSetB", "GetAndSetS", "GetAndAddI", "GetAndSetI", "GetAndSetP",
"GetAndAddB", "GetAndAddS", "GetAndAddL", "GetAndSetL", "GetAndSetN",
"LoadBarrierSlowReg", "LoadBarrierWeakSlowReg"
#if INCLUDE_ZGC
"LoadBarrierSlowReg", "ZGetAndSetP", "ZCompareAndSwapP", "ZCompareAndExchangeP", "ZWeakCompareAndSwapP",
#endif
"ClearArray"
};
int cnt = sizeof(needs_ideal_memory_list)/sizeof(char*);
if( strcmp(_opType,"PrefetchAllocation")==0 )

@ -67,7 +67,7 @@ NOT_PRODUCT(cflags(TraceOptoOutput, bool, TraceOptoOutput, TraceOptoOutput))
cflags(CloneMapDebug, bool, false, CloneMapDebug) \
cflags(IGVPrintLevel, intx, PrintIdealGraphLevel, IGVPrintLevel) \
cflags(MaxNodeLimit, intx, MaxNodeLimit, MaxNodeLimit) \
ZGC_ONLY(cflags(ZOptimizeLoadBarriers, bool, ZOptimizeLoadBarriers, ZOptimizeLoadBarriers))
ZGC_ONLY(cflags(ZTraceLoadBarriers, bool, false, ZTraceLoadBarriers))
#else
#define compilerdirectives_c2_flags(cflags)
#endif

@ -259,6 +259,7 @@ public:
Optimization,
Expansion
};
virtual bool array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type, bool is_clone, ArrayCopyPhase phase) const { return false; }
virtual void clone_barrier_at_expansion(ArrayCopyNode* ac, Node* call, PhaseIterGVN& igvn) const;
@ -273,7 +274,6 @@ public:
virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { }
virtual void enqueue_useful_gc_barrier(PhaseIterGVN* igvn, Node* node) const {}
virtual void eliminate_useless_gc_barriers(Unique_Node_List &useful, Compile* C) const {}
virtual void add_users_to_worklist(Unique_Node_List* worklist) const {}
// Allow barrier sets to have shared state that is preserved across a compilation unit.
// This could for example comprise macro nodes to be expanded during macro expansion.
@ -286,17 +286,21 @@ public:
virtual bool is_gc_specific_loop_opts_pass(LoopOptsMode mode) const { return false; }
virtual bool has_special_unique_user(const Node* node) const { return false; }
virtual bool needs_anti_dependence_check(const Node* node) const { return true; }
virtual void barrier_insertion_phase(Compile* C, PhaseIterGVN &igvn) const { }
enum CompilePhase {
BeforeOptimize, /* post_parse = true */
BeforeExpand, /* post_parse = false */
BeforeOptimize,
BeforeLateInsertion,
BeforeMacroExpand,
BeforeCodeGen
};
virtual void verify_gc_barriers(Compile* compile, CompilePhase phase) const {}
virtual bool flatten_gc_alias_type(const TypePtr*& adr_type) const { return false; }
#ifdef ASSERT
virtual bool verify_gc_alias_type(const TypePtr* adr_type, int offset) const { return false; }
virtual void verify_gc_barriers(Compile* compile, CompilePhase phase) const {}
#endif
virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode) const { return false; }
@ -310,8 +314,8 @@ public:
virtual bool matcher_find_shared_post_visit(Matcher* matcher, Node* n, uint opcode) const { return false; };
virtual bool matcher_is_store_load_barrier(Node* x, uint xop) const { return false; }
virtual void igvn_add_users_to_worklist(PhaseIterGVN* igvn, Node* use) const {}
virtual void ccp_analyze(PhaseCCP* ccp, Unique_Node_List& worklist, Node* use) const {}
virtual void igvn_add_users_to_worklist(PhaseIterGVN* igvn, Node* use) const { }
virtual void ccp_analyze(PhaseCCP* ccp, Unique_Node_List& worklist, Node* use) const { }
virtual Node* split_if_pre(PhaseIdealLoop* phase, Node* n) const { return NULL; }
virtual bool build_loop_late_post(PhaseIdealLoop* phase, Node* n) const { return false; }

@ -912,8 +912,6 @@ void ShenandoahBarrierSetC2::eliminate_useless_gc_barriers(Unique_Node_List &use
}
}
void ShenandoahBarrierSetC2::add_users_to_worklist(Unique_Node_List* worklist) const {}
void* ShenandoahBarrierSetC2::create_barrier_state(Arena* comp_arena) const {
return new(comp_arena) ShenandoahBarrierSetC2State(comp_arena);
}
@ -928,7 +926,7 @@ bool ShenandoahBarrierSetC2::expand_macro_nodes(PhaseMacroExpand* macro) const {
#ifdef ASSERT
void ShenandoahBarrierSetC2::verify_gc_barriers(Compile* compile, CompilePhase phase) const {
if (ShenandoahVerifyOptoBarriers && phase == BarrierSetC2::BeforeExpand) {
if (ShenandoahVerifyOptoBarriers && phase == BarrierSetC2::BeforeMacroExpand) {
ShenandoahBarrierC2Support::verify(Compile::current()->root());
} else if (phase == BarrierSetC2::BeforeCodeGen) {
// Verify G1 pre-barriers

@ -126,7 +126,6 @@ public:
virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const;
virtual void enqueue_useful_gc_barrier(PhaseIterGVN* igvn, Node* node) const;
virtual void eliminate_useless_gc_barriers(Unique_Node_List &useful, Compile* C) const;
virtual void add_users_to_worklist(Unique_Node_List* worklist) const;
// Allow barrier sets to have shared state that is preserved across a compilation unit.
// This could for example comprise macro nodes to be expanded during macro expansion.

File diff suppressed because it is too large Load Diff

@ -29,15 +29,33 @@
#include "opto/node.hpp"
#include "utilities/growableArray.hpp"
class ZCompareAndSwapPNode : public CompareAndSwapPNode {
public:
ZCompareAndSwapPNode(Node* c, Node *mem, Node *adr, Node *val, Node *ex, MemNode::MemOrd mem_ord) : CompareAndSwapPNode(c, mem, adr, val, ex, mem_ord) { }
virtual int Opcode() const;
};
class ZWeakCompareAndSwapPNode : public WeakCompareAndSwapPNode {
public:
ZWeakCompareAndSwapPNode(Node* c, Node *mem, Node *adr, Node *val, Node *ex, MemNode::MemOrd mem_ord) : WeakCompareAndSwapPNode(c, mem, adr, val, ex, mem_ord) { }
virtual int Opcode() const;
};
class ZCompareAndExchangePNode : public CompareAndExchangePNode {
public:
ZCompareAndExchangePNode(Node* c, Node *mem, Node *adr, Node *val, Node *ex, const TypePtr* at, const Type* t, MemNode::MemOrd mem_ord) : CompareAndExchangePNode(c, mem, adr, val, ex, at, t, mem_ord) { }
virtual int Opcode() const;
};
class ZGetAndSetPNode : public GetAndSetPNode {
public:
ZGetAndSetPNode(Node* c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* t) : GetAndSetPNode(c, mem, adr, val, at, t) { }
virtual int Opcode() const;
};
class LoadBarrierNode : public MultiNode {
private:
bool _weak; // On strong or weak oop reference
bool _writeback; // Controls if the barrier writes the healed oop back to memory
// A swap on a memory location must never write back the healed oop
bool _oop_reload_allowed; // Controls if the barrier are allowed to reload the oop from memory
// before healing, otherwise both the oop and the address must be
// passed to the barrier from the oop
static bool is_dominator(PhaseIdealLoop* phase, bool linear_only, Node *d, Node *n);
void push_dominated_barriers(PhaseIterGVN* igvn) const;
@ -57,9 +75,7 @@ public:
Node* mem,
Node* val,
Node* adr,
bool weak,
bool writeback,
bool oop_reload_allowed);
bool weak);
virtual int Opcode() const;
virtual uint size_of() const;
@ -86,17 +102,11 @@ public:
bool is_weak() const {
return _weak;
}
bool is_writeback() const {
return _writeback;
}
bool oop_reload_allowed() const {
return _oop_reload_allowed;
}
};
class LoadBarrierSlowRegNode : public LoadPNode {
private:
bool _is_weak;
public:
LoadBarrierSlowRegNode(Node *c,
Node *mem,
@ -104,8 +114,9 @@ public:
const TypePtr *at,
const TypePtr* t,
MemOrd mo,
bool weak = false,
ControlDependency control_dependency = DependsOnlyOnTest) :
LoadPNode(c, mem, adr, at, t, mo, control_dependency) {
LoadPNode(c, mem, adr, at, t, mo, control_dependency), _is_weak(weak) {
init_class_id(Class_LoadBarrierSlowReg);
}
@ -118,30 +129,8 @@ public:
}
virtual int Opcode() const;
};
class LoadBarrierWeakSlowRegNode : public LoadPNode {
public:
LoadBarrierWeakSlowRegNode(Node *c,
Node *mem,
Node *adr,
const TypePtr *at,
const TypePtr* t,
MemOrd mo,
ControlDependency control_dependency = DependsOnlyOnTest) :
LoadPNode(c, mem, adr, at, t, mo, control_dependency) {
init_class_id(Class_LoadBarrierWeakSlowReg);
}
virtual const char * name() {
return "LoadBarrierWeakSlowRegNode";
}
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape) {
return NULL;
}
virtual int Opcode() const;
bool is_weak() { return _is_weak; }
};
class ZBarrierSetC2State : public ResourceObj {
@ -157,15 +146,17 @@ public:
LoadBarrierNode* load_barrier_node(int idx) const;
};
enum BarrierInfo {
NoBarrier = 0,
RequireBarrier = 1,
WeakBarrier = 3, // Inclusive with RequireBarrier
ExpandedBarrier = 4
};
class ZBarrierSetC2 : public BarrierSetC2 {
private:
ZBarrierSetC2State* state() const;
Node* make_cas_loadbarrier(C2AtomicParseAccess& access) const;
Node* make_cmpx_loadbarrier(C2AtomicParseAccess& access) const;
void expand_loadbarrier_basic(PhaseMacroExpand* phase, LoadBarrierNode *barrier) const;
void expand_loadbarrier_node(PhaseMacroExpand* phase, LoadBarrierNode* barrier) const;
void expand_loadbarrier_optimized(PhaseMacroExpand* phase, LoadBarrierNode *barrier) const;
const TypeFunc* load_barrier_Type() const;
#ifdef ASSERT
void verify_gc_barriers(bool post_parse) const;
@ -186,41 +177,42 @@ protected:
const Type* val_type) const;
public:
Node* load_barrier(GraphKit* kit,
Node* val,
Node* adr,
bool weak = false,
bool writeback = true,
bool oop_reload_allowed = true) const;
virtual void* create_barrier_state(Arena* comp_arena) const;
virtual bool has_load_barriers() const { return true; }
virtual bool is_gc_barrier_node(Node* node) const;
virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { }
virtual void eliminate_useless_gc_barriers(Unique_Node_List &useful, Compile* C) const;
virtual void add_users_to_worklist(Unique_Node_List* worklist) const;
virtual void enqueue_useful_gc_barrier(PhaseIterGVN* igvn, Node* node) const;
virtual Node* step_over_gc_barrier(Node* c) const;
virtual void register_potential_barrier_node(Node* node) const;
virtual void unregister_potential_barrier_node(Node* node) const;
virtual void eliminate_gc_barrier(PhaseMacroExpand* macro, Node* node) const { }
virtual void enqueue_useful_gc_barrier(PhaseIterGVN* igvn, Node* node) const;
virtual void eliminate_useless_gc_barriers(Unique_Node_List &useful, Compile* C) const;
virtual bool array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type, bool is_clone, ArrayCopyPhase phase) const;
virtual Node* step_over_gc_barrier(Node* c) const;
// If the BarrierSetC2 state has kept barrier nodes in its compilation unit state to be
// expanded later, then now is the time to do so.
virtual bool expand_barriers(Compile* C, PhaseIterGVN& igvn) const;
static void find_dominating_barriers(PhaseIterGVN& igvn);
static void loop_optimize_gc_barrier(PhaseIdealLoop* phase, Node* node, bool last_round);
virtual bool final_graph_reshaping(Compile* compile, Node* n, uint opcode) const;
virtual bool matcher_find_shared_visit(Matcher* matcher, Matcher::MStack& mstack, Node* n, uint opcode, bool& mem_op, int& mem_addr_idx) const;
virtual bool matcher_find_shared_post_visit(Matcher* matcher, Node* n, uint opcode) const;
virtual bool needs_anti_dependence_check(const Node* node) const;
#ifdef ASSERT
virtual void verify_gc_barriers(Compile* compile, CompilePhase phase) const;
#endif
virtual bool escape_add_to_con_graph(ConnectionGraph* conn_graph, PhaseGVN* gvn, Unique_Node_List* delayed_worklist, Node* n, uint opcode) const;
virtual bool escape_add_final_edges(ConnectionGraph* conn_graph, PhaseGVN* gvn, Node* n, uint opcode) const;
// Load barrier insertion and expansion external
virtual void barrier_insertion_phase(Compile* C, PhaseIterGVN &igvn) const;
virtual bool optimize_loops(PhaseIdealLoop* phase, LoopOptsMode mode, VectorSet& visited, Node_Stack& nstack, Node_List& worklist) const;
virtual bool is_gc_specific_loop_opts_pass(LoopOptsMode mode) const { return (mode == LoopOptsZBarrierInsertion); }
private:
// Load barrier insertion and expansion internal
void insert_barriers_on_unsafe(PhaseIdealLoop* phase) const;
void clean_catch_blocks(PhaseIdealLoop* phase) const;
void insert_load_barriers(PhaseIdealLoop* phase) const;
LoadNode* insert_one_loadbarrier(PhaseIdealLoop* phase, LoadNode* load, Node* ctrl) const;
void insert_one_loadbarrier_inner(PhaseIdealLoop* phase, LoadNode* load, Node* ctrl, VectorSet visited) const;
};
#endif // SHARE_GC_Z_C2_ZBARRIERSETC2_HPP

@ -328,46 +328,9 @@ void ZHeap::mark_flush_and_free(Thread* thread) {
_mark.flush_and_free(thread);
}
class ZFixupPartialLoadsClosure : public ZRootsIteratorClosure {
public:
virtual void do_oop(oop* p) {
ZBarrier::mark_barrier_on_root_oop_field(p);
}
virtual void do_oop(narrowOop* p) {
ShouldNotReachHere();
}
};
class ZFixupPartialLoadsTask : public ZTask {
private:
ZThreadRootsIterator _thread_roots;
public:
ZFixupPartialLoadsTask() :
ZTask("ZFixupPartialLoadsTask"),
_thread_roots() {}
virtual void work() {
ZFixupPartialLoadsClosure cl;
_thread_roots.oops_do(&cl);
}
};
void ZHeap::fixup_partial_loads() {
ZFixupPartialLoadsTask task;
_workers.run_parallel(&task);
}
bool ZHeap::mark_end() {
assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
// C2 can generate code where a safepoint poll is inserted
// between a load and the associated load barrier. To handle
// this case we need to rescan the thread stack here to make
// sure such oops are marked.
fixup_partial_loads();
// Try end marking
if (!_mark.end()) {
// Marking not completed, continue concurrent mark

@ -82,9 +82,6 @@
diagnostic(bool, ZVerifyForwarding, false, \
"Verify forwarding tables") \
\
diagnostic(bool, ZOptimizeLoadBarriers, true, \
"Apply load barrier optimizations") \
\
develop(bool, ZVerifyLoadBarriers, false, \
"Verify that reference loads are followed by barriers")

@ -196,7 +196,10 @@ macro(LoadS)
#endif
zgcmacro(LoadBarrier)
zgcmacro(LoadBarrierSlowReg)
zgcmacro(LoadBarrierWeakSlowReg)
zgcmacro(ZCompareAndSwapP)
zgcmacro(ZWeakCompareAndSwapP)
zgcmacro(ZCompareAndExchangeP)
zgcmacro(ZGetAndSetP)
macro(Lock)
macro(Loop)
macro(LoopLimit)

@ -2211,8 +2211,8 @@ void Compile::Optimize() {
#endif
#ifdef ASSERT
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
#ifdef ASSERT
bs->verify_gc_barriers(this, BarrierSetC2::BeforeOptimize);
#endif
@ -2371,7 +2371,6 @@ void Compile::Optimize() {
igvn = ccp;
igvn.optimize();
}
print_method(PHASE_ITER_GVN2, 2);
if (failing()) return;
@ -2382,12 +2381,6 @@ void Compile::Optimize() {
return;
}
#if INCLUDE_ZGC
if (UseZGC) {
ZBarrierSetC2::find_dominating_barriers(igvn);
}
#endif
if (failing()) return;
// Ensure that major progress is now clear
@ -2407,28 +2400,33 @@ void Compile::Optimize() {
}
#ifdef ASSERT
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
bs->verify_gc_barriers(this, BarrierSetC2::BeforeExpand);
bs->verify_gc_barriers(this, BarrierSetC2::BeforeLateInsertion);
#endif
bs->barrier_insertion_phase(C, igvn);
if (failing()) return;
#ifdef ASSERT
bs->verify_gc_barriers(this, BarrierSetC2::BeforeMacroExpand);
#endif
{
TracePhase tp("macroExpand", &timers[_t_macroExpand]);
PhaseMacroExpand mex(igvn);
print_method(PHASE_BEFORE_MACRO_EXPANSION, 2);
if (mex.expand_macro_nodes()) {
assert(failing(), "must bail out w/ explicit message");
return;
}
print_method(PHASE_MACRO_EXPANSION, 2);
}
{
TracePhase tp("barrierExpand", &timers[_t_barrierExpand]);
print_method(PHASE_BEFORE_BARRIER_EXPAND, 2);
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
if (bs->expand_barriers(this, igvn)) {
assert(failing(), "must bail out w/ explicit message");
return;
}
print_method(PHASE_BARRIER_EXPANSION, 2);
}
if (opaque4_count() > 0) {
@ -2824,7 +2822,7 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) {
MemBarNode* mb = n->as_MemBar();
if (mb->trailing_store() || mb->trailing_load_store()) {
assert(mb->leading_membar()->trailing_membar() == mb, "bad membar pair");
Node* mem = mb->in(MemBarNode::Precedent);
Node* mem = BarrierSet::barrier_set()->barrier_set_c2()->step_over_gc_barrier(mb->in(MemBarNode::Precedent));
assert((mb->trailing_store() && mem->is_Store() && mem->as_Store()->is_release()) ||
(mb->trailing_load_store() && mem->is_LoadStore()), "missing mem op");
} else if (mb->leading()) {

@ -52,6 +52,7 @@ class C2Compiler;
class CallGenerator;
class CloneMap;
class ConnectionGraph;
class IdealGraphPrinter;
class InlineTree;
class Int_Array;
class LoadBarrierNode;
@ -95,9 +96,9 @@ enum LoopOptsMode {
LoopOptsNone,
LoopOptsShenandoahExpand,
LoopOptsShenandoahPostExpand,
LoopOptsZBarrierInsertion,
LoopOptsSkipSplitIf,
LoopOptsVerify,
LoopOptsLastRound
LoopOptsVerify
};
typedef unsigned int node_idx_t;
@ -658,6 +659,7 @@ class Compile : public Phase {
void set_do_cleanup(bool z) { _do_cleanup = z; }
int do_cleanup() const { return _do_cleanup; }
void set_major_progress() { _major_progress++; }
void restore_major_progress(int progress) { _major_progress += progress; }
void clear_major_progress() { _major_progress = 0; }
int max_inline_size() const { return _max_inline_size; }
void set_freq_inline_size(int n) { _freq_inline_size = n; }
@ -747,7 +749,15 @@ class Compile : public Phase {
C->_latest_stage_start_counter.stamp();
}
void print_method(CompilerPhaseType cpt, int level = 1) {
bool should_print(int level = 1) {
#ifndef PRODUCT
return (_printer && _printer->should_print(level));
#else
return false;
#endif
}
void print_method(CompilerPhaseType cpt, int level = 1, int idx = 0) {
EventCompilerPhase event;
if (event.should_commit()) {
event.set_starttime(C->_latest_stage_start_counter);
@ -757,10 +767,15 @@ class Compile : public Phase {
event.commit();
}
#ifndef PRODUCT
if (_printer && _printer->should_print(level)) {
_printer->print_method(CompilerPhaseTypeHelper::to_string(cpt), level);
if (should_print(level)) {
char output[1024];
if (idx != 0) {
sprintf(output, "%s:%d", CompilerPhaseTypeHelper::to_string(cpt), idx);
} else {
sprintf(output, "%s", CompilerPhaseTypeHelper::to_string(cpt));
}
_printer->print_method(output, level);
}
#endif
C->_latest_stage_start_counter.stamp();

@ -350,14 +350,6 @@ void IdealGraphPrinter::end_method() {
_xml->flush();
}
// Print indent
void IdealGraphPrinter::print_indent() {
tty->print_cr("printing indent %d", _depth);
for (int i = 0; i < _depth; i++) {
_xml->print("%s", INDENT);
}
}
bool IdealGraphPrinter::traverse_outs() {
return _traverse_outs;
}
@ -663,14 +655,16 @@ void IdealGraphPrinter::walk_nodes(Node *start, bool edges, VectorSet* temp_set)
}
}
void IdealGraphPrinter::print_method(const char *name, int level, bool clear_nodes) {
print(name, (Node *)C->root(), level, clear_nodes);
void IdealGraphPrinter::print_method(const char *name, int level) {
if (should_print(level)) {
print(name, (Node *) C->root());
}
}
// Print current ideal graph
void IdealGraphPrinter::print(const char *name, Node *node, int level, bool clear_nodes) {
void IdealGraphPrinter::print(const char *name, Node *node) {
if (!_current_method || !_should_send_method || !should_print(level)) return;
if (!_current_method || !_should_send_method) return;
// Warning, unsafe cast?
_chaitin = (PhaseChaitin *)C->regalloc();

@ -81,11 +81,7 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
static const char *METHOD_SHORT_NAME_PROPERTY;
static const char *ASSEMBLY_ELEMENT;
elapsedTimer _walk_time;
elapsedTimer _output_time;
elapsedTimer _build_blocks_time;
static int _file_count;
static int _file_count;
networkStream *_stream;
xmlStream *_xml;
outputStream *_output;
@ -97,10 +93,6 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
bool _traverse_outs;
Compile *C;
static void pre_node(Node* node, void *env);
static void post_node(Node* node, void *env);
void print_indent();
void print_method(ciMethod *method, int bci, InlineTree *tree);
void print_inline_tree(InlineTree *tree);
void visit_node(Node *n, bool edges, VectorSet* temp_set);
@ -116,7 +108,6 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
void tail(const char *name);
void head(const char *name);
void text(const char *s);
intptr_t get_node_id(Node *n);
IdealGraphPrinter();
~IdealGraphPrinter();
@ -130,9 +121,8 @@ class IdealGraphPrinter : public CHeapObj<mtCompiler> {
void print_inlining();
void begin_method();
void end_method();
void print_method(const char *name, int level=1, bool clear_nodes = false);
void print(const char *name, Node *root, int level=1, bool clear_nodes = false);
void print_xml(const char *name);
void print_method(const char *name, int level = 0);
void print(const char *name, Node *root);
bool should_print(int level);
void set_compile(Compile* compile) {C = compile; }
};

@ -171,7 +171,6 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo
case Op_LoadL:
case Op_LoadP:
case Op_LoadBarrierSlowReg:
case Op_LoadBarrierWeakSlowReg:
case Op_LoadN:
case Op_LoadS:
case Op_LoadKlass:

@ -978,7 +978,7 @@ void LoopNode::verify_strip_mined(int expect_skeleton) const {
wq.push(u);
bool found_sfpt = false;
for (uint next = 0; next < wq.size() && !found_sfpt; next++) {
Node *n = wq.at(next);
Node* n = wq.at(next);
for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax && !found_sfpt; i++) {
Node* u = n->fast_out(i);
if (u == sfpt) {
@ -992,6 +992,19 @@ void LoopNode::verify_strip_mined(int expect_skeleton) const {
assert(found_sfpt, "no node in loop that's not input to safepoint");
}
}
if (UseZGC && !inner_out->in(0)->is_CountedLoopEnd()) {
// In some very special cases there can be a load that has no other uses than the
// counted loop safepoint. Then its loadbarrier will be placed between the inner
// loop exit and the safepoint. This is very rare
Node* ifnode = inner_out->in(1)->in(0);
// Region->IfTrue->If == Region->Iffalse->If
if (ifnode == inner_out->in(2)->in(0)) {
inner_out = ifnode->in(0);
}
}
CountedLoopEndNode* cle = inner_out->in(0)->as_CountedLoopEnd();
assert(cle == inner->loopexit_or_null(), "mismatch");
bool has_skeleton = outer_le->in(1)->bottom_type()->singleton() && outer_le->in(1)->bottom_type()->is_int()->get_con() == 0;
@ -2761,7 +2774,7 @@ bool PhaseIdealLoop::process_expensive_nodes() {
// Create a PhaseLoop. Build the ideal Loop tree. Map each Ideal Node to
// its corresponding LoopNode. If 'optimize' is true, do some loop cleanups.
void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) {
bool do_split_ifs = (mode == LoopOptsDefault || mode == LoopOptsLastRound);
bool do_split_ifs = (mode == LoopOptsDefault);
bool skip_loop_opts = (mode == LoopOptsNone);
int old_progress = C->major_progress();
@ -2921,9 +2934,7 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) {
build_loop_late( visited, worklist, nstack );
if (_verify_only) {
// restore major progress flag
for (int i = 0; i < old_progress; i++)
C->set_major_progress();
C->restore_major_progress(old_progress);
assert(C->unique() == unique, "verification mode made Nodes? ? ?");
assert(_igvn._worklist.size() == orig_worklist_size, "shouldn't push anything");
return;
@ -2967,9 +2978,7 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) {
if (skip_loop_opts) {
// restore major progress flag
for (int i = 0; i < old_progress; i++) {
C->set_major_progress();
}
C->restore_major_progress(old_progress);
// Cleanup any modified bits
_igvn.optimize();
@ -3018,11 +3027,8 @@ void PhaseIdealLoop::build_and_optimize(LoopOptsMode mode) {
// that require basic-block info (like cloning through Phi's)
if( SplitIfBlocks && do_split_ifs ) {
visited.Clear();
split_if_with_blocks( visited, nstack, mode == LoopOptsLastRound );
split_if_with_blocks( visited, nstack);
NOT_PRODUCT( if( VerifyLoopOptimizations ) verify(); );
if (mode == LoopOptsLastRound) {
C->set_major_progress();
}
}
if (!C->major_progress() && do_expensive_nodes && process_expensive_nodes()) {
@ -3157,8 +3163,7 @@ void PhaseIdealLoop::verify() const {
_ltree_root->verify_tree(loop_verify._ltree_root, NULL);
// Reset major-progress. It was cleared by creating a verify version of
// PhaseIdealLoop.
for( int i=0; i<old_progress; i++ )
C->set_major_progress();
C->restore_major_progress(old_progress);
}
//------------------------------verify_compare---------------------------------
@ -4288,7 +4293,6 @@ void PhaseIdealLoop::build_loop_late_post_work(Node *n, bool pinned) {
case Op_LoadS:
case Op_LoadP:
case Op_LoadBarrierSlowReg:
case Op_LoadBarrierWeakSlowReg:
case Op_LoadN:
case Op_LoadRange:
case Op_LoadD_unaligned:

@ -824,6 +824,7 @@ public:
// pull such a subsumed block out of the array, we write back the final
// correct block.
Node *get_ctrl( Node *i ) {
assert(has_node(i), "");
Node *n = get_ctrl_no_update(i);
_nodes.map( i->_idx, (Node*)((intptr_t)n + 1) );
@ -1306,9 +1307,9 @@ public:
// Check for aggressive application of 'split-if' optimization,
// using basic block level info.
void split_if_with_blocks ( VectorSet &visited, Node_Stack &nstack, bool last_round );
void split_if_with_blocks ( VectorSet &visited, Node_Stack &nstack);
Node *split_if_with_blocks_pre ( Node *n );
void split_if_with_blocks_post( Node *n, bool last_round );
void split_if_with_blocks_post( Node *n );
Node *has_local_phi_input( Node *n );
// Mark an IfNode as being dominated by a prior test,
// without actually altering the CFG (and hence IDOM info).

@ -1195,11 +1195,11 @@ bool PhaseIdealLoop::can_split_if(Node* n_ctrl) {
// Do the real work in a non-recursive function. CFG hackery wants to be
// in the post-order, so it can dirty the I-DOM info and not use the dirtied
// info.
void PhaseIdealLoop::split_if_with_blocks_post(Node *n, bool last_round) {
void PhaseIdealLoop::split_if_with_blocks_post(Node *n) {
// Cloning Cmp through Phi's involves the split-if transform.
// FastLock is not used by an If
if (n->is_Cmp() && !n->is_FastLock() && !last_round) {
if (n->is_Cmp() && !n->is_FastLock()) {
Node *n_ctrl = get_ctrl(n);
// Determine if the Node has inputs from some local Phi.
// Returns the block to clone thru.
@ -1451,18 +1451,12 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n, bool last_round) {
get_loop(get_ctrl(n)) == get_loop(get_ctrl(n->in(1))) ) {
_igvn.replace_node( n, n->in(1) );
}
#if INCLUDE_ZGC
if (UseZGC) {
ZBarrierSetC2::loop_optimize_gc_barrier(this, n, last_round);
}
#endif
}
//------------------------------split_if_with_blocks---------------------------
// Check for aggressive application of 'split-if' optimization,
// using basic block level info.
void PhaseIdealLoop::split_if_with_blocks(VectorSet &visited, Node_Stack &nstack, bool last_round) {
void PhaseIdealLoop::split_if_with_blocks(VectorSet &visited, Node_Stack &nstack) {
Node* root = C->root();
visited.set(root->_idx); // first, mark root as visited
// Do pre-visit work for root
@ -1488,7 +1482,7 @@ void PhaseIdealLoop::split_if_with_blocks(VectorSet &visited, Node_Stack &nstack
// All of n's children have been processed, complete post-processing.
if (cnt != 0 && !n->is_Con()) {
assert(has_node(n), "no dead nodes");
split_if_with_blocks_post(n, last_round);
split_if_with_blocks_post(n);
}
if (must_throttle_split_if()) {
nstack.clear();

@ -908,14 +908,6 @@ static bool skip_through_membars(Compile::AliasType* atp, const TypeInstPtr* tp,
// a load node that reads from the source array so we may be able to
// optimize out the ArrayCopy node later.
Node* LoadNode::can_see_arraycopy_value(Node* st, PhaseGVN* phase) const {
#if INCLUDE_ZGC
if (UseZGC) {
if (bottom_type()->make_oopptr() != NULL) {
return NULL;
}
}
#endif
Node* ld_adr = in(MemNode::Address);
intptr_t ld_off = 0;
AllocateNode* ld_alloc = AllocateNode::Ideal_allocation(ld_adr, phase, ld_off);
@ -2811,7 +2803,8 @@ const Type* SCMemProjNode::Value(PhaseGVN* phase) const
LoadStoreNode::LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required )
: Node(required),
_type(rt),
_adr_type(at)
_adr_type(at),
_has_barrier(false)
{
init_req(MemNode::Control, c );
init_req(MemNode::Memory , mem);
@ -3105,16 +3098,6 @@ Node *MemBarNode::Ideal(PhaseGVN *phase, bool can_reshape) {
return NULL;
}
#if INCLUDE_ZGC
if (UseZGC) {
if (req() == (Precedent+1) && in(MemBarNode::Precedent)->in(0) != NULL && in(MemBarNode::Precedent)->in(0)->is_LoadBarrier()) {
Node* load_node = in(MemBarNode::Precedent)->in(0)->in(LoadBarrierNode::Oop);
set_req(MemBarNode::Precedent, load_node);
return this;
}
}
#endif
bool progress = false;
// Eliminate volatile MemBars for scalar replaced objects.
if (can_reshape && req() == (Precedent+1)) {

@ -164,6 +164,7 @@ public:
Pinned,
DependsOnlyOnTest
};
private:
// LoadNode::hash() doesn't take the _control_dependency field
// into account: If the graph already has a non-pinned LoadNode and
@ -182,6 +183,8 @@ private:
// this field.
const MemOrd _mo;
uint _barrier; // Bit field with barrier information
protected:
virtual bool cmp(const Node &n) const;
virtual uint size_of() const; // Size is bigger
@ -193,7 +196,7 @@ protected:
public:
LoadNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *rt, MemOrd mo, ControlDependency control_dependency)
: MemNode(c,mem,adr,at), _control_dependency(control_dependency), _mo(mo), _type(rt) {
: MemNode(c,mem,adr,at), _control_dependency(control_dependency), _mo(mo), _barrier(0), _type(rt) {
init_class_id(Class_Load);
}
inline bool is_unordered() const { return !is_acquire(); }
@ -262,6 +265,10 @@ public:
Node* convert_to_unsigned_load(PhaseGVN& gvn);
Node* convert_to_signed_load(PhaseGVN& gvn);
void copy_barrier_info(const Node* src) { _barrier = src->as_Load()->_barrier; }
uint barrier_data() { return _barrier; }
void set_barrier_data(uint barrier_data) { _barrier |= barrier_data; }
#ifndef PRODUCT
virtual void dump_spec(outputStream *st) const;
#endif
@ -810,6 +817,7 @@ class LoadStoreNode : public Node {
private:
const Type* const _type; // What kind of value is loaded?
const TypePtr* _adr_type; // What kind of memory is being addressed?
bool _has_barrier;
virtual uint size_of() const; // Size is bigger
public:
LoadStoreNode( Node *c, Node *mem, Node *adr, Node *val, const TypePtr* at, const Type* rt, uint required );
@ -822,6 +830,8 @@ public:
bool result_not_used() const;
MemBarNode* trailing_membar() const;
void set_has_barrier() { _has_barrier = true; };
bool has_barrier() const { return _has_barrier; };
};
class LoadStoreConditionalNode : public LoadStoreNode {

@ -546,6 +546,9 @@ Node *Node::clone() const {
if (n->is_SafePoint()) {
n->as_SafePoint()->clone_replaced_nodes();
}
if (n->is_Load()) {
n->as_Load()->copy_barrier_info(this);
}
return n; // Return the clone
}
@ -564,7 +567,6 @@ void Node::setup_is_top() {
}
}
//------------------------------~Node------------------------------------------
// Fancy destructor; eagerly attempt to reclaim Node numberings and storage
void Node::destruct() {
@ -1454,13 +1456,16 @@ bool Node::rematerialize() const {
//------------------------------needs_anti_dependence_check---------------------
// Nodes which use memory without consuming it, hence need antidependences.
bool Node::needs_anti_dependence_check() const {
if( req() < 2 || (_flags & Flag_needs_anti_dependence_check) == 0 )
if (req() < 2 || (_flags & Flag_needs_anti_dependence_check) == 0) {
return false;
else
return in(1)->bottom_type()->has_memory();
}
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
if (!bs->needs_anti_dependence_check(this)) {
return false;
}
return in(1)->bottom_type()->has_memory();
}
// Get an integer constant from a ConNode (or CastIINode).
// Return a default value if there is no apparent constant here.
const TypeInt* Node::find_int_type() const {

@ -83,8 +83,8 @@ class JumpProjNode;
class LoadNode;
class LoadBarrierNode;
class LoadBarrierSlowRegNode;
class LoadBarrierWeakSlowRegNode;
class LoadStoreNode;
class LoadStoreConditionalNode;
class LockNode;
class LoopNode;
class MachBranchNode;
@ -688,8 +688,7 @@ public:
DEFINE_CLASS_ID(Mem, Node, 4)
DEFINE_CLASS_ID(Load, Mem, 0)
DEFINE_CLASS_ID(LoadVector, Load, 0)
DEFINE_CLASS_ID(LoadBarrierSlowReg, Load, 1)
DEFINE_CLASS_ID(LoadBarrierWeakSlowReg, Load, 2)
DEFINE_CLASS_ID(LoadBarrierSlowReg, Load, 1)
DEFINE_CLASS_ID(Store, Mem, 1)
DEFINE_CLASS_ID(StoreVector, Store, 0)
DEFINE_CLASS_ID(LoadStore, Mem, 2)
@ -830,9 +829,9 @@ public:
DEFINE_CLASS_QUERY(JumpProj)
DEFINE_CLASS_QUERY(Load)
DEFINE_CLASS_QUERY(LoadStore)
DEFINE_CLASS_QUERY(LoadStoreConditional)
DEFINE_CLASS_QUERY(LoadBarrier)
DEFINE_CLASS_QUERY(LoadBarrierSlowReg)
DEFINE_CLASS_QUERY(LoadBarrierWeakSlowReg)
DEFINE_CLASS_QUERY(Lock)
DEFINE_CLASS_QUERY(Loop)
DEFINE_CLASS_QUERY(Mach)

@ -1003,9 +1003,6 @@ PhaseIterGVN::PhaseIterGVN( PhaseGVN *gvn ) : PhaseGVN(gvn),
n->is_Mem() )
add_users_to_worklist(n);
}
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
bs->add_users_to_worklist(&_worklist);
}
/**

@ -52,8 +52,11 @@ enum CompilerPhaseType {
PHASE_MATCHING,
PHASE_INCREMENTAL_INLINE,
PHASE_INCREMENTAL_BOXING_INLINE,
PHASE_BEFORE_BARRIER_EXPAND,
PHASE_BEFORE_MACRO_EXPANSION,
PHASE_CALL_CATCH_CLEANUP,
PHASE_INSERT_BARRIER,
PHASE_MACRO_EXPANSION,
PHASE_BARRIER_EXPANSION,
PHASE_ADD_UNSAFE_BARRIER,
PHASE_END,
PHASE_FAILURE,
@ -90,8 +93,11 @@ class CompilerPhaseTypeHelper {
case PHASE_MATCHING: return "After matching";
case PHASE_INCREMENTAL_INLINE: return "Incremental Inline";
case PHASE_INCREMENTAL_BOXING_INLINE: return "Incremental Boxing Inline";
case PHASE_BEFORE_BARRIER_EXPAND: return "Before Barrier Expand";
case PHASE_BEFORE_MACRO_EXPANSION: return "Before macro expansion";
case PHASE_CALL_CATCH_CLEANUP: return "Call catch cleanup";
case PHASE_INSERT_BARRIER: return "Insert barrier";
case PHASE_MACRO_EXPANSION: return "Macro expand";
case PHASE_BARRIER_EXPANSION: return "Barrier expand";
case PHASE_ADD_UNSAFE_BARRIER: return "Add barrier to unsafe op";
case PHASE_END: return "End";
case PHASE_FAILURE: return "Failure";
default:

@ -298,7 +298,6 @@ void VectorNode::vector_operands(Node* n, uint* start, uint* end) {
case Op_LoadF: case Op_LoadD:
case Op_LoadP: case Op_LoadN:
case Op_LoadBarrierSlowReg:
case Op_LoadBarrierWeakSlowReg:
*start = 0;
*end = 0; // no vector operands
break;

@ -133,16 +133,12 @@ StackValue* StackValue::create_stack_value(const frame* fr, const RegisterMap* r
}
#endif
// Deoptimization must make sure all oops have passed load barriers
#if INCLUDE_ZGC
if (UseZGC) {
val = ZBarrier::load_barrier_on_oop_field_preloaded((oop*)value_addr, val);
}
#endif
#if INCLUDE_SHENANDOAHGC
if (UseShenandoahGC) {
val = ShenandoahBarrierSet::barrier_set()->load_reference_barrier(val);
}
#endif
assert(oopDesc::is_oop_or_null(val, false), "bad oop found");
Handle h(Thread::current(), val); // Wrap a handle around the oop
return new StackValue(h);
}

@ -152,6 +152,12 @@ class GenericGrowableArray : public ResourceObj {
template<class E> class GrowableArrayIterator;
template<class E, class UnaryPredicate> class GrowableArrayFilterIterator;
template<class E>
class CompareClosure : public Closure {
public:
virtual int do_compare(const E&, const E&) = 0;
};
template<class E> class GrowableArray : public GenericGrowableArray {
friend class VMStructs;
@ -443,6 +449,37 @@ template<class E> class GrowableArray : public GenericGrowableArray {
}
return min;
}
E insert_sorted(CompareClosure<E>* cc, const E& key) {
bool found;
int location = find_sorted(cc, key, found);
if (!found) {
insert_before(location, key);
}
return at(location);
}
template<typename K>
int find_sorted(CompareClosure<E>* cc, const K& key, bool& found) {
found = false;
int min = 0;
int max = length() - 1;
while (max >= min) {
int mid = (int)(((uint)max + min) / 2);
E value = at(mid);
int diff = cc->do_compare(key, value);
if (diff > 0) {
min = mid + 1;
} else if (diff < 0) {
max = mid - 1;
} else {
found = true;
return mid;
}
}
return min;
}
};
// Global GrowableArray methods (one instance in the library per each 'E' type).