jdk/test/hotspot/gtest/opto/test_regmask.cpp
2025-10-21 13:17:14 +00:00

1257 lines
36 KiB
C++

/*
* Copyright (c) 2020, 2025, 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 "opto/chaitin.hpp"
#include "opto/opcodes.hpp"
#include "opto/regmask.hpp"
#include "unittest.hpp"
// Sanity tests for RegMask and RegMaskIterator. The file tests operations on
// combinations of different RegMask versions ("basic", i.e. only statically
// allocated and "extended", i.e. extended with dynamically allocated memory).
static void contains_expected_num_of_registers(const RegMask& rm, unsigned int expected) {
ASSERT_TRUE(rm.size() == expected);
if (expected > 0) {
ASSERT_TRUE(!rm.is_empty());
} else {
ASSERT_TRUE(rm.is_empty());
ASSERT_TRUE(!rm.is_infinite_stack());
}
RegMaskIterator rmi(rm);
unsigned int count = 0;
OptoReg::Name reg = OptoReg::Bad;
while (rmi.has_next()) {
reg = rmi.next();
ASSERT_TRUE(OptoReg::is_valid(reg));
count++;
}
ASSERT_EQ(OptoReg::Bad, rmi.next());
ASSERT_TRUE(count == expected);
}
TEST_VM(RegMask, empty) {
RegMask rm;
contains_expected_num_of_registers(rm, 0);
}
TEST_VM(RegMask, iteration) {
RegMask rm;
rm.insert(30);
rm.insert(31);
rm.insert(32);
rm.insert(33);
rm.insert(62);
rm.insert(63);
rm.insert(64);
rm.insert(65);
RegMaskIterator rmi(rm);
ASSERT_TRUE(rmi.next() == OptoReg::Name(30));
ASSERT_TRUE(rmi.next() == OptoReg::Name(31));
ASSERT_TRUE(rmi.next() == OptoReg::Name(32));
ASSERT_TRUE(rmi.next() == OptoReg::Name(33));
ASSERT_TRUE(rmi.next() == OptoReg::Name(62));
ASSERT_TRUE(rmi.next() == OptoReg::Name(63));
ASSERT_TRUE(rmi.next() == OptoReg::Name(64));
ASSERT_TRUE(rmi.next() == OptoReg::Name(65));
ASSERT_FALSE(rmi.has_next());
}
TEST_VM(RegMask, Set_ALL) {
// Check that set_all doesn't add bits outside of rm.rm_size_bits()
RegMask rm;
rm.set_all();
ASSERT_TRUE(rm.size() == rm.rm_size_in_bits());
ASSERT_TRUE(!rm.is_empty());
// set_all sets infinite_stack
ASSERT_TRUE(rm.is_infinite_stack());
contains_expected_num_of_registers(rm, rm.rm_size_in_bits());
}
TEST_VM(RegMask, Clear) {
// Check that Clear doesn't leave any stray bits
RegMask rm;
rm.set_all();
rm.clear();
contains_expected_num_of_registers(rm, 0);
}
TEST_VM(RegMask, and_with) {
RegMask rm1;
rm1.insert(OptoReg::Name(1));
contains_expected_num_of_registers(rm1, 1);
ASSERT_TRUE(rm1.member(OptoReg::Name(1)));
rm1.and_with(rm1);
contains_expected_num_of_registers(rm1, 1);
RegMask rm2;
rm1.and_with(rm2);
contains_expected_num_of_registers(rm1, 0);
contains_expected_num_of_registers(rm2, 0);
}
TEST_VM(RegMask, or_with) {
RegMask rm1;
rm1.insert(OptoReg::Name(1));
contains_expected_num_of_registers(rm1, 1);
ASSERT_TRUE(rm1.member(OptoReg::Name(1)));
rm1.or_with(rm1);
contains_expected_num_of_registers(rm1, 1);
RegMask rm2;
rm1.or_with(rm2);
contains_expected_num_of_registers(rm1, 1);
contains_expected_num_of_registers(rm2, 0);
}
TEST_VM(RegMask, subtract) {
RegMask rm1;
RegMask rm2;
rm2.set_all();
for (int i = 17; i < (int)rm1.rm_size_in_bits(); i++) {
rm1.insert(i);
}
rm1.set_infinite_stack(true);
ASSERT_TRUE(rm1.is_infinite_stack());
rm2.subtract(rm1);
contains_expected_num_of_registers(rm1, rm1.rm_size_in_bits() - 17);
contains_expected_num_of_registers(rm2, 17);
}
TEST_VM(RegMask, subtract_inner) {
RegMask rm1;
RegMask rm2;
rm2.set_all();
for (int i = 17; i < (int)rm1.rm_size_in_bits(); i++) {
rm1.insert(i);
}
rm2.subtract_inner(rm1);
contains_expected_num_of_registers(rm1, rm1.rm_size_in_bits() - 17);
contains_expected_num_of_registers(rm2, 17);
}
TEST_VM(RegMask, is_bound1) {
RegMask rm;
ASSERT_FALSE(rm.is_bound1());
for (int i = 0; i < (int)rm.rm_size_in_bits() - 1; i++) {
rm.insert(i);
ASSERT_TRUE(rm.is_bound1()) << "Index " << i;
ASSERT_TRUE(rm.is_bound(Op_RegI)) << "Index " << i;
contains_expected_num_of_registers(rm, 1);
rm.remove(i);
}
// infinite_stack does not count as a bound register
rm.set_infinite_stack(true);
ASSERT_FALSE(rm.is_bound1());
}
TEST_VM(RegMask, is_bound_pair) {
RegMask rm;
ASSERT_TRUE(rm.is_bound_pair());
for (int i = 0; i < (int)rm.rm_size_in_bits() - 2; i++) {
rm.insert(i);
rm.insert(i + 1);
ASSERT_TRUE(rm.is_bound_pair()) << "Index " << i;
ASSERT_TRUE(rm.is_bound_set(2)) << "Index " << i;
ASSERT_TRUE(rm.is_bound(Op_RegI)) << "Index " << i;
contains_expected_num_of_registers(rm, 2);
rm.clear();
}
// A pair with the infinite bit does not count as a bound pair
rm.clear();
rm.insert(rm.rm_size_in_bits() - 2);
rm.insert(rm.rm_size_in_bits() - 1);
rm.set_infinite_stack(true);
ASSERT_FALSE(rm.is_bound_pair());
}
TEST_VM(RegMask, is_bound_set) {
RegMask rm;
for (int size = 1; size <= 16; size++) {
ASSERT_TRUE(rm.is_bound_set(size));
for (int i = 0; i < (int)rm.rm_size_in_bits() - size; i++) {
for (int j = i; j < i + size; j++) {
rm.insert(j);
}
ASSERT_TRUE(rm.is_bound_set(size)) << "Size " << size << " Index " << i;
contains_expected_num_of_registers(rm, size);
rm.clear();
}
// A set with infinite_stack does not count as a bound set
for (int j = rm.rm_size_in_bits() - size; j < (int)rm.rm_size_in_bits(); j++) {
rm.insert(j);
}
rm.set_infinite_stack(true);
ASSERT_FALSE(rm.is_bound_set(size));
rm.clear();
}
}
TEST_VM(RegMask, external_member) {
RegMask rm;
rm.set_infinite_stack(false);
ASSERT_FALSE(rm.member(OptoReg::Name(rm.rm_size_in_bits())));
rm.set_infinite_stack(true);
ASSERT_TRUE(rm.member(OptoReg::Name(rm.rm_size_in_bits())));
}
TEST_VM(RegMask, find_element) {
RegMask rm;
rm.insert(OptoReg::Name(44));
rm.insert(OptoReg::Name(30));
rm.insert(OptoReg::Name(54));
ASSERT_EQ(rm.find_first_elem(), OptoReg::Name(30));
ASSERT_EQ(rm.find_last_elem(), OptoReg::Name(54));
rm.set_infinite_stack(true);
ASSERT_EQ(rm.find_last_elem(), OptoReg::Name(54));
rm.clear();
ASSERT_EQ(rm.find_first_elem(), OptoReg::Bad);
ASSERT_EQ(rm.find_last_elem(), OptoReg::Bad);
}
TEST_VM(RegMask, find_first_set) {
RegMask rm;
LRG lrg;
lrg._is_scalable = 0;
lrg._is_vector = 0;
ASSERT_EQ(rm.find_first_set(lrg, 2), OptoReg::Bad);
rm.insert(OptoReg::Name(24));
rm.insert(OptoReg::Name(25));
rm.insert(OptoReg::Name(26));
rm.insert(OptoReg::Name(27));
rm.insert(OptoReg::Name(16));
rm.insert(OptoReg::Name(17));
rm.insert(OptoReg::Name(18));
rm.insert(OptoReg::Name(19));
ASSERT_EQ(rm.find_first_set(lrg, 4), OptoReg::Name(19));
}
TEST_VM(RegMask, alignment) {
RegMask rm;
rm.insert(OptoReg::Name(30));
rm.insert(OptoReg::Name(31));
ASSERT_TRUE(rm.is_aligned_sets(2));
rm.insert(OptoReg::Name(32));
rm.insert(OptoReg::Name(37));
rm.insert(OptoReg::Name(62));
rm.insert(OptoReg::Name(71));
rm.insert(OptoReg::Name(74));
rm.insert(OptoReg::Name(75));
ASSERT_FALSE(rm.is_aligned_pairs());
rm.clear_to_pairs();
ASSERT_TRUE(rm.is_aligned_sets(2));
ASSERT_TRUE(rm.is_aligned_pairs());
contains_expected_num_of_registers(rm, 4);
ASSERT_TRUE(rm.member(OptoReg::Name(30)));
ASSERT_TRUE(rm.member(OptoReg::Name(31)));
ASSERT_TRUE(rm.member(OptoReg::Name(74)));
ASSERT_TRUE(rm.member(OptoReg::Name(75)));
ASSERT_FALSE(rm.is_misaligned_pair());
rm.remove(OptoReg::Name(30));
rm.remove(OptoReg::Name(74));
ASSERT_TRUE(rm.is_misaligned_pair());
}
TEST_VM(RegMask, clear_to_sets) {
RegMask rm;
rm.insert(OptoReg::Name(3));
rm.insert(OptoReg::Name(20));
rm.insert(OptoReg::Name(21));
rm.insert(OptoReg::Name(22));
rm.insert(OptoReg::Name(23));
rm.insert(OptoReg::Name(25));
rm.insert(OptoReg::Name(26));
rm.insert(OptoReg::Name(27));
rm.insert(OptoReg::Name(40));
rm.insert(OptoReg::Name(42));
rm.insert(OptoReg::Name(43));
rm.insert(OptoReg::Name(44));
rm.insert(OptoReg::Name(45));
rm.clear_to_sets(2);
ASSERT_TRUE(rm.is_aligned_sets(2));
contains_expected_num_of_registers(rm, 10);
rm.clear_to_sets(4);
ASSERT_TRUE(rm.is_aligned_sets(4));
contains_expected_num_of_registers(rm, 4);
rm.clear_to_sets(8);
ASSERT_TRUE(rm.is_aligned_sets(8));
contains_expected_num_of_registers(rm, 0);
}
TEST_VM(RegMask, smear_to_sets) {
RegMask rm;
rm.insert(OptoReg::Name(3));
rm.smear_to_sets(2);
ASSERT_TRUE(rm.is_aligned_sets(2));
contains_expected_num_of_registers(rm, 2);
rm.smear_to_sets(4);
ASSERT_TRUE(rm.is_aligned_sets(4));
contains_expected_num_of_registers(rm, 4);
rm.smear_to_sets(8);
ASSERT_TRUE(rm.is_aligned_sets(8));
contains_expected_num_of_registers(rm, 8);
rm.smear_to_sets(16);
ASSERT_TRUE(rm.is_aligned_sets(16));
contains_expected_num_of_registers(rm, 16);
}
TEST_VM(RegMask, overlap) {
RegMask rm1;
RegMask rm2;
ASSERT_FALSE(rm1.overlap(rm2));
ASSERT_FALSE(rm2.overlap(rm1));
rm1.insert(OptoReg::Name(23));
rm1.insert(OptoReg::Name(2));
rm1.insert(OptoReg::Name(12));
rm2.insert(OptoReg::Name(1));
rm2.insert(OptoReg::Name(4));
ASSERT_FALSE(rm1.overlap(rm2));
ASSERT_FALSE(rm2.overlap(rm1));
rm1.insert(OptoReg::Name(4));
ASSERT_TRUE(rm1.overlap(rm2));
ASSERT_TRUE(rm2.overlap(rm1));
}
TEST_VM(RegMask, valid_reg) {
RegMask rm;
ASSERT_FALSE(rm.is_valid_reg(OptoReg::Name(42), 1));
rm.insert(OptoReg::Name(3));
rm.insert(OptoReg::Name(5));
rm.insert(OptoReg::Name(6));
rm.insert(OptoReg::Name(7));
ASSERT_FALSE(rm.is_valid_reg(OptoReg::Name(7), 4));
ASSERT_TRUE(rm.is_valid_reg(OptoReg::Name(7), 2));
}
TEST_VM(RegMask, rollover_and_insert_remove) {
RegMask rm;
OptoReg::Name reg1(rm.rm_size_in_bits() + 42);
OptoReg::Name reg2(rm.rm_size_in_bits() * 2 + 42);
rm.set_infinite_stack(true);
ASSERT_TRUE(rm.member(reg1));
rm.rollover();
rm.clear();
rm.insert(reg1);
ASSERT_TRUE(rm.member(reg1));
rm.remove(reg1);
ASSERT_FALSE(rm.member(reg1));
rm.set_infinite_stack(true);
rm.rollover();
rm.clear();
rm.insert(reg2);
ASSERT_FALSE(rm.member(reg1));
ASSERT_TRUE(rm.member(reg2));
}
TEST_VM(RegMask, rollover_and_find) {
RegMask rm;
OptoReg::Name reg1(rm.rm_size_in_bits() + 42);
OptoReg::Name reg2(rm.rm_size_in_bits() + 7);
rm.set_infinite_stack(true);
rm.rollover();
rm.clear();
ASSERT_EQ(rm.find_first_elem(), OptoReg::Bad);
ASSERT_EQ(rm.find_last_elem(), OptoReg::Bad);
rm.insert(reg1);
rm.insert(reg2);
ASSERT_EQ(rm.find_first_elem(), reg2);
ASSERT_EQ(rm.find_last_elem(), reg1);
}
TEST_VM(RegMask, rollover_and_find_first_set) {
LRG lrg;
lrg._is_scalable = 0;
lrg._is_vector = 0;
RegMask rm;
OptoReg::Name reg1(rm.rm_size_in_bits() + 24);
OptoReg::Name reg2(rm.rm_size_in_bits() + 25);
OptoReg::Name reg3(rm.rm_size_in_bits() + 26);
OptoReg::Name reg4(rm.rm_size_in_bits() + 27);
OptoReg::Name reg5(rm.rm_size_in_bits() + 16);
OptoReg::Name reg6(rm.rm_size_in_bits() + 17);
OptoReg::Name reg7(rm.rm_size_in_bits() + 18);
OptoReg::Name reg8(rm.rm_size_in_bits() + 19);
rm.set_infinite_stack(true);
rm.rollover();
rm.clear();
ASSERT_EQ(rm.find_first_set(lrg, 2), OptoReg::Bad);
rm.insert(reg1);
rm.insert(reg2);
rm.insert(reg3);
rm.insert(reg4);
rm.insert(reg5);
rm.insert(reg6);
rm.insert(reg7);
rm.insert(reg8);
ASSERT_EQ(rm.find_first_set(lrg, 4), reg8);
}
TEST_VM(RegMask, rollover_and_set_all_from) {
RegMask rm;
OptoReg::Name reg1(rm.rm_size_in_bits() + 42);
rm.set_infinite_stack(true);
rm.rollover();
rm.clear();
rm.set_all_from(reg1);
contains_expected_num_of_registers(rm, rm.rm_size_in_bits() - 42);
}
TEST_VM(RegMask, rollover_and_set_all_from_offset) {
RegMask rm;
rm.set_infinite_stack(true);
rm.rollover();
rm.clear();
rm.set_all_from_offset();
contains_expected_num_of_registers(rm, rm.rm_size_in_bits());
}
TEST_VM(RegMask, rollover_and_iterate) {
RegMask rm;
OptoReg::Name reg1(rm.rm_size_in_bits() + 2);
OptoReg::Name reg2(rm.rm_size_in_bits() + 6);
OptoReg::Name reg3(rm.rm_size_in_bits() + 17);
OptoReg::Name reg4(rm.rm_size_in_bits() + 43);
rm.set_infinite_stack(true);
rm.rollover();
rm.clear();
rm.insert(reg1);
rm.insert(reg2);
rm.insert(reg3);
rm.insert(reg4);
RegMaskIterator rmi(rm);
ASSERT_EQ(rmi.next(), reg1);
ASSERT_EQ(rmi.next(), reg2);
ASSERT_EQ(rmi.next(), reg3);
ASSERT_EQ(rmi.next(), reg4);
ASSERT_FALSE(rmi.has_next());
}
TEST_VM(RegMask, rollover_and_subtract_inner_disjoint) {
RegMask rm1;
RegMask rm2;
OptoReg::Name reg1(rm1.rm_size_in_bits() + 42);
rm1.set_infinite_stack(true);
rm1.rollover();
rm1.clear();
rm1.subtract_inner(rm2);
contains_expected_num_of_registers(rm1, 0);
rm2.subtract_inner(rm1);
contains_expected_num_of_registers(rm2, 0);
rm1.insert(reg1);
rm2.insert(42);
rm1.subtract_inner(rm2);
contains_expected_num_of_registers(rm1, 1);
rm2.subtract_inner(rm1);
contains_expected_num_of_registers(rm2, 1);
}
TEST_VM(RegMask, rollover_and_subtract_inner_overlap) {
RegMask rm1;
RegMask rm2;
OptoReg::Name reg1(rm1.rm_size_in_bits() + 42);
rm1.set_infinite_stack(true);
rm1.rollover();
rm1.clear();
rm2.set_infinite_stack(true);
rm2.rollover();
rm2.clear();
rm1.subtract_inner(rm2);
contains_expected_num_of_registers(rm1, 0);
rm2.subtract_inner(rm1);
contains_expected_num_of_registers(rm2, 0);
rm1.insert(reg1);
rm2.insert(reg1);
rm1.subtract_inner(rm2);
contains_expected_num_of_registers(rm1, 0);
rm1.insert(reg1);
rm2.subtract_inner(rm1);
contains_expected_num_of_registers(rm2, 0);
}
#ifdef ASSERT
TEST_VM_ASSERT_MSG(RegMask, unexpected_clone, ".*clone sanity check") {
RegMask rm1;
RegMask rm2;
// Copy contents of rm1 to rm2 inappropriately (no copy constructor)
memcpy((void*)&rm2, (void*)&rm1, sizeof(RegMask));
rm2.member(0); // Safeguard in RegMask must catch this.
}
TEST_VM_ASSERT_MSG(RegMask, unexpected_growth, ".*unexpected register mask growth") {
RegMask rm;
// Add clearly out of range OptoReg::Name
rm.insert(std::numeric_limits<OptoReg::Name>::max());
}
TEST_VM_ASSERT_MSG(RegMask, not_growable, ".*register mask not growable") {
RegMask rm;
// Add a bit just outside the mask, without having specified an arena for
// extension.
rm.insert(rm.rm_size_in_bits());
}
TEST_VM_ASSERT_MSG(RegMask, offset_mismatch, ".*offset mismatch") {
RegMask rm1;
RegMask rm2;
rm1.set_infinite_stack(true);
rm1.rollover();
// Cannot assign with different offsets
rm2.assignFrom(rm1);
}
#endif
#ifndef PRODUCT
Arena* arena() {
return Thread::current()->resource_area();
}
static void is_basic(const RegMask& rm) {
ASSERT_EQ(rm.rm_size_in_words(), RegMask::gtest_basic_rm_size_in_words());
}
static void is_extended(const RegMask& rm) {
ASSERT_TRUE(rm.rm_size_in_words() > RegMask::gtest_basic_rm_size_in_words());
}
static int first_extended() {
return RegMask::gtest_basic_rm_size_in_words() * BitsPerWord;
}
static void extend(RegMask& rm, unsigned int n = 4) {
// Extend the given RegMask with at least n dynamically-allocated words.
rm.insert(OptoReg::Name(first_extended() + (BitsPerWord * n) - 1));
rm.clear();
ASSERT_TRUE(rm.rm_size_in_words() >= RegMask::gtest_basic_rm_size_in_words() + n);
}
TEST_VM(RegMask, static_by_default) {
// Check that a freshly created RegMask does not allocate dynamic memory.
RegMask rm;
is_basic(rm);
}
TEST_VM(RegMask, iteration_extended) {
RegMask rm(arena());
rm.insert(30);
rm.insert(31);
rm.insert(33);
rm.insert(62);
rm.insert(first_extended());
rm.insert(first_extended() + 42);
rm.insert(first_extended() + 55);
rm.insert(first_extended() + 456);
RegMaskIterator rmi(rm);
ASSERT_TRUE(rmi.next() == OptoReg::Name(30));
ASSERT_TRUE(rmi.next() == OptoReg::Name(31));
ASSERT_TRUE(rmi.next() == OptoReg::Name(33));
ASSERT_TRUE(rmi.next() == OptoReg::Name(62));
ASSERT_TRUE(rmi.next() == OptoReg::Name(first_extended()));
ASSERT_TRUE(rmi.next() == OptoReg::Name(first_extended() + 42));
ASSERT_TRUE(rmi.next() == OptoReg::Name(first_extended() + 55));
ASSERT_TRUE(rmi.next() == OptoReg::Name(first_extended() + 456));
ASSERT_FALSE(rmi.has_next());
}
TEST_VM(RegMask, set_all_extended) {
// Check that set_all doesn't add bits outside of rm.rm_size_bits() on
// extended RegMasks.
RegMask rm(arena());
extend(rm);
rm.set_all();
ASSERT_EQ(rm.size(), rm.rm_size_in_bits());
ASSERT_TRUE(!rm.is_empty());
// set_all sets infinite_stack bit
ASSERT_TRUE(rm.is_infinite_stack());
contains_expected_num_of_registers(rm, rm.rm_size_in_bits());
}
TEST_VM(RegMask, set_all_from_extended) {
RegMask rm(arena());
extend(rm);
rm.set_all_from(OptoReg::Name(42));
contains_expected_num_of_registers(rm, rm.rm_size_in_bits() - 42);
}
TEST_VM(RegMask, set_all_from_extended_grow) {
RegMask rm(arena());
rm.set_all_from(first_extended() + OptoReg::Name(42));
is_extended(rm);
contains_expected_num_of_registers(rm, rm.rm_size_in_bits() - first_extended() - 42);
}
TEST_VM(RegMask, clear_extended) {
// Check that clear doesn't leave any stray bits on extended RegMasks.
RegMask rm(arena());
rm.insert(first_extended());
is_extended(rm);
rm.set_all();
rm.clear();
contains_expected_num_of_registers(rm, 0);
}
TEST_VM(RegMask, and_with_extended_basic) {
RegMask rm1(arena());
rm1.insert(OptoReg::Name(first_extended()));
is_extended(rm1);
contains_expected_num_of_registers(rm1, 1);
ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended())));
rm1.and_with(rm1);
contains_expected_num_of_registers(rm1, 1);
RegMask rm2;
is_basic(rm2);
rm1.and_with(rm2);
contains_expected_num_of_registers(rm1, 0);
contains_expected_num_of_registers(rm2, 0);
}
TEST_VM(RegMask, and_with_extended_extended) {
RegMask rm1(arena());
rm1.insert(OptoReg::Name(first_extended()));
is_extended(rm1);
contains_expected_num_of_registers(rm1, 1);
ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended())));
rm1.and_with(rm1);
contains_expected_num_of_registers(rm1, 1);
RegMask rm2(arena());
extend(rm2);
rm1.and_with(rm2);
contains_expected_num_of_registers(rm1, 0);
contains_expected_num_of_registers(rm2, 0);
}
TEST_VM(RegMask, or_with_extended_basic) {
RegMask rm1(arena());
rm1.insert(OptoReg::Name(first_extended()));
is_extended(rm1);
contains_expected_num_of_registers(rm1, 1);
ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended())));
rm1.or_with(rm1);
contains_expected_num_of_registers(rm1, 1);
RegMask rm2;
is_basic(rm2);
rm1.or_with(rm2);
contains_expected_num_of_registers(rm1, 1);
contains_expected_num_of_registers(rm2, 0);
}
TEST_VM(RegMask, or_with_extended_extended) {
RegMask rm1(arena());
rm1.insert(OptoReg::Name(first_extended()));
is_extended(rm1);
contains_expected_num_of_registers(rm1, 1);
ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended())));
rm1.or_with(rm1);
contains_expected_num_of_registers(rm1, 1);
RegMask rm2(arena());
extend(rm2);
rm1.or_with(rm2);
contains_expected_num_of_registers(rm1, 1);
contains_expected_num_of_registers(rm2, 0);
}
TEST_VM(RegMask, subtract_extended) {
RegMask rm1(arena());
extend(rm1);
RegMask rm2(arena());
extend(rm2);
rm2.set_all();
ASSERT_TRUE(rm2.is_infinite_stack());
for (int i = first_extended() + 17; i < (int)rm1.rm_size_in_bits(); i++) {
rm1.insert(i);
}
rm1.set_infinite_stack(true);
ASSERT_TRUE(rm1.is_infinite_stack());
rm2.subtract(rm1);
contains_expected_num_of_registers(rm1, rm1.rm_size_in_bits() - first_extended() - 17);
contains_expected_num_of_registers(rm2, first_extended() + 17);
}
TEST_VM(RegMask, external_member_extended) {
RegMask rm(arena());
extend(rm);
rm.set_infinite_stack(false);
ASSERT_FALSE(rm.member(OptoReg::Name(rm.rm_size_in_bits())));
rm.set_infinite_stack(true);
ASSERT_TRUE(rm.member(OptoReg::Name(rm.rm_size_in_bits())));
}
TEST_VM(RegMask, overlap_extended) {
RegMask rm1(arena());
extend(rm1);
RegMask rm2(arena());
extend(rm2);
ASSERT_FALSE(rm1.overlap(rm2));
ASSERT_FALSE(rm2.overlap(rm1));
rm1.insert(OptoReg::Name(23));
rm1.insert(OptoReg::Name(2));
rm1.insert(OptoReg::Name(first_extended() + 12));
rm2.insert(OptoReg::Name(1));
rm2.insert(OptoReg::Name(first_extended() + 4));
ASSERT_FALSE(rm1.overlap(rm2));
ASSERT_FALSE(rm2.overlap(rm1));
rm1.insert(OptoReg::Name(first_extended() + 4));
ASSERT_TRUE(rm1.overlap(rm2));
ASSERT_TRUE(rm2.overlap(rm1));
}
TEST_VM(RegMask, up_extended) {
RegMask rm(arena());
extend(rm);
ASSERT_TRUE(rm.is_UP());
rm.insert(OptoReg::Name(1));
ASSERT_TRUE(rm.is_UP());
rm.insert(OptoReg::Name(first_extended()));
ASSERT_FALSE(rm.is_UP());
rm.clear();
rm.set_infinite_stack(true);
ASSERT_FALSE(rm.is_UP());
}
TEST_VM(RegMask, subtract_inner_basic_extended) {
RegMask rm1;
RegMask rm2(arena());
rm1.insert(OptoReg::Name(1));
rm1.insert(OptoReg::Name(42));
is_basic(rm1);
rm2.insert(OptoReg::Name(1));
rm2.insert(OptoReg::Name(first_extended() + 20));
is_extended(rm2);
rm1.subtract_inner(rm2);
is_basic(rm1);
contains_expected_num_of_registers(rm1, 1);
ASSERT_TRUE(rm1.member(OptoReg::Name(42)));
}
TEST_VM(RegMask, subtract_inner_extended_basic) {
RegMask rm1(arena());
RegMask rm2;
rm1.insert(OptoReg::Name(1));
rm1.insert(OptoReg::Name(42));
rm1.insert(OptoReg::Name(first_extended() + 20));
is_extended(rm1);
rm2.insert(OptoReg::Name(1));
is_basic(rm2);
rm1.subtract_inner(rm2);
contains_expected_num_of_registers(rm1, 2);
ASSERT_TRUE(rm1.member(OptoReg::Name(42)));
ASSERT_TRUE(rm1.member(OptoReg::Name(first_extended() + 20)));
}
TEST_VM(RegMask, rollover_extended) {
RegMask rm(arena());
extend(rm);
is_extended(rm);
OptoReg::Name reg1(rm.rm_size_in_bits() + 42);
rm.set_infinite_stack(true);
rm.rollover();
rm.insert(reg1);
ASSERT_TRUE(rm.member(reg1));
}
TEST_VM(RegMask, rollover_and_subtract_inner_disjoint_extended) {
RegMask rm1(arena());
RegMask rm2;
extend(rm1);
OptoReg::Name reg1(rm1.rm_size_in_bits() + 42);
rm1.set_infinite_stack(true);
rm1.rollover();
rm1.clear();
rm1.subtract_inner(rm2);
contains_expected_num_of_registers(rm1, 0);
rm2.subtract_inner(rm1);
contains_expected_num_of_registers(rm2, 0);
rm1.insert(reg1);
rm2.insert(42);
rm1.subtract_inner(rm2);
contains_expected_num_of_registers(rm1, 1);
rm2.subtract_inner(rm1);
contains_expected_num_of_registers(rm2, 1);
}
TEST_VM(RegMask, rollover_and_subtract_inner_overlap_extended) {
RegMask rm1(arena());
RegMask rm2;
OptoReg::Name reg1(rm1.rm_size_in_bits() + 42);
extend(rm1);
rm2.set_infinite_stack(true);
rm2.rollover();
rm2.clear();
rm1.subtract_inner(rm2);
contains_expected_num_of_registers(rm1, 0);
rm2.subtract_inner(rm1);
contains_expected_num_of_registers(rm2, 0);
rm1.insert(reg1);
rm2.insert(reg1);
rm1.subtract_inner(rm2);
contains_expected_num_of_registers(rm1, 0);
rm1.insert(reg1);
rm2.subtract_inner(rm1);
contains_expected_num_of_registers(rm2, 0);
}
const uint iterations = 50000;
static uint r;
static uint next_random() {
r = os::next_random(r);
return r;
}
static void init_random() {
if (StressSeed == 0) {
r = static_cast<uint>(Ticks::now().nanoseconds());
tty->print_cr("seed: %u", r);
} else {
r = StressSeed;
}
}
static void print(const char* name, const RegMask& mask) {
tty->print("%s: ", name);
mask.print();
tty->print_cr(", size: %u, offset: %u, infinite_stack: %u", mask.rm_size_in_bits(),
mask.offset_bits(), mask.is_infinite_stack());
}
static void assert_equivalent(const RegMask& mask,
const ResourceBitMap& mask_ref,
bool infinite_stack_ref) {
ASSERT_EQ(mask_ref.count_one_bits(), mask.size());
RegMaskIterator it(mask);
OptoReg::Name reg = OptoReg::Bad;
while (it.has_next()) {
reg = it.next();
ASSERT_TRUE(OptoReg::is_valid(reg));
ASSERT_TRUE(mask_ref.at(reg));
}
ASSERT_EQ(infinite_stack_ref, mask.is_infinite_stack());
}
static void populate_auxiliary_sets(RegMask& mask_aux,
ResourceBitMap& mask_aux_ref,
uint reg_capacity, uint offset,
bool random_offset) {
mask_aux.clear();
mask_aux_ref.clear();
if (random_offset) {
uint offset_in_words = offset / BitsPerWord;
uint capacity_in_words = reg_capacity / BitsPerWord;
uint new_offset_in_words;
uint offset_target = next_random() % 3;
switch (offset_target) {
case 0: // before
if (offset_in_words == 0) {
new_offset_in_words = 0;
} else {
new_offset_in_words = next_random() % offset_in_words;
}
break;
case 1: // within
new_offset_in_words =
(next_random() % capacity_in_words) + offset_in_words;
break;
case 2: // after
new_offset_in_words = offset_in_words + capacity_in_words +
(next_random() % (capacity_in_words));
break;
default:
FAIL();
}
offset = new_offset_in_words * BitsPerWord;
if (offset + RegMask::gtest_rm_size_in_bits_max() > mask_aux_ref.size()) {
// Ensure that there is space in the reference mask.
offset = 0;
}
}
mask_aux.gtest_set_offset(offset / BitsPerWord);
assert_equivalent(mask_aux, mask_aux_ref, false);
uint max_size;
uint size_target = next_random() % 3;
switch (size_target) {
case 0: // smaller
max_size = reg_capacity / 2;
break;
case 1: // equal
max_size = reg_capacity;
break;
case 2: // larger (if possible)
max_size = RegMask::gtest_rm_size_in_bits_max();
break;
default:
FAIL();
}
uint regs;
uint regs_target = next_random() % 3;
switch (regs_target) {
case 0: // sparse
regs = next_random() % 8;
break;
case 1: // medium
regs = next_random() % (max_size / 8);
break;
case 2: // dense
regs = next_random() % max_size;
break;
default:
FAIL();
}
for (uint i = 0; i < regs; i++) {
uint reg = (next_random() % max_size) + offset;
mask_aux.insert(reg);
mask_aux_ref.set_bit(reg);
}
mask_aux.set_infinite_stack(next_random() % 2);
assert_equivalent(mask_aux, mask_aux_ref, mask_aux.is_infinite_stack());
if (Verbose) {
print("mask_aux", mask_aux);
}
}
static void stack_extend_ref_masks(ResourceBitMap& mask1, bool infinite_stack1,
uint size_bits1, uint offset1,
ResourceBitMap& mask2, bool infinite_stack2,
uint size_bits2, uint offset2) {
uint size_bits_after = MAX2(size_bits1, size_bits2);
if (infinite_stack1) {
mask1.set_range(size_bits1 + offset1, size_bits_after + offset1);
}
if (infinite_stack2) {
mask2.set_range(size_bits2 + offset2, size_bits_after + offset2);
}
}
TEST_VM(RegMask, random) {
ResourceMark rm;
RegMask mask(arena());
ResourceBitMap mask_ref(std::numeric_limits<short>::max() + 1);
bool infinite_stack_ref = false;
uint offset_ref = 0;
init_random();
for (uint i = 0; i < iterations; i++) {
if (Verbose) {
print("mask ", mask);
tty->print("%u. ", i);
}
uint action = next_random() % 13;
uint reg;
uint size_bits_before = mask.rm_size_in_bits();
// This copy is used for stack-extension in overlap.
ResourceBitMap mask_ref_copy(std::numeric_limits<short>::max() + 1);
mask_ref_copy.clear();
mask_ref.iterate([&](BitMap::idx_t index) {
mask_ref_copy.set_bit(index);
return true;
});
ResourceBitMap mask_aux_ref(std::numeric_limits<short>::max() + 1);
RegMask mask_aux(arena());
switch (action) {
case 0:
reg = (next_random() % RegMask::gtest_rm_size_in_bits_max()) + offset_ref;
if (Verbose) {
tty->print_cr("action: Insert");
tty->print("value : ");
OptoReg::dump(reg);
tty->cr();
}
mask.insert(reg);
mask_ref.set_bit(reg);
if (mask.is_infinite_stack() && reg >= size_bits_before) {
// Stack-extend reference bitset.
mask_ref.set_range(size_bits_before + offset_ref,
mask.rm_size_in_bits() + offset_ref);
}
break;
case 1:
reg = (next_random() % size_bits_before) + offset_ref;
if (Verbose) {
tty->print_cr("action: Remove");
tty->print("value : ");
OptoReg::dump(reg);
tty->cr();
}
mask.remove(reg);
mask_ref.clear_bit(reg);
break;
case 2:
if (Verbose) {
tty->print_cr("action: Clear");
}
mask.clear();
mask_ref.clear();
infinite_stack_ref = false;
break;
case 3:
if (offset_ref > 0) {
// set_all expects a zero-offset.
break;
}
if (Verbose) {
tty->print_cr("action: set_all");
}
mask.set_all();
mask_ref.set_range(0, size_bits_before);
infinite_stack_ref = true;
break;
case 4:
if (Verbose) {
tty->print_cr("action: and_with");
}
populate_auxiliary_sets(mask_aux, mask_aux_ref, mask.rm_size_in_bits(),
offset_ref, /*random_offset*/ false);
mask.and_with(mask_aux);
stack_extend_ref_masks(mask_ref, infinite_stack_ref, size_bits_before,
offset_ref, mask_aux_ref, mask_aux.is_infinite_stack(),
mask_aux.rm_size_in_bits(), mask_aux.offset_bits());
mask_ref.set_intersection(mask_aux_ref);
infinite_stack_ref = infinite_stack_ref && mask_aux.is_infinite_stack();
break;
case 5:
if (Verbose) {
tty->print_cr("action: or_with");
}
populate_auxiliary_sets(mask_aux, mask_aux_ref, mask.rm_size_in_bits(),
offset_ref, /*random_offset*/ false);
mask.or_with(mask_aux);
stack_extend_ref_masks(mask_ref, infinite_stack_ref, size_bits_before,
offset_ref, mask_aux_ref, mask_aux.is_infinite_stack(),
mask_aux.rm_size_in_bits(), mask_aux.offset_bits());
mask_ref.set_union(mask_aux_ref);
infinite_stack_ref = infinite_stack_ref || mask_aux.is_infinite_stack();
break;
case 6:
if (Verbose) {
tty->print_cr("action: subtract");
}
populate_auxiliary_sets(mask_aux, mask_aux_ref, mask.rm_size_in_bits(),
offset_ref, /*random_offset*/ false);
mask.subtract(mask_aux);
stack_extend_ref_masks(mask_ref, infinite_stack_ref, size_bits_before,
offset_ref, mask_aux_ref, mask_aux.is_infinite_stack(),
mask_aux.rm_size_in_bits(), mask_aux.offset_bits());
mask_ref.set_difference(mask_aux_ref);
if (mask_aux.is_infinite_stack()) {
infinite_stack_ref = false;
}
break;
case 7:
if (Verbose) {
tty->print_cr("action: subtract_inner");
}
populate_auxiliary_sets(mask_aux, mask_aux_ref, mask.rm_size_in_bits(),
offset_ref, /*random_offset*/ true);
// subtract_inner expects an argument register mask with infinite_stack =
// false.
mask_aux.set_infinite_stack(false);
mask.subtract_inner(mask_aux);
// subtract_inner does not have "stack-extension semantics".
mask_ref.set_difference(mask_aux_ref);
break;
case 8:
if (Verbose) {
tty->print_cr("action: overlap");
}
populate_auxiliary_sets(mask_aux, mask_aux_ref, mask.rm_size_in_bits(),
offset_ref, /*random_offset*/ false);
// Stack-extend a copy of mask_ref to avoid mutating the original.
stack_extend_ref_masks(mask_ref_copy, infinite_stack_ref, size_bits_before,
offset_ref, mask_aux_ref, mask_aux.is_infinite_stack(),
mask_aux.rm_size_in_bits(), mask_aux.offset_bits());
ASSERT_EQ(mask_ref_copy.intersects(mask_aux_ref) ||
(infinite_stack_ref && mask_aux.is_infinite_stack()),
mask.overlap(mask_aux));
break;
case 9:
if (Verbose) {
tty->print_cr("action: rollover");
}
// rollover expects the mask to be cleared and with infinite_stack = true
mask.clear();
mask.set_infinite_stack(true);
mask_ref.clear();
infinite_stack_ref = true;
if (mask.rollover()) {
offset_ref += size_bits_before;
mask_ref.set_range(offset_ref, offset_ref + size_bits_before);
}
break;
case 10:
if (Verbose) {
tty->print_cr("action: reset");
}
mask.gtest_set_offset(0);
mask.clear();
mask_ref.clear();
infinite_stack_ref = false;
offset_ref = 0;
break;
case 11:
if (Verbose) {
tty->print_cr("action: set_all_from_offset");
}
mask.set_all_from_offset();
mask_ref.set_range(offset_ref, offset_ref + size_bits_before);
infinite_stack_ref = true;
break;
case 12:
reg = (next_random() % size_bits_before) + offset_ref;
if (Verbose) {
tty->print_cr("action: set_all_from");
tty->print("value : ");
OptoReg::dump(reg);
tty->cr();
}
mask.set_all_from(reg);
mask_ref.set_range(reg, offset_ref + size_bits_before);
infinite_stack_ref = true;
break;
default:
FAIL() << "Unimplemented action";
}
ASSERT_NO_FATAL_FAILURE(assert_equivalent(mask, mask_ref, infinite_stack_ref));
}
}
// Randomly sets register mask contents. Does not change register mask size.
static void randomize(RegMask& rm) {
rm.clear();
// Uniform distribution over number of registers.
uint regs = next_random() % (rm.rm_size_in_bits() + 1);
for (uint i = 0; i < regs; i++) {
uint reg = (next_random() % rm.rm_size_in_bits()) + rm.offset_bits();
rm.insert(reg);
}
rm.set_infinite_stack(next_random() % 2);
}
static uint grow_randomly(RegMask& rm, uint min_growth = 1,
uint max_growth = 3) {
// Grow between min_growth and max_growth times.
uint grow = min_growth + (max_growth > 0 ? next_random() % max_growth : 0);
for (uint i = 0; i < grow; ++i) {
uint reg = rm.rm_size_in_bits();
if (reg >= RegMask::gtest_rm_size_in_bits_max()) {
// Cannot grow more
break;
}
// Force grow
rm.insert(reg);
if (!rm.is_infinite_stack()) {
// Restore
rm.remove(reg);
}
}
// Return how many times we grew
return grow;
}
TEST_VM(RegMask, random_copy) {
init_random();
auto print_failure = [&](const RegMask& src, const RegMask& dst) {
tty->print_cr("Failure, src and dst not equal");
tty->print("src: ");
src.dump_hex();
tty->cr();
tty->print("dst: ");
dst.dump_hex();
tty->cr();
};
// Test copying a larger register mask
for (uint i = 0; i < iterations; i++) {
ResourceMark rm;
// Create source RegMask
RegMask src(arena());
// Grow source randomly
grow_randomly(src);
// Randomly initialize source
randomize(src);
// Copy construct source to destination
RegMask dst(src, arena());
// Check equality
bool passed = src.gtest_equals(dst);
if (Verbose && !passed) {
print_failure(src, dst);
}
ASSERT_TRUE(passed);
}
// Test copying a smaller register mask
for (uint i = 0; i < iterations; i++) {
ResourceMark rm;
// Create destination RegMask
RegMask dst(arena());
// Grow destination arbitrarily (1-3 times)
uint growth = grow_randomly(dst, 1, 3);
// Create source RegMask
RegMask src(arena());
// Grow source arbitrarily, but not as much as destination
grow_randomly(src, 0, growth - 1);
// Randomly initialize source
randomize(src);
// Set destination to source
dst.assignFrom(src);
// Check equality
bool passed = src.gtest_equals(dst);
if (Verbose && !passed) {
print_failure(src, dst);
}
ASSERT_TRUE(passed);
}
}
#endif // !PRODUCT