Skip to content

Commit 2d30a10

Browse files
committedNov 26, 2020
8257069: C2: Clarify and sanity test RegMask/RegMaskIterator
Reviewed-by: jvernee, kvn
1 parent 0a5de50 commit 2d30a10

File tree

3 files changed

+214
-34
lines changed

3 files changed

+214
-34
lines changed
 

‎src/hotspot/share/adlc/output_h.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ void ArchDesc::buildMachRegisterNumbers(FILE *fp_hpp) {
9898
}
9999

100100
fprintf(fp_hpp, "\n// Size of register-mask in ints\n");
101-
fprintf(fp_hpp, "#define RM_SIZE %d\n",RegisterForm::RegMask_Size());
101+
fprintf(fp_hpp, "#define RM_SIZE %d\n", RegisterForm::RegMask_Size());
102102
fprintf(fp_hpp, "// Unroll factor for loops over the data in a RegMask\n");
103103
fprintf(fp_hpp, "#define FORALL_BODY ");
104104
int len = RegisterForm::RegMask_Size();

‎src/hotspot/share/opto/regmask.hpp

+57-33
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@ class RegMask {
5858

5959
friend class RegMaskIterator;
6060

61-
enum {
62-
_WordBits = BitsPerWord,
63-
_LogWordBits = LogBitsPerWord,
64-
_RM_SIZE = LP64_ONLY(align_up(RM_SIZE, 2) >> 1) NOT_LP64(RM_SIZE)
65-
};
61+
// The RM_SIZE is aligned to 64-bit - assert that this holds
62+
LP64_ONLY(STATIC_ASSERT(is_aligned(RM_SIZE, 2)));
63+
64+
static const unsigned int _WordBitMask = BitsPerWord - 1U;
65+
static const unsigned int _LogWordBits = LogBitsPerWord;
66+
static const unsigned int _RM_SIZE = LP64_ONLY(RM_SIZE >> 1) NOT_LP64(RM_SIZE);
67+
static const unsigned int _RM_MAX = _RM_SIZE - 1U;
6668

6769
union {
6870
// Array of Register Mask bits. This array is large enough to cover
@@ -82,7 +84,7 @@ class RegMask {
8284
unsigned int _hwm;
8385

8486
public:
85-
enum { CHUNK_SIZE = RM_SIZE*BitsPerInt };
87+
enum { CHUNK_SIZE = _RM_SIZE * BitsPerWord };
8688

8789
// SlotsPerLong is 2, since slots are 32 bits and longs are 64 bits.
8890
// Also, consider the maximum alignment size for a normally allocated
@@ -121,7 +123,7 @@ class RegMask {
121123
FORALL_BODY
122124
# undef BODY
123125
_lwm = 0;
124-
_hwm = _RM_SIZE - 1;
126+
_hwm = _RM_MAX;
125127
while (_hwm > 0 && _RM_UP[_hwm] == 0) _hwm--;
126128
while ((_lwm < _hwm) && _RM_UP[_lwm] == 0) _lwm++;
127129
assert(valid_watermarks(), "post-condition");
@@ -138,7 +140,7 @@ class RegMask {
138140
}
139141

140142
// Construct an empty mask
141-
RegMask() : _RM_UP(), _lwm(_RM_SIZE - 1), _hwm(0) {
143+
RegMask() : _RM_UP(), _lwm(_RM_MAX), _hwm(0) {
142144
assert(valid_watermarks(), "post-condition");
143145
}
144146

@@ -152,15 +154,19 @@ class RegMask {
152154
assert(reg < CHUNK_SIZE, "");
153155

154156
unsigned r = (unsigned)reg;
155-
return _RM_UP[r >> _LogWordBits] & (uintptr_t(1) <<(r & (_WordBits - 1U)));
157+
return _RM_UP[r >> _LogWordBits] & (uintptr_t(1) << (r & _WordBitMask));
156158
}
157159

158160
// The last bit in the register mask indicates that the mask should repeat
159161
// indefinitely with ONE bits. Returns TRUE if mask is infinite or
160162
// unbounded in size. Returns FALSE if mask is finite size.
161-
bool is_AllStack() const { return _RM_UP[_RM_SIZE - 1U] >> (_WordBits - 1U); }
163+
bool is_AllStack() const {
164+
return (_RM_UP[_RM_MAX] & (uintptr_t(1) << _WordBitMask)) != 0;
165+
}
162166

163-
void set_AllStack() { Insert(OptoReg::Name(CHUNK_SIZE-1)); }
167+
void set_AllStack() {
168+
_RM_UP[_RM_MAX] |= (uintptr_t(1) << _WordBitMask);
169+
}
164170

165171
// Test for being a not-empty mask.
166172
bool is_NotEmpty() const {
@@ -178,7 +184,7 @@ class RegMask {
178184
for (unsigned i = _lwm; i <= _hwm; i++) {
179185
uintptr_t bits = _RM_UP[i];
180186
if (bits) {
181-
return OptoReg::Name((i<<_LogWordBits) + find_lowest_bit(bits));
187+
return OptoReg::Name((i << _LogWordBits) + find_lowest_bit(bits));
182188
}
183189
}
184190
return OptoReg::Name(OptoReg::Bad);
@@ -192,7 +198,7 @@ class RegMask {
192198
while (i > _lwm) {
193199
uintptr_t bits = _RM_UP[--i];
194200
if (bits) {
195-
return OptoReg::Name((i<<_LogWordBits) + find_highest_bit(bits));
201+
return OptoReg::Name((i << _LogWordBits) + find_highest_bit(bits));
196202
}
197203
}
198204
return OptoReg::Name(OptoReg::Bad);
@@ -270,17 +276,17 @@ class RegMask {
270276

271277
// Clear a register mask
272278
void Clear() {
273-
_lwm = _RM_SIZE - 1;
279+
_lwm = _RM_MAX;
274280
_hwm = 0;
275-
memset(_RM_UP, 0, sizeof(uintptr_t)*_RM_SIZE);
281+
memset(_RM_UP, 0, sizeof(uintptr_t) * _RM_SIZE);
276282
assert(valid_watermarks(), "sanity");
277283
}
278284

279285
// Fill a register mask with 1's
280286
void Set_All() {
281287
_lwm = 0;
282-
_hwm = _RM_SIZE - 1;
283-
memset(_RM_UP, 0xFF, sizeof(uintptr_t)*_RM_SIZE);
288+
_hwm = _RM_MAX;
289+
memset(_RM_UP, 0xFF, sizeof(uintptr_t) * _RM_SIZE);
284290
assert(valid_watermarks(), "sanity");
285291
}
286292

@@ -294,15 +300,15 @@ class RegMask {
294300
unsigned index = r >> _LogWordBits;
295301
if (index > _hwm) _hwm = index;
296302
if (index < _lwm) _lwm = index;
297-
_RM_UP[index] |= (uintptr_t(1) << (r & (_WordBits - 1U)));
303+
_RM_UP[index] |= (uintptr_t(1) << (r & _WordBitMask));
298304
assert(valid_watermarks(), "post-condition");
299305
}
300306

301307
// Remove register from mask
302308
void Remove(OptoReg::Name reg) {
303309
assert(reg < CHUNK_SIZE, "");
304310
unsigned r = (unsigned)reg;
305-
_RM_UP[r >> _LogWordBits] &= ~(uintptr_t(1) << (r & (_WordBits-1U)));
311+
_RM_UP[r >> _LogWordBits] &= ~(uintptr_t(1) << (r & _WordBitMask));
306312
}
307313

308314
// OR 'rm' into 'this'
@@ -355,23 +361,23 @@ class RegMask {
355361
// NOTE: -1 in computation reflects the usage of the last
356362
// bit of the regmask as an infinite stack flag and
357363
// -7 is to keep mask aligned for largest value (VecZ).
358-
return (int)reg < (int)(CHUNK_SIZE-1);
364+
return (int)reg < (int)(CHUNK_SIZE - 1);
359365
}
360366
static bool can_represent_arg(OptoReg::Name reg) {
361367
// NOTE: -SlotsPerVecZ in computation reflects the need
362368
// to keep mask aligned for largest value (VecZ).
363-
return (int)reg < (int)(CHUNK_SIZE-SlotsPerVecZ);
369+
return (int)reg < (int)(CHUNK_SIZE - SlotsPerVecZ);
364370
}
365371
};
366372

367373
class RegMaskIterator {
368374
private:
369-
uintptr_t _current_word;
375+
uintptr_t _current_bits;
370376
unsigned int _next_index;
371377
OptoReg::Name _reg;
372-
const RegMask& _rm;
378+
const RegMask& _rm;
373379
public:
374-
RegMaskIterator(const RegMask& rm) : _current_word(0), _next_index(rm._lwm), _reg(OptoReg::Special), _rm(rm) {
380+
RegMaskIterator(const RegMask& rm) : _current_bits(0), _next_index(rm._lwm), _reg(OptoReg::Bad), _rm(rm) {
375381
// Calculate the first element
376382
next();
377383
}
@@ -383,26 +389,44 @@ class RegMaskIterator {
383389
// Get the current element and calculate the next
384390
OptoReg::Name next() {
385391
OptoReg::Name r = _reg;
386-
if (_current_word != 0) {
387-
unsigned int next_bit = find_lowest_bit(_current_word);
392+
393+
// This bit shift scheme, borrowed from IndexSetIterator,
394+
// shifts the _current_bits down by the number of trailing
395+
// zeros - which leaves the "current" bit on position zero,
396+
// then subtracts by 1 to clear it. This quirk avoids the
397+
// undefined behavior that could arise if trying to shift
398+
// away the bit with a single >> (next_bit + 1) shift when
399+
// next_bit is 31/63. It also keeps number of shifts and
400+
// arithmetic ops to a minimum.
401+
402+
// We have previously found bits at _next_index - 1, and
403+
// still have some left at the same index.
404+
if (_current_bits != 0) {
405+
unsigned int next_bit = find_lowest_bit(_current_bits);
406+
assert(_reg != OptoReg::Bad, "can't be in a bad state");
388407
assert(next_bit > 0, "must be");
389-
assert(((_current_word >> next_bit) & 0x1) == 1, "sanity");
390-
_current_word = (_current_word >> next_bit) - 1;
408+
assert(((_current_bits >> next_bit) & 0x1) == 1, "lowest bit must be set after shift");
409+
_current_bits = (_current_bits >> next_bit) - 1;
391410
_reg = OptoReg::add(_reg, next_bit);
392411
return r;
393412
}
394413

414+
// Find the next word with bits
395415
while (_next_index <= _rm._hwm) {
396-
_current_word = _rm._RM_UP[_next_index++];
397-
if (_current_word != 0) {
398-
unsigned int next_bit = find_lowest_bit(_current_word);
399-
assert(((_current_word >> next_bit) & 0x1) == 1, "sanity");
400-
_current_word = (_current_word >> next_bit) - 1;
416+
_current_bits = _rm._RM_UP[_next_index++];
417+
if (_current_bits != 0) {
418+
// Found a word. Calculate the first register element and
419+
// prepare _current_bits by shifting it down and clearing
420+
// the lowest bit
421+
unsigned int next_bit = find_lowest_bit(_current_bits);
422+
assert(((_current_bits >> next_bit) & 0x1) == 1, "lowest bit must be set after shift");
423+
_current_bits = (_current_bits >> next_bit) - 1;
401424
_reg = OptoReg::Name(((_next_index - 1) << RegMask::_LogWordBits) + next_bit);
402425
return r;
403426
}
404427
}
405428

429+
// No more bits
406430
_reg = OptoReg::Name(OptoReg::Bad);
407431
return r;
408432
}
+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
#include "precompiled.hpp"
26+
#include "opto/regmask.hpp"
27+
#include "unittest.hpp"
28+
29+
// Sanity tests for RegMask and RegMaskIterator
30+
31+
static void contains_expected_num_of_registers(const RegMask& rm, unsigned int expected) {
32+
33+
ASSERT_TRUE(rm.Size() == expected);
34+
if (expected > 0) {
35+
ASSERT_TRUE(rm.is_NotEmpty());
36+
} else {
37+
ASSERT_TRUE(!rm.is_NotEmpty());
38+
ASSERT_TRUE(!rm.is_AllStack());
39+
}
40+
41+
RegMaskIterator rmi(rm);
42+
unsigned int count = 0;
43+
OptoReg::Name reg = OptoReg::Bad;
44+
while (rmi.has_next()) {
45+
reg = rmi.next();
46+
ASSERT_TRUE(OptoReg::is_valid(reg));
47+
count++;
48+
}
49+
ASSERT_EQ(OptoReg::Bad, rmi.next());
50+
ASSERT_TRUE(count == expected);
51+
}
52+
53+
TEST_VM(RegMask, empty) {
54+
RegMask rm;
55+
contains_expected_num_of_registers(rm, 0);
56+
}
57+
58+
TEST_VM(RegMask, iteration) {
59+
RegMask rm;
60+
rm.Insert(30);
61+
rm.Insert(31);
62+
rm.Insert(32);
63+
rm.Insert(33);
64+
rm.Insert(62);
65+
rm.Insert(63);
66+
rm.Insert(64);
67+
rm.Insert(65);
68+
69+
RegMaskIterator rmi(rm);
70+
ASSERT_TRUE(rmi.next() == OptoReg::Name(30));
71+
ASSERT_TRUE(rmi.next() == OptoReg::Name(31));
72+
ASSERT_TRUE(rmi.next() == OptoReg::Name(32));
73+
ASSERT_TRUE(rmi.next() == OptoReg::Name(33));
74+
ASSERT_TRUE(rmi.next() == OptoReg::Name(62));
75+
ASSERT_TRUE(rmi.next() == OptoReg::Name(63));
76+
ASSERT_TRUE(rmi.next() == OptoReg::Name(64));
77+
ASSERT_TRUE(rmi.next() == OptoReg::Name(65));
78+
ASSERT_FALSE(rmi.has_next());
79+
}
80+
81+
TEST_VM(RegMask, Set_ALL) {
82+
// Check that Set_All doesn't add bits outside of CHUNK_SIZE
83+
RegMask rm;
84+
rm.Set_All();
85+
ASSERT_TRUE(rm.Size() == RegMask::CHUNK_SIZE);
86+
ASSERT_TRUE(rm.is_NotEmpty());
87+
// Set_All sets AllStack bit
88+
ASSERT_TRUE(rm.is_AllStack());
89+
contains_expected_num_of_registers(rm, RegMask::CHUNK_SIZE);
90+
}
91+
92+
TEST_VM(RegMask, Clear) {
93+
// Check that Clear doesn't leave any stray bits
94+
RegMask rm;
95+
rm.Set_All();
96+
rm.Clear();
97+
contains_expected_num_of_registers(rm, 0);
98+
}
99+
100+
TEST_VM(RegMask, AND) {
101+
RegMask rm1;
102+
rm1.Insert(OptoReg::Name(1));
103+
contains_expected_num_of_registers(rm1, 1);
104+
ASSERT_TRUE(rm1.Member(OptoReg::Name(1)));
105+
106+
rm1.AND(rm1);
107+
contains_expected_num_of_registers(rm1, 1);
108+
109+
RegMask rm2;
110+
rm1.AND(rm2);
111+
contains_expected_num_of_registers(rm1, 0);
112+
contains_expected_num_of_registers(rm2, 0);
113+
}
114+
115+
TEST_VM(RegMask, OR) {
116+
RegMask rm1;
117+
rm1.Insert(OptoReg::Name(1));
118+
contains_expected_num_of_registers(rm1, 1);
119+
ASSERT_TRUE(rm1.Member(OptoReg::Name(1)));
120+
121+
rm1.OR(rm1);
122+
contains_expected_num_of_registers(rm1, 1);
123+
124+
RegMask rm2;
125+
rm1.OR(rm2);
126+
contains_expected_num_of_registers(rm1, 1);
127+
contains_expected_num_of_registers(rm2, 0);
128+
}
129+
130+
TEST_VM(RegMask, SUBTRACT) {
131+
RegMask rm1;
132+
RegMask rm2;
133+
134+
rm2.Set_All();
135+
for (int i = 17; i < RegMask::CHUNK_SIZE; i++) {
136+
rm1.Insert(i);
137+
}
138+
ASSERT_TRUE(rm1.is_AllStack());
139+
rm2.SUBTRACT(rm1);
140+
contains_expected_num_of_registers(rm1, RegMask::CHUNK_SIZE - 17);
141+
contains_expected_num_of_registers(rm2, 17);
142+
}
143+
144+
TEST_VM(RegMask, is_bound1) {
145+
RegMask rm;
146+
ASSERT_FALSE(rm.is_bound1());
147+
for (int i = 0; i < RegMask::CHUNK_SIZE - 1; i++) {
148+
rm.Insert(i);
149+
ASSERT_TRUE(rm.is_bound1());
150+
contains_expected_num_of_registers(rm, 1);
151+
rm.Remove(i);
152+
}
153+
// AllStack bit does not count as a bound register
154+
rm.set_AllStack();
155+
ASSERT_FALSE(rm.is_bound1());
156+
}

0 commit comments

Comments
 (0)
Please sign in to comment.