view src/share/vm/gc_implementation/g1/g1CodeCacheRemSet.cpp @ 10905:f57189b7648d

8257192: Integrate AArch64 JIT port into 8u 7009641: Don't fail VM when CodeCache is full 8073108: [AArch64] Use x86 and SPARC CPU instructions for GHASH acceleration 8130309: Need to bailout cleanly if creation of stubs fails when codecache is out of space (AArch64 changes) 8131779: AARCH64: add Montgomery multiply intrinsic 8132875: AArch64: Fix error introduced into AArch64 CodeCache by commit for 8130309 8135018: AARCH64: Missing memory barriers for CMS collector 8145320: Create unsafe_arraycopy and generic_arraycopy for AArch64 8148328: aarch64: redundant lsr instructions in stub code. 8148783: aarch64: SEGV running SpecJBB2013 8148948: aarch64: generate_copy_longs calls align() incorrectly 8149080: AArch64: Recognise disjoint array copy in stub code 8149365: aarch64: memory copy does not prefetch on backwards copy 8149907: aarch64: use load/store pair instructions in call_stub 8150038: aarch64: make use of CBZ and CBNZ when comparing narrow pointer with zero 8150045: arraycopy causes segfaults in SATB during garbage collection 8150082: aarch64: optimise small array copy 8150229: aarch64: pipeline class for several instructions is not set correctly 8150313: aarch64: optimise array copy using SIMD instructions 8150394: aarch64: add support for 8.1 LSE CAS instructions 8150652: Remove unused code in AArch64 back end 8151340: aarch64: prefetch the destination word for write prior to ldxr/stxr loops. 8151502: optimize pd_disjoint_words and pd_conjoint_words 8151775: aarch64: add support for 8.1 LSE atomic operations 8152537: aarch64: Make use of CBZ and CBNZ when comparing unsigned values with zero. 8152840: aarch64: improve _unsafe_arraycopy stub routine 8153172: aarch64: hotspot crashes after the 8.1 LSE patch is merged 8153713: aarch64: improve short array clearing using store pair 8153797: aarch64: Add Arrays.fill stub code 8154413: AArch64: Better byte behaviour 8154537: AArch64: some integer rotate instructions are never emitted 8154739: AArch64: TemplateTable::fast_xaccess loads in wrong mode 8155015: Aarch64: bad assert in spill generation code 8155100: AArch64: Relax alignment requirement for byte_map_base 8155612: Aarch64: vector nodes need to support misaligned offset 8155617: aarch64: ClearArray does not use DC ZVA 8155627: Enable SA on AArch64 8155653: TestVectorUnalignedOffset.java not pushed with 8155612 8156731: aarch64: java/util/Arrays/Correct.java fails due to _generic_arraycopy stub routine 8157841: aarch64: prefetch ignores cache line size 8157906: aarch64: some more integer rotate instructions are never emitted 8158913: aarch64: SEGV running Spark terasort 8159052: aarch64: optimise unaligned copies in pd_disjoint_words and pd_conjoint_words 8159063: aarch64: optimise unaligned array copy long 8160748: [AArch64] Inconsistent types for ideal_reg 8161072: AArch64: jtreg compiler/uncommontrap/TestDeoptOOM failure 8161190: AArch64: Fix overflow in immediate cmp instruction 8164113: AArch64: follow-up the fix for 8161598 8165673: AArch64: Fix JNI floating point argument handling 8167200: AArch64: Broken stack pointer adjustment in interpreter 8167421: AArch64: in one core system, fatal error: Illegal threadstate encountered 8167595: AArch64: SEGV in stub code cipherBlockChaining_decryptAESCrypt 8168699: Validate special case invocations [AArch64 support] 8168888: Port 8160591: Improve internal array handling to AArch64. 8170100: AArch64: Crash in C1-compiled code accessing References 8170188: jtreg test compiler/types/TestMeetIncompatibleInterfaceArrays.java causes JVM crash 8170873: PPC64/aarch64: Poor StrictMath performance due to non-optimized compilation 8171537: aarch64: compiler/c1/Test6849574.java generates guarantee failure in C1 8172881: AArch64: assertion failure: the int pressure is incorrect 8173472: AArch64: C1 comparisons with null only use 32-bit instructions 8176100: [AArch64] [REDO][REDO] G1 Needs pre barrier on dereference of weak JNI handles 8177661: Correct ad rule output register types from iRegX to iRegXNoSp 8179954: AArch64: C1 and C2 volatile accesses are not sequentially consistent 8182581: aarch64: fix for crash caused by earlyret of compiled method 8183925: [AArch64] Decouple crash protection from watcher thread 8186325: AArch64: jtreg test hotspot/test/gc/g1/TestJNIWeakG1/TestJNIWeakG1.java SEGV 8187224: aarch64: some inconsistency between aarch64_ad.m4 and aarch64.ad 8189170: [AArch64] Add option to disable stack overflow checking in primordial thread for use with JNI_CreateJavaJVM 8193133: Assertion failure because 0xDEADDEAD can be in-heap 8195685: AArch64 port of 8174962: Better interface invocations 8195859: AArch64: vtableStubs gtest fails after 8174962 8196136: AArch64: Correct register use in patch for JDK-8194686 8196221: AArch64: Mistake in committed patch for JDK-8195859 8199712: [AArch64] Flight Recorder 8203481: Incorrect constraint for unextended_sp in frame:safe_for_sender 8203699: java/lang/invoke/SpecialInterfaceCall fails with SIGILL on aarch64 8205421: AARCH64: StubCodeMark should be placed after alignment 8206163: AArch64: incorrect code generation for StoreCM 8207345: Trampoline generation code reads from uninitialized memory 8207838: AArch64: Float registers incorrectly restored in JNI call 8209413: AArch64: NPE in clhsdb jstack command 8209414: [AArch64] method handle invocation does not respect JVMTI interp_only mode 8209415: Fix JVMTI test failure HS202 8209420: Track membars for volatile accesses so they can be properly optimized 8209835: Aarch64: elide barriers on all volatile operations 8210425: [AArch64] sharedRuntimeTrig/sharedRuntimeTrans compiled without optimization 8211064: [AArch64] Interpreter and c1 don't correctly handle jboolean results in native calls 8211233: MemBarNode::trailing_membar() and MemBarNode::leading_membar() need to handle dying subgraphs better 8213134: AArch64: vector shift failed with MaxVectorSize=8 8213419: [AArch64] C2 may hang in MulLNode::Ideal()/MulINode::Ideal() with gcc 8.2.1 8214857: "bad trailing membar" assert failure at memnode.cpp:3220 8215951: AArch64: jtreg test vmTestbase/nsk/jvmti/PopFrame/popframe005 segfaults 8215961: jdk/jfr/event/os/TestCPUInformation.java fails on AArch64 8216350: AArch64: monitor unlock fast path not called 8216989: CardTableBarrierSetAssembler::gen_write_ref_array_post_barrier() does not check for zero length on AARCH64 8217368: AArch64: C2 recursive stack locking optimisation not triggered 8218185: aarch64: missing LoadStore barrier in TemplateTable::putfield_or_static 8219011: Implement MacroAssembler::warn method on AArch64 8219635: aarch64: missing LoadStore barrier in TemplateTable::fast_storefield 8221220: AArch64: Add StoreStore membar explicitly for Volatile Writes in TemplateTable 8221658: aarch64: add necessary predicate for ubfx patterns 8224671: AArch64: mauve System.arraycopy test failure 8224828: aarch64: rflags is not correct after safepoint poll 8224851: AArch64: fix warnings and errors with Clang and GCC 8.3 8224880: AArch64: java/javac error with AllocatePrefetchDistance 8228400: Remove built-in AArch64 simulator 8228406: Superfluous change in chaitin.hpp 8228593: Revert explicit JDK 7 support additions 8228716: Revert InstanceKlass::print_on debug additions 8228718: Revert incorrect backport of JDK-8129757 to 8-aarch64 8228725: AArch64: Purge method call format support 8228747: Revert "unused" attribute from test_arraycopy_func 8228767: Revert ResourceMark additions 8228770: Revert development hsdis changes 8229123: Revert build fixes for aarch64/zero 8229124: Revert disassembler.cpp changes 8229145: Revert TemplateTable::bytecode() visibility change 8233839: aarch64: missing memory barrier in NewObjectArrayStub and NewTypeArrayStub 8237512: AArch64: aarch64TestHook leaks a BufferBlob 8246482: Build failures with +JFR -PCH 8247979: aarch64: missing side effect of killing flags for clearArray_reg_reg 8248219: aarch64: missing memory barrier in fast_storefield and fast_accessfield Reviewed-by: shade, aph
author andrew
date Mon, 01 Feb 2021 03:48:36 +0000
parents 3fa12c91c20a
children f79e943d15a7
line wrap: on
line source

/*
 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * 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.
 *
 */

#include "precompiled.hpp"
#include "code/codeCache.hpp"
#include "code/nmethod.hpp"
#include "gc_implementation/g1/g1CodeCacheRemSet.hpp"
#include "gc_implementation/g1/heapRegion.hpp"
#include "memory/heap.hpp"
#include "memory/iterator.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/hashtable.inline.hpp"
#include "utilities/stack.inline.hpp"

PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC

class CodeRootSetTable : public Hashtable<nmethod*, mtGC> {
  friend class G1CodeRootSetTest;
  typedef HashtableEntry<nmethod*, mtGC> Entry;

  static CodeRootSetTable* volatile _purge_list;

  CodeRootSetTable* _purge_next;

  unsigned int compute_hash(nmethod* nm) {
    uintptr_t hash = (uintptr_t)nm;
    return hash ^ (hash >> 7); // code heap blocks are 128byte aligned
  }

  void remove_entry(Entry* e, Entry* previous);
  Entry* new_entry(nmethod* nm);

 public:
  CodeRootSetTable(int size) : Hashtable<nmethod*, mtGC>(size, sizeof(Entry)), _purge_next(NULL) {}
  ~CodeRootSetTable();

  // Needs to be protected locks
  bool add(nmethod* nm);
  bool remove(nmethod* nm);

  // Can be called without locking
  bool contains(nmethod* nm);

  int entry_size() const { return BasicHashtable<mtGC>::entry_size(); }

  void copy_to(CodeRootSetTable* new_table);
  void nmethods_do(CodeBlobClosure* blk);

  template<typename CB>
  int remove_if(CB& should_remove);

  static void purge_list_append(CodeRootSetTable* tbl);
  static void purge();

  static size_t static_mem_size() {
    return sizeof(_purge_list);
  }

  size_t mem_size();
};

CodeRootSetTable* volatile CodeRootSetTable::_purge_list = NULL;

size_t CodeRootSetTable::mem_size() {
  return sizeof(CodeRootSetTable) + (entry_size() * number_of_entries()) + (sizeof(HashtableBucket<mtGC>) * table_size());
}

CodeRootSetTable::Entry* CodeRootSetTable::new_entry(nmethod* nm) {
  unsigned int hash = compute_hash(nm);
  Entry* entry = (Entry*) new_entry_free_list();
  if (entry == NULL) {
    entry = (Entry*) NEW_C_HEAP_ARRAY2(char, entry_size(), mtGC, CURRENT_PC);
  }
  entry->set_next(NULL);
  entry->set_hash(hash);
  entry->set_literal(nm);
  return entry;
}

void CodeRootSetTable::remove_entry(Entry* e, Entry* previous) {
  int index = hash_to_index(e->hash());
  assert((e == bucket(index)) == (previous == NULL), "if e is the first entry then previous should be null");

  if (previous == NULL) {
    set_entry(index, e->next());
  } else {
    previous->set_next(e->next());
  }
  free_entry(e);
}

CodeRootSetTable::~CodeRootSetTable() {
  for (int index = 0; index < table_size(); ++index) {
    for (Entry* e = bucket(index); e != NULL; ) {
      Entry* to_remove = e;
      // read next before freeing.
      e = e->next();
      unlink_entry(to_remove);
      FREE_C_HEAP_ARRAY(char, to_remove, mtGC);
    }
  }
  assert(number_of_entries() == 0, "should have removed all entries");
  free_buckets();
  for (BasicHashtableEntry<mtGC>* e = new_entry_free_list(); e != NULL; e = new_entry_free_list()) {
    FREE_C_HEAP_ARRAY(char, e, mtGC);
  }
}

bool CodeRootSetTable::add(nmethod* nm) {
  if (!contains(nm)) {
    Entry* e = new_entry(nm);
    int index = hash_to_index(e->hash());
    add_entry(index, e);
    return true;
  }
  return false;
}

bool CodeRootSetTable::contains(nmethod* nm) {
  int index = hash_to_index(compute_hash(nm));
  for (Entry* e = bucket(index); e != NULL; e = e->next()) {
    if (e->literal() == nm) {
      return true;
    }
  }
  return false;
}

bool CodeRootSetTable::remove(nmethod* nm) {
  int index = hash_to_index(compute_hash(nm));
  Entry* previous = NULL;
  for (Entry* e = bucket(index); e != NULL; previous = e, e = e->next()) {
    if (e->literal() == nm) {
      remove_entry(e, previous);
      return true;
    }
  }
  return false;
}

void CodeRootSetTable::copy_to(CodeRootSetTable* new_table) {
  for (int index = 0; index < table_size(); ++index) {
    for (Entry* e = bucket(index); e != NULL; e = e->next()) {
      new_table->add(e->literal());
    }
  }
  new_table->copy_freelist(this);
}

void CodeRootSetTable::nmethods_do(CodeBlobClosure* blk) {
  for (int index = 0; index < table_size(); ++index) {
    for (Entry* e = bucket(index); e != NULL; e = e->next()) {
      blk->do_code_blob(e->literal());
    }
  }
}

template<typename CB>
int CodeRootSetTable::remove_if(CB& should_remove) {
  int num_removed = 0;
  for (int index = 0; index < table_size(); ++index) {
    Entry* previous = NULL;
    Entry* e = bucket(index);
    while (e != NULL) {
      Entry* next = e->next();
      if (should_remove(e->literal())) {
        remove_entry(e, previous);
        ++num_removed;
      } else {
        previous = e;
      }
      e = next;
    }
  }
  return num_removed;
}

G1CodeRootSet::~G1CodeRootSet() {
  delete _table;
}

CodeRootSetTable* G1CodeRootSet::load_acquire_table() {
  return (CodeRootSetTable*) OrderAccess::load_ptr_acquire(&_table);
}

void G1CodeRootSet::allocate_small_table() {
  CodeRootSetTable* temp = new CodeRootSetTable(SmallSize);

  OrderAccess::release_store_ptr(&_table, temp);
}

void CodeRootSetTable::purge_list_append(CodeRootSetTable* table) {
  for (;;) {
    table->_purge_next = _purge_list;
    CodeRootSetTable* old = (CodeRootSetTable*) Atomic::cmpxchg_ptr(table, &_purge_list, table->_purge_next);
    if (old == table->_purge_next) {
      break;
    }
  }
}

void CodeRootSetTable::purge() {
  CodeRootSetTable* table = _purge_list;
  _purge_list = NULL;
  while (table != NULL) {
    CodeRootSetTable* to_purge = table;
    table = table->_purge_next;
    delete to_purge;
  }
}

void G1CodeRootSet::move_to_large() {
  CodeRootSetTable* temp = new CodeRootSetTable(LargeSize);

  _table->copy_to(temp);

  CodeRootSetTable::purge_list_append(_table);

  OrderAccess::release_store_ptr(&_table, temp);
}

void G1CodeRootSet::purge() {
  CodeRootSetTable::purge();
}

size_t G1CodeRootSet::static_mem_size() {
  return CodeRootSetTable::static_mem_size();
}

void G1CodeRootSet::add(nmethod* method) {
  bool added = false;
  if (is_empty()) {
    allocate_small_table();
  }
  added = _table->add(method);
  if (added) {
    if (_length == Threshold) {
      move_to_large();
    }
    ++_length;
  }
  assert(_length == (size_t)_table->number_of_entries(), "sizes should match");
}

bool G1CodeRootSet::remove(nmethod* method) {
  bool removed = false;
  if (_table != NULL) {
    removed = _table->remove(method);
  }
  if (removed) {
    _length--;
    if (_length == 0) {
      clear();
    }
  }
  assert((_length == 0 && _table == NULL) ||
         (_length == (size_t)_table->number_of_entries()), "sizes should match");
  return removed;
}

bool G1CodeRootSet::contains(nmethod* method) {
  CodeRootSetTable* table = load_acquire_table(); // contains() may be called outside of lock, so ensure mem sync.
  if (table != NULL) {
    return table->contains(method);
  }
  return false;
}

void G1CodeRootSet::clear() {
  delete _table;
  _table = NULL;
  _length = 0;
}

size_t G1CodeRootSet::mem_size() {
  return sizeof(*this) + (_table != NULL ? _table->mem_size() : 0);
}

void G1CodeRootSet::nmethods_do(CodeBlobClosure* blk) const {
  if (_table != NULL) {
    _table->nmethods_do(blk);
  }
}

class CleanCallback : public StackObj {
  class PointsIntoHRDetectionClosure : public OopClosure {
    HeapRegion* _hr;
   public:
    bool _points_into;
    PointsIntoHRDetectionClosure(HeapRegion* hr) : _hr(hr), _points_into(false) {}

    void do_oop(narrowOop* o) {
      do_oop_work(o);
    }

    void do_oop(oop* o) {
      do_oop_work(o);
    }

    template <typename T>
    void do_oop_work(T* p) {
      if (_hr->is_in(oopDesc::load_decode_heap_oop(p))) {
        _points_into = true;
      }
    }
  };

  PointsIntoHRDetectionClosure _detector;
  CodeBlobToOopClosure _blobs;

 public:
  CleanCallback(HeapRegion* hr) : _detector(hr), _blobs(&_detector, !CodeBlobToOopClosure::FixRelocations) {}

  bool operator() (nmethod* nm) {
    _detector._points_into = false;
    _blobs.do_code_blob(nm);
    return !_detector._points_into;
  }
};

void G1CodeRootSet::clean(HeapRegion* owner) {
  CleanCallback should_clean(owner);
  if (_table != NULL) {
    int removed = _table->remove_if(should_clean);
    assert((size_t)removed <= _length, "impossible");
    _length -= removed;
  }
  if (_length == 0) {
    clear();
  }
}

#ifndef PRODUCT

class G1CodeRootSetTest {
 public:
  static void test() {
    {
      G1CodeRootSet set1;
      assert(set1.is_empty(), "Code root set must be initially empty but is not.");

      assert(G1CodeRootSet::static_mem_size() == sizeof(void*),
          err_msg("The code root set's static memory usage is incorrect, " SIZE_FORMAT " bytes", G1CodeRootSet::static_mem_size()));

      set1.add((nmethod*)1);
      assert(set1.length() == 1, err_msg("Added exactly one element, but set contains "
          SIZE_FORMAT " elements", set1.length()));

      const size_t num_to_add = (size_t)G1CodeRootSet::Threshold + 1;

      for (size_t i = 1; i <= num_to_add; i++) {
        set1.add((nmethod*)1);
      }
      assert(set1.length() == 1,
          err_msg("Duplicate detection should not have increased the set size but "
              "is " SIZE_FORMAT, set1.length()));

      for (size_t i = 2; i <= num_to_add; i++) {
        set1.add((nmethod*)(uintptr_t)(i));
      }
      assert(set1.length() == num_to_add,
          err_msg("After adding in total " SIZE_FORMAT " distinct code roots, they "
              "need to be in the set, but there are only " SIZE_FORMAT,
              num_to_add, set1.length()));

      assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");

      size_t num_popped = 0;
      for (size_t i = 1; i <= num_to_add; i++) {
        bool removed = set1.remove((nmethod*)i);
        if (removed) {
          num_popped += 1;
        } else {
          break;
        }
      }
      assert(num_popped == num_to_add,
          err_msg("Managed to pop " SIZE_FORMAT " code roots, but only " SIZE_FORMAT " "
              "were added", num_popped, num_to_add));
      assert(CodeRootSetTable::_purge_list != NULL, "should have grown to large hashtable");

      G1CodeRootSet::purge();

      assert(CodeRootSetTable::_purge_list == NULL, "should have purged old small tables");

    }

  }
};

void TestCodeCacheRemSet_test() {
  G1CodeRootSetTest::test();
}

#endif