8336999: Verification for resource area allocated data structures in C2

Reviewed-by: chagedorn, kvn
This commit is contained in:
Tobias Hartmann 2024-07-29 05:31:10 +00:00
parent 90641a601c
commit 657c0bddf9
14 changed files with 48 additions and 24 deletions

@ -132,6 +132,7 @@ class CompileReplay : public StackObj {
char* _bufptr;
char* _buffer;
int _buffer_length;
ReallocMark _nesting; // Safety checks for arena reallocation
// "compile" data
ciKlass* _iklass;
@ -601,6 +602,7 @@ class CompileReplay : public StackObj {
int buffer_pos = 0;
while(c != EOF) {
if (buffer_pos + 1 >= _buffer_length) {
_nesting.check(); // Check if a reallocation in the resource arena is safe
int new_length = _buffer_length * 2;
// Next call will throw error in case of OOM.
_buffer = REALLOC_RESOURCE_ARRAY(char, _buffer, _buffer_length, new_length);

@ -48,6 +48,10 @@ void VectorSet::init(Arena* arena) {
// Expand the existing set to a bigger size
void VectorSet::grow(uint new_word_capacity) {
_nesting.check(_set_arena); // Check if a potential reallocation in the arena is safe
if (new_word_capacity < _size) {
return; // No need to grow
}
assert(new_word_capacity < (1U << 30), "");
uint x = next_power_of_2(new_word_capacity);
if (x > _data_size) {

@ -46,6 +46,7 @@ private:
// Allocated words
uint _data_size;
Arena* _set_arena;
ReallocMark _nesting; // Safety checks for arena reallocation
void init(Arena* arena);
// Grow vector to required word capacity
@ -77,10 +78,7 @@ public:
//
bool test_set(uint elem) {
uint32_t word = elem >> word_bits;
if (word >= _size) {
// Then grow
grow(word);
}
grow(word);
uint32_t mask = 1U << (elem & bit_mask);
uint32_t data = _data[word];
_data[word] = data | mask;
@ -109,9 +107,7 @@ public:
// Fast inlined set
void set(uint elem) {
uint32_t word = elem >> word_bits;
if (word >= _size) {
grow(word);
}
grow(word);
uint32_t mask = 1U << (elem & bit_mask);
_data[word] |= mask;
}

@ -237,9 +237,10 @@ ReallocMark::ReallocMark() {
#endif
}
void ReallocMark::check() {
void ReallocMark::check(Arena* arena) {
#ifdef ASSERT
if (_nesting != Thread::current()->resource_area()->nesting()) {
if ((arena == nullptr || arena == Thread::current()->resource_area()) &&
_nesting != Thread::current()->resource_area()->nesting()) {
fatal("allocation bug: array could grow within nested ResourceMark");
}
#endif

@ -556,8 +556,8 @@ protected:
NOT_PRODUCT(int _nesting;)
public:
ReallocMark() PRODUCT_RETURN;
void check() PRODUCT_RETURN;
ReallocMark() PRODUCT_RETURN;
void check(Arena* arena = nullptr) PRODUCT_RETURN;
};
// Uses mmapped memory for all allocations. All allocations are initially

@ -39,7 +39,10 @@
#include "utilities/powerOfTwo.hpp"
void Block_Array::grow( uint i ) {
assert(i >= Max(), "must be an overflow");
_nesting.check(_arena); // Check if a potential reallocation in the arena is safe
if (i < Max()) {
return; // No need to grow
}
debug_only(_limit = i+1);
if( i < _size ) return;
if( !_size ) {
@ -374,6 +377,7 @@ void Block::dump(const PhaseCFG* cfg) const {
PhaseCFG::PhaseCFG(Arena* arena, RootNode* root, Matcher& matcher)
: Phase(CFG)
, _root(root)
, _blocks(arena)
, _block_arena(arena)
, _regalloc(nullptr)
, _scheduling_for_pressure(false)
@ -1417,7 +1421,7 @@ UnionFind::UnionFind( uint max ) : _cnt(max), _max(max), _indices(NEW_RESOURCE_A
}
void UnionFind::extend( uint from_idx, uint to_idx ) {
_nesting.check();
_nesting.check(); // Check if a potential reallocation in the resource arena is safe
if( from_idx >= _max ) {
uint size = 16;
while( size <= from_idx ) size <<=1;

@ -51,6 +51,7 @@ class Block_Array : public ArenaObj {
uint _size; // allocated size, as opposed to formal limit
debug_only(uint _limit;) // limit to formal domain
Arena *_arena; // Arena to allocate in
ReallocMark _nesting; // Safety checks for arena reallocation
protected:
Block **_blocks;
void grow( uint i ); // Grow array node to fit
@ -68,7 +69,7 @@ public:
Block *operator[] ( uint i ) const // Lookup, or assert for not mapped
{ assert( i < Max(), "oob" ); return _blocks[i]; }
// Extend the mapping: index i maps to Block *n.
void map( uint i, Block *n ) { if( i>=Max() ) grow(i); _blocks[i] = n; }
void map( uint i, Block *n ) { grow(i); _blocks[i] = n; }
uint Max() const { debug_only(return _limit); return _size; }
};
@ -77,7 +78,9 @@ class Block_List : public Block_Array {
friend class VMStructs;
public:
uint _cnt;
Block_List() : Block_Array(Thread::current()->resource_area()), _cnt(0) {}
Block_List() : Block_List(Thread::current()->resource_area()) { }
Block_List(Arena* a) : Block_Array(a), _cnt(0) { }
void push( Block *b ) { map(_cnt++,b); }
Block *pop() { return _blocks[--_cnt]; }
Block *rpop() { Block *b = _blocks[0]; _blocks[0]=_blocks[--_cnt]; return b;}
@ -655,7 +658,7 @@ class PhaseCFG : public Phase {
class UnionFind : public ResourceObj {
uint _cnt, _max;
uint* _indices;
ReallocMark _nesting; // assertion check for reallocations
ReallocMark _nesting; // Safety checks for arena reallocation
public:
UnionFind( uint max );
void reset( uint max ); // Reset to identity map for [0..max]

@ -61,9 +61,6 @@ struct Tarjan {
// Compute the dominator tree of the CFG. The CFG must already have been
// constructed. This is the Lengauer & Tarjan O(E-alpha(E,V)) algorithm.
void PhaseCFG::build_dominator_tree() {
// Pre-grow the blocks array, prior to the ResourceMark kicking in
_blocks.map(number_of_blocks(), nullptr);
ResourceMark rm;
// Setup mappings from my Graph to Tarjan's stuff and back
// Note: Tarjan uses 1-based arrays

@ -5248,6 +5248,7 @@ bool IdealLoopTree::verify_tree(IdealLoopTree* loop_verify) const {
//------------------------------set_idom---------------------------------------
void PhaseIdealLoop::set_idom(Node* d, Node* n, uint dom_depth) {
_nesting.check(); // Check if a potential reallocation in the resource arena is safe
uint idx = d->_idx;
if (idx >= _idom_size) {
uint newsize = next_power_of_2(idx);

@ -841,6 +841,8 @@ class PhaseIdealLoop : public PhaseTransform {
uint *_preorders;
uint _max_preorder;
ReallocMark _nesting; // Safety checks for arena reallocation
const PhaseIdealLoop* _verify_me;
bool _verify_only;
@ -853,6 +855,7 @@ class PhaseIdealLoop : public PhaseTransform {
// Allocate _preorders[] array
void reallocate_preorders() {
_nesting.check(); // Check if a potential re-allocation in the resource arena is safe
if ( _max_preorder < C->unique() ) {
_preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, C->unique());
_max_preorder = C->unique();
@ -863,6 +866,7 @@ class PhaseIdealLoop : public PhaseTransform {
// Check to grow _preorders[] array for the case when build_loop_tree_impl()
// adds new nodes.
void check_grow_preorders( ) {
_nesting.check(); // Check if a potential re-allocation in the resource arena is safe
if ( _max_preorder < C->unique() ) {
uint newsize = _max_preorder<<1; // double size of array
_preorders = REALLOC_RESOURCE_ARRAY(uint, _preorders, _max_preorder, newsize);

@ -2766,6 +2766,10 @@ const RegMask &Node::in_RegMask(uint) const {
}
void Node_Array::grow(uint i) {
_nesting.check(_a); // Check if a potential reallocation in the arena is safe
if (i < _max) {
return; // No need to grow
}
assert(_max > 0, "invariant");
uint old = _max;
_max = next_power_of_2(i);
@ -2973,6 +2977,10 @@ void Unique_Node_List::remove_useless_nodes(VectorSet &useful) {
//=============================================================================
void Node_Stack::grow() {
_nesting.check(_a); // Check if a potential reallocation in the arena is safe
if (_inode_top < _inode_max) {
return; // No need to grow
}
size_t old_top = pointer_delta(_inode_top,_inodes,sizeof(INode)); // save _top
size_t old_max = pointer_delta(_inode_max,_inodes,sizeof(INode));
size_t max = old_max << 1; // max * 2

@ -1596,6 +1596,8 @@ protected:
Arena* _a; // Arena to allocate in
uint _max;
Node** _nodes;
ReallocMark _nesting; // Safety checks for arena reallocation
void grow( uint i ); // Grow array node to fit
public:
Node_Array(Arena* a, uint max = OptoNodeListSize) : _a(a), _max(max) {
@ -1614,7 +1616,7 @@ public:
Node* at(uint i) const { assert(i<_max,"oob"); return _nodes[i]; }
Node** adr() { return _nodes; }
// Extend the mapping: index i maps to Node *n.
void map( uint i, Node *n ) { if( i>=_max ) grow(i); _nodes[i] = n; }
void map( uint i, Node *n ) { grow(i); _nodes[i] = n; }
void insert( uint i, Node *n );
void remove( uint i ); // Remove, preserving order
// Clear all entries in _nodes to null but keep storage
@ -1844,6 +1846,7 @@ protected:
INode *_inode_max; // End of _inodes == _inodes + _max
INode *_inodes; // Array storage for the stack
Arena *_a; // Arena to allocate in
ReallocMark _nesting; // Safety checks for arena reallocation
void grow();
public:
Node_Stack(int size) {
@ -1867,7 +1870,7 @@ public:
}
void push(Node *n, uint i) {
++_inode_top;
if (_inode_top >= _inode_max) grow();
grow();
INode *top = _inode_top; // optimization
top->node = n;
top->indx = i;

@ -2351,6 +2351,7 @@ void Node::replace_by(Node *new_node) {
//=============================================================================
//-----------------------------------------------------------------------------
void Type_Array::grow( uint i ) {
assert(_a == Compile::current()->comp_arena(), "Should be allocated in comp_arena");
if( !_max ) {
_max = 1;
_types = (const Type**)_a->Amalloc( _max * sizeof(Type*) );

@ -184,7 +184,7 @@ void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) {
// Process merged VBAs
if (EnableVectorAggressiveReboxing) {
Unique_Node_List calls(C->comp_arena());
Unique_Node_List calls;
for (DUIterator_Fast imax, i = vec_box->fast_outs(imax); i < imax; i++) {
Node* use = vec_box->fast_out(i);
if (use->is_CallJava()) {
@ -238,9 +238,9 @@ void PhaseVector::scalarize_vbox_node(VectorBoxNode* vec_box) {
}
// Process debug uses at safepoints
Unique_Node_List safepoints(C->comp_arena());
Unique_Node_List safepoints;
Unique_Node_List worklist(C->comp_arena());
Unique_Node_List worklist;
worklist.push(vec_box);
while (worklist.size() > 0) {
Node* n = worklist.pop();