Path: blob/master/src/hotspot/share/gc/z/c2/zBarrierSetC2.cpp
66646 views
/*1* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223#include "precompiled.hpp"24#include "classfile/javaClasses.hpp"25#include "gc/z/c2/zBarrierSetC2.hpp"26#include "gc/z/zBarrierSet.hpp"27#include "gc/z/zBarrierSetAssembler.hpp"28#include "gc/z/zBarrierSetRuntime.hpp"29#include "opto/arraycopynode.hpp"30#include "opto/addnode.hpp"31#include "opto/block.hpp"32#include "opto/compile.hpp"33#include "opto/graphKit.hpp"34#include "opto/machnode.hpp"35#include "opto/macro.hpp"36#include "opto/memnode.hpp"37#include "opto/node.hpp"38#include "opto/output.hpp"39#include "opto/regalloc.hpp"40#include "opto/rootnode.hpp"41#include "opto/runtime.hpp"42#include "opto/type.hpp"43#include "utilities/growableArray.hpp"44#include "utilities/macros.hpp"4546class ZBarrierSetC2State : public ResourceObj {47private:48GrowableArray<ZLoadBarrierStubC2*>* _stubs;49Node_Array _live;5051public:52ZBarrierSetC2State(Arena* arena) :53_stubs(new (arena) GrowableArray<ZLoadBarrierStubC2*>(arena, 8, 0, NULL)),54_live(arena) {}5556GrowableArray<ZLoadBarrierStubC2*>* stubs() {57return _stubs;58}5960RegMask* live(const Node* node) {61if (!node->is_Mach()) {62// Don't need liveness for non-MachNodes63return NULL;64}6566const MachNode* const mach = node->as_Mach();67if (mach->barrier_data() == ZLoadBarrierElided) {68// Don't need liveness data for nodes without barriers69return NULL;70}7172RegMask* live = (RegMask*)_live[node->_idx];73if (live == NULL) {74live = new (Compile::current()->comp_arena()->Amalloc_D(sizeof(RegMask))) RegMask();75_live.map(node->_idx, (Node*)live);76}7778return live;79}80};8182static ZBarrierSetC2State* barrier_set_state() {83return reinterpret_cast<ZBarrierSetC2State*>(Compile::current()->barrier_set_state());84}8586ZLoadBarrierStubC2* ZLoadBarrierStubC2::create(const MachNode* node, Address ref_addr, Register ref, Register tmp, uint8_t barrier_data) {87ZLoadBarrierStubC2* const stub = new (Compile::current()->comp_arena()) ZLoadBarrierStubC2(node, ref_addr, ref, tmp, barrier_data);88if (!Compile::current()->output()->in_scratch_emit_size()) {89barrier_set_state()->stubs()->append(stub);90}9192return stub;93}9495ZLoadBarrierStubC2::ZLoadBarrierStubC2(const MachNode* node, Address ref_addr, Register ref, Register tmp, uint8_t barrier_data) :96_node(node),97_ref_addr(ref_addr),98_ref(ref),99_tmp(tmp),100_barrier_data(barrier_data),101_entry(),102_continuation() {103assert_different_registers(ref, ref_addr.base());104assert_different_registers(ref, ref_addr.index());105}106107Address ZLoadBarrierStubC2::ref_addr() const {108return _ref_addr;109}110111Register ZLoadBarrierStubC2::ref() const {112return _ref;113}114115Register ZLoadBarrierStubC2::tmp() const {116return _tmp;117}118119address ZLoadBarrierStubC2::slow_path() const {120DecoratorSet decorators = DECORATORS_NONE;121if (_barrier_data & ZLoadBarrierStrong) {122decorators |= ON_STRONG_OOP_REF;123}124if (_barrier_data & ZLoadBarrierWeak) {125decorators |= ON_WEAK_OOP_REF;126}127if (_barrier_data & ZLoadBarrierPhantom) {128decorators |= ON_PHANTOM_OOP_REF;129}130if (_barrier_data & ZLoadBarrierNoKeepalive) {131decorators |= AS_NO_KEEPALIVE;132}133return ZBarrierSetRuntime::load_barrier_on_oop_field_preloaded_addr(decorators);134}135136RegMask& ZLoadBarrierStubC2::live() const {137return *barrier_set_state()->live(_node);138}139140Label* ZLoadBarrierStubC2::entry() {141// The _entry will never be bound when in_scratch_emit_size() is true.142// However, we still need to return a label that is not bound now, but143// will eventually be bound. Any lable will do, as it will only act as144// a placeholder, so we return the _continuation label.145return Compile::current()->output()->in_scratch_emit_size() ? &_continuation : &_entry;146}147148Label* ZLoadBarrierStubC2::continuation() {149return &_continuation;150}151152void* ZBarrierSetC2::create_barrier_state(Arena* comp_arena) const {153return new (comp_arena) ZBarrierSetC2State(comp_arena);154}155156void ZBarrierSetC2::late_barrier_analysis() const {157analyze_dominating_barriers();158compute_liveness_at_stubs();159}160161void ZBarrierSetC2::emit_stubs(CodeBuffer& cb) const {162MacroAssembler masm(&cb);163GrowableArray<ZLoadBarrierStubC2*>* const stubs = barrier_set_state()->stubs();164165for (int i = 0; i < stubs->length(); i++) {166// Make sure there is enough space in the code buffer167if (cb.insts()->maybe_expand_to_ensure_remaining(PhaseOutput::MAX_inst_size) && cb.blob() == NULL) {168ciEnv::current()->record_failure("CodeCache is full");169return;170}171172ZBarrierSet::assembler()->generate_c2_load_barrier_stub(&masm, stubs->at(i));173}174175masm.flush();176}177178int ZBarrierSetC2::estimate_stub_size() const {179Compile* const C = Compile::current();180BufferBlob* const blob = C->output()->scratch_buffer_blob();181GrowableArray<ZLoadBarrierStubC2*>* const stubs = barrier_set_state()->stubs();182int size = 0;183184for (int i = 0; i < stubs->length(); i++) {185CodeBuffer cb(blob->content_begin(), (address)C->output()->scratch_locs_memory() - blob->content_begin());186MacroAssembler masm(&cb);187ZBarrierSet::assembler()->generate_c2_load_barrier_stub(&masm, stubs->at(i));188size += cb.insts_size();189}190191return size;192}193194static void set_barrier_data(C2Access& access) {195if (ZBarrierSet::barrier_needed(access.decorators(), access.type())) {196uint8_t barrier_data = 0;197198if (access.decorators() & ON_PHANTOM_OOP_REF) {199barrier_data |= ZLoadBarrierPhantom;200} else if (access.decorators() & ON_WEAK_OOP_REF) {201barrier_data |= ZLoadBarrierWeak;202} else {203barrier_data |= ZLoadBarrierStrong;204}205206if (access.decorators() & AS_NO_KEEPALIVE) {207barrier_data |= ZLoadBarrierNoKeepalive;208}209210access.set_barrier_data(barrier_data);211}212}213214Node* ZBarrierSetC2::load_at_resolved(C2Access& access, const Type* val_type) const {215set_barrier_data(access);216return BarrierSetC2::load_at_resolved(access, val_type);217}218219Node* ZBarrierSetC2::atomic_cmpxchg_val_at_resolved(C2AtomicParseAccess& access, Node* expected_val,220Node* new_val, const Type* val_type) const {221set_barrier_data(access);222return BarrierSetC2::atomic_cmpxchg_val_at_resolved(access, expected_val, new_val, val_type);223}224225Node* ZBarrierSetC2::atomic_cmpxchg_bool_at_resolved(C2AtomicParseAccess& access, Node* expected_val,226Node* new_val, const Type* value_type) const {227set_barrier_data(access);228return BarrierSetC2::atomic_cmpxchg_bool_at_resolved(access, expected_val, new_val, value_type);229}230231Node* ZBarrierSetC2::atomic_xchg_at_resolved(C2AtomicParseAccess& access, Node* new_val, const Type* val_type) const {232set_barrier_data(access);233return BarrierSetC2::atomic_xchg_at_resolved(access, new_val, val_type);234}235236bool ZBarrierSetC2::array_copy_requires_gc_barriers(bool tightly_coupled_alloc, BasicType type,237bool is_clone, bool is_clone_instance,238ArrayCopyPhase phase) const {239if (phase == ArrayCopyPhase::Parsing) {240return false;241}242if (phase == ArrayCopyPhase::Optimization) {243return is_clone_instance;244}245// else ArrayCopyPhase::Expansion246return type == T_OBJECT || type == T_ARRAY;247}248249// This TypeFunc assumes a 64bit system250static const TypeFunc* clone_type() {251// Create input type (domain)252const Type** domain_fields = TypeTuple::fields(4);253domain_fields[TypeFunc::Parms + 0] = TypeInstPtr::NOTNULL; // src254domain_fields[TypeFunc::Parms + 1] = TypeInstPtr::NOTNULL; // dst255domain_fields[TypeFunc::Parms + 2] = TypeLong::LONG; // size lower256domain_fields[TypeFunc::Parms + 3] = Type::HALF; // size upper257const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms + 4, domain_fields);258259// Create result type (range)260const Type** range_fields = TypeTuple::fields(0);261const TypeTuple* range = TypeTuple::make(TypeFunc::Parms + 0, range_fields);262263return TypeFunc::make(domain, range);264}265266#define XTOP LP64_ONLY(COMMA phase->top())267268void ZBarrierSetC2::clone_at_expansion(PhaseMacroExpand* phase, ArrayCopyNode* ac) const {269Node* const src = ac->in(ArrayCopyNode::Src);270const TypeAryPtr* ary_ptr = src->get_ptr_type()->isa_aryptr();271272if (ac->is_clone_array() && ary_ptr != NULL) {273BasicType bt = ary_ptr->elem()->array_element_basic_type();274if (is_reference_type(bt)) {275// Clone object array276bt = T_OBJECT;277} else {278// Clone primitive array279bt = T_LONG;280}281282Node* ctrl = ac->in(TypeFunc::Control);283Node* mem = ac->in(TypeFunc::Memory);284Node* src = ac->in(ArrayCopyNode::Src);285Node* src_offset = ac->in(ArrayCopyNode::SrcPos);286Node* dest = ac->in(ArrayCopyNode::Dest);287Node* dest_offset = ac->in(ArrayCopyNode::DestPos);288Node* length = ac->in(ArrayCopyNode::Length);289290if (bt == T_OBJECT) {291// BarrierSetC2::clone sets the offsets via BarrierSetC2::arraycopy_payload_base_offset292// which 8-byte aligns them to allow for word size copies. Make sure the offsets point293// to the first element in the array when cloning object arrays. Otherwise, load294// barriers are applied to parts of the header. Also adjust the length accordingly.295assert(src_offset == dest_offset, "should be equal");296jlong offset = src_offset->get_long();297if (offset != arrayOopDesc::base_offset_in_bytes(T_OBJECT)) {298assert(!UseCompressedClassPointers, "should only happen without compressed class pointers");299assert((arrayOopDesc::base_offset_in_bytes(T_OBJECT) - offset) == BytesPerLong, "unexpected offset");300length = phase->transform_later(new SubLNode(length, phase->longcon(1))); // Size is in longs301src_offset = phase->longcon(arrayOopDesc::base_offset_in_bytes(T_OBJECT));302dest_offset = src_offset;303}304}305Node* payload_src = phase->basic_plus_adr(src, src_offset);306Node* payload_dst = phase->basic_plus_adr(dest, dest_offset);307308const char* copyfunc_name = "arraycopy";309address copyfunc_addr = phase->basictype2arraycopy(bt, NULL, NULL, true, copyfunc_name, true);310311const TypePtr* raw_adr_type = TypeRawPtr::BOTTOM;312const TypeFunc* call_type = OptoRuntime::fast_arraycopy_Type();313314Node* call = phase->make_leaf_call(ctrl, mem, call_type, copyfunc_addr, copyfunc_name, raw_adr_type, payload_src, payload_dst, length XTOP);315phase->transform_later(call);316317phase->igvn().replace_node(ac, call);318return;319}320321// Clone instance322Node* const ctrl = ac->in(TypeFunc::Control);323Node* const mem = ac->in(TypeFunc::Memory);324Node* const dst = ac->in(ArrayCopyNode::Dest);325Node* const size = ac->in(ArrayCopyNode::Length);326327assert(size->bottom_type()->is_long(), "Should be long");328329// The native clone we are calling here expects the instance size in words330// Add header/offset size to payload size to get instance size.331Node* const base_offset = phase->longcon(arraycopy_payload_base_offset(ac->is_clone_array()) >> LogBytesPerLong);332Node* const full_size = phase->transform_later(new AddLNode(size, base_offset));333334Node* const call = phase->make_leaf_call(ctrl,335mem,336clone_type(),337ZBarrierSetRuntime::clone_addr(),338"ZBarrierSetRuntime::clone",339TypeRawPtr::BOTTOM,340src,341dst,342full_size,343phase->top());344phase->transform_later(call);345phase->igvn().replace_node(ac, call);346}347348#undef XTOP349350// == Dominating barrier elision ==351352static bool block_has_safepoint(const Block* block, uint from, uint to) {353for (uint i = from; i < to; i++) {354if (block->get_node(i)->is_MachSafePoint()) {355// Safepoint found356return true;357}358}359360// Safepoint not found361return false;362}363364static bool block_has_safepoint(const Block* block) {365return block_has_safepoint(block, 0, block->number_of_nodes());366}367368static uint block_index(const Block* block, const Node* node) {369for (uint j = 0; j < block->number_of_nodes(); ++j) {370if (block->get_node(j) == node) {371return j;372}373}374ShouldNotReachHere();375return 0;376}377378void ZBarrierSetC2::analyze_dominating_barriers() const {379ResourceMark rm;380Compile* const C = Compile::current();381PhaseCFG* const cfg = C->cfg();382Block_List worklist;383Node_List mem_ops;384Node_List barrier_loads;385386// Step 1 - Find accesses, and track them in lists387for (uint i = 0; i < cfg->number_of_blocks(); ++i) {388const Block* const block = cfg->get_block(i);389for (uint j = 0; j < block->number_of_nodes(); ++j) {390const Node* const node = block->get_node(j);391if (!node->is_Mach()) {392continue;393}394395MachNode* const mach = node->as_Mach();396switch (mach->ideal_Opcode()) {397case Op_LoadP:398if ((mach->barrier_data() & ZLoadBarrierStrong) != 0) {399barrier_loads.push(mach);400}401if ((mach->barrier_data() & (ZLoadBarrierStrong | ZLoadBarrierNoKeepalive)) ==402ZLoadBarrierStrong) {403mem_ops.push(mach);404}405break;406case Op_CompareAndExchangeP:407case Op_CompareAndSwapP:408case Op_GetAndSetP:409if ((mach->barrier_data() & ZLoadBarrierStrong) != 0) {410barrier_loads.push(mach);411}412case Op_StoreP:413mem_ops.push(mach);414break;415416default:417break;418}419}420}421422// Step 2 - Find dominating accesses for each load423for (uint i = 0; i < barrier_loads.size(); i++) {424MachNode* const load = barrier_loads.at(i)->as_Mach();425const TypePtr* load_adr_type = NULL;426intptr_t load_offset = 0;427const Node* const load_obj = load->get_base_and_disp(load_offset, load_adr_type);428Block* const load_block = cfg->get_block_for_node(load);429const uint load_index = block_index(load_block, load);430431for (uint j = 0; j < mem_ops.size(); j++) {432MachNode* mem = mem_ops.at(j)->as_Mach();433const TypePtr* mem_adr_type = NULL;434intptr_t mem_offset = 0;435const Node* mem_obj = mem->get_base_and_disp(mem_offset, mem_adr_type);436Block* mem_block = cfg->get_block_for_node(mem);437uint mem_index = block_index(mem_block, mem);438439if (load_obj == NodeSentinel || mem_obj == NodeSentinel ||440load_obj == NULL || mem_obj == NULL ||441load_offset < 0 || mem_offset < 0) {442continue;443}444445if (mem_obj != load_obj || mem_offset != load_offset) {446// Not the same addresses, not a candidate447continue;448}449450if (load_block == mem_block) {451// Earlier accesses in the same block452if (mem_index < load_index && !block_has_safepoint(mem_block, mem_index + 1, load_index)) {453load->set_barrier_data(ZLoadBarrierElided);454}455} else if (mem_block->dominates(load_block)) {456// Dominating block? Look around for safepoints457ResourceMark rm;458Block_List stack;459VectorSet visited;460stack.push(load_block);461bool safepoint_found = block_has_safepoint(load_block);462while (!safepoint_found && stack.size() > 0) {463Block* block = stack.pop();464if (visited.test_set(block->_pre_order)) {465continue;466}467if (block_has_safepoint(block)) {468safepoint_found = true;469break;470}471if (block == mem_block) {472continue;473}474475// Push predecessor blocks476for (uint p = 1; p < block->num_preds(); ++p) {477Block* pred = cfg->get_block_for_node(block->pred(p));478stack.push(pred);479}480}481482if (!safepoint_found) {483load->set_barrier_data(ZLoadBarrierElided);484}485}486}487}488}489490// == Reduced spilling optimization ==491492void ZBarrierSetC2::compute_liveness_at_stubs() const {493ResourceMark rm;494Compile* const C = Compile::current();495Arena* const A = Thread::current()->resource_area();496PhaseCFG* const cfg = C->cfg();497PhaseRegAlloc* const regalloc = C->regalloc();498RegMask* const live = NEW_ARENA_ARRAY(A, RegMask, cfg->number_of_blocks() * sizeof(RegMask));499ZBarrierSetAssembler* const bs = ZBarrierSet::assembler();500Block_List worklist;501502for (uint i = 0; i < cfg->number_of_blocks(); ++i) {503new ((void*)(live + i)) RegMask();504worklist.push(cfg->get_block(i));505}506507while (worklist.size() > 0) {508const Block* const block = worklist.pop();509RegMask& old_live = live[block->_pre_order];510RegMask new_live;511512// Initialize to union of successors513for (uint i = 0; i < block->_num_succs; i++) {514const uint succ_id = block->_succs[i]->_pre_order;515new_live.OR(live[succ_id]);516}517518// Walk block backwards, computing liveness519for (int i = block->number_of_nodes() - 1; i >= 0; --i) {520const Node* const node = block->get_node(i);521522// Remove def bits523const OptoReg::Name first = bs->refine_register(node, regalloc->get_reg_first(node));524const OptoReg::Name second = bs->refine_register(node, regalloc->get_reg_second(node));525if (first != OptoReg::Bad) {526new_live.Remove(first);527}528if (second != OptoReg::Bad) {529new_live.Remove(second);530}531532// Add use bits533for (uint j = 1; j < node->req(); ++j) {534const Node* const use = node->in(j);535const OptoReg::Name first = bs->refine_register(use, regalloc->get_reg_first(use));536const OptoReg::Name second = bs->refine_register(use, regalloc->get_reg_second(use));537if (first != OptoReg::Bad) {538new_live.Insert(first);539}540if (second != OptoReg::Bad) {541new_live.Insert(second);542}543}544545// If this node tracks liveness, update it546RegMask* const regs = barrier_set_state()->live(node);547if (regs != NULL) {548regs->OR(new_live);549}550}551552// Now at block top, see if we have any changes553new_live.SUBTRACT(old_live);554if (new_live.is_NotEmpty()) {555// Liveness has refined, update and propagate to prior blocks556old_live.OR(new_live);557for (uint i = 1; i < block->num_preds(); ++i) {558Block* const pred = cfg->get_block_for_node(block->pred(i));559worklist.push(pred);560}561}562}563}564565566