8312213: Remove unnecessary TEST instructions on x86 when flags reg will already be set

Reviewed-by: jvernee, dlong
This commit is contained in:
Tobias Hotz 2023-09-05 22:08:20 +00:00 committed by Jorn Vernee
parent 1f4cdb327f
commit aba89f20bf
14 changed files with 720 additions and 7 deletions

@ -26,6 +26,7 @@
#ifdef COMPILER2
#include "peephole_x86_64.hpp"
#include "adfiles/ad_x86.hpp"
// This function transforms the shapes
// mov d, s1; add d, s2 into
@ -132,6 +133,109 @@ bool lea_coalesce_helper(Block* block, int block_index, PhaseCFG* cfg_, PhaseReg
return true;
}
// This helper func takes a condition and returns the flags that need to be set for the condition
// It uses the same flags as the test instruction, so if the e.g. the overflow bit is required,
// this func returns clears_overflow, as that is what the test instruction does and what the downstream path expects
juint map_condition_to_required_test_flags(Assembler::Condition condition) {
switch (condition) {
case Assembler::Condition::zero: // Same value as equal
case Assembler::Condition::notZero: // Same value as notEqual
return Node::PD::Flag_sets_zero_flag;
case Assembler::Condition::less:
case Assembler::Condition::greaterEqual:
return Node::PD::Flag_sets_sign_flag | Node::PD::Flag_clears_overflow_flag;
case Assembler::Condition::lessEqual:
case Assembler::Condition::greater:
return Node::PD::Flag_sets_sign_flag | Node::PD::Flag_clears_overflow_flag | Node::PD::Flag_sets_zero_flag;
case Assembler::Condition::below: // Same value as carrySet
case Assembler::Condition::aboveEqual: // Same value as carryClear
return Node::PD::Flag_clears_carry_flag;
case Assembler::Condition::belowEqual:
case Assembler::Condition::above:
return Node::PD::Flag_clears_carry_flag | Node::PD::Flag_sets_zero_flag;
case Assembler::Condition::overflow:
case Assembler::Condition::noOverflow:
return Node::PD::Flag_clears_overflow_flag;
case Assembler::Condition::negative:
case Assembler::Condition::positive:
return Node::PD::Flag_sets_sign_flag;
case Assembler::Condition::parity:
case Assembler::Condition::noParity:
return Node::PD::Flag_sets_parity_flag;
default:
ShouldNotReachHere();
return 0;
}
}
// This function removes the TEST instruction when it detected shapes likes AND r1, r2; TEST r1, r1
// It checks the required EFLAGS for the downstream instructions of the TEST
// and removes the TEST if the preceding instructions already sets all these flags
bool Peephole::test_may_remove(Block* block, int block_index, PhaseCFG* cfg_, PhaseRegAlloc* ra_,
MachNode* (*new_root)(), uint inst0_rule) {
MachNode* test_to_check = block->get_node(block_index)->as_Mach();
assert(test_to_check->rule() == inst0_rule, "sanity");
Node* inst1 = test_to_check->in(1);
// Only remove test if the block order is inst1 -> MachProjNode (because the node to match must specify KILL cr) -> test_to_check
// So inst1 must be at index - 2
if (block_index < 2 || block->get_node(block_index - 2) != inst1) {
return false;
}
if (inst1 != nullptr) {
MachNode* prevNode = inst1->isa_Mach();
if (prevNode != nullptr) {
// Includes other flags as well, but that doesn't matter here
juint all_node_flags = prevNode->flags();
if (all_node_flags == 0) {
// We can return early - there is no way the test can be removed, the preceding node does not set any flags
return false;
}
juint required_flags = 0;
// Search for the uses of the node and compute which flags are required
for (DUIterator_Fast imax, i = test_to_check->fast_outs(imax); i < imax; i++) {
MachNode* node_out = test_to_check->fast_out(i)->isa_Mach();
bool found_correct_oper = false;
for (uint16_t j = 0; j < node_out->_num_opnds; ++j) {
MachOper* operand = node_out->_opnds[j];
if (operand->opcode() == cmpOp_rule || operand->opcode() == cmpOpU_rule) {
auto condition = static_cast<Assembler::Condition>(operand->ccode());
juint flags_for_inst = map_condition_to_required_test_flags(condition);
required_flags = required_flags | flags_for_inst;
found_correct_oper = true;
break;
}
}
if (!found_correct_oper) {
// We could not find one the required flags for one of the dependencies. Keep the test as it might set flags needed for that node
return false;
}
}
assert(required_flags != 0, "No flags required, should be impossible!");
bool sets_all_required_flags = (required_flags & ~all_node_flags) == 0;
if (sets_all_required_flags) {
// All flags are covered are clear to remove this test
MachProjNode* machProjNode = block->get_node(block_index - 1)->isa_MachProj();
assert(machProjNode != nullptr, "Expected a MachProj node here!");
assert(ra_->get_reg_first(machProjNode) == ra_->get_reg_first(test_to_check), "Test must operate on the same register as its replacement");
// Remove the original test node and replace it with the pseudo test node. The AND node already sets ZF
test_to_check->replace_by(machProjNode);
// Modify the block
test_to_check->set_removed();
block->remove_node(block_index);
// Modify the control flow
cfg_->map_node_to_block(test_to_check, nullptr);
return true;
}
}
}
return false;
}
bool Peephole::lea_coalesce_reg(Block* block, int block_index, PhaseCFG* cfg_, PhaseRegAlloc* ra_,
MachNode* (*new_root)(), uint inst0_rule) {
return lea_coalesce_helper(block, block_index, cfg_, ra_, new_root, inst0_rule, false);

@ -34,6 +34,8 @@ public:
MachNode* (*new_root)(), uint inst0_rule);
static bool lea_coalesce_imm(Block* block, int block_index, PhaseCFG* cfg_, PhaseRegAlloc* ra_,
MachNode* (*new_root)(), uint inst0_rule);
static bool test_may_remove(Block* block, int block_index, PhaseCFG* cfg_, PhaseRegAlloc* ra_,
MachNode* (*new_root)(), uint inst0_rule);
};
#endif // CPU_X86_PEEPHOLE_X86_64_HPP

@ -1254,8 +1254,18 @@ static inline bool is_clz_non_subword_predicate_evex(BasicType bt, int vlen_byte
class Node::PD {
public:
enum NodeFlags {
Flag_intel_jcc_erratum = Node::_last_flag << 1,
_last_flag = Flag_intel_jcc_erratum
Flag_intel_jcc_erratum = Node::_last_flag << 1,
Flag_sets_carry_flag = Node::_last_flag << 2,
Flag_sets_parity_flag = Node::_last_flag << 3,
Flag_sets_zero_flag = Node::_last_flag << 4,
Flag_sets_overflow_flag = Node::_last_flag << 5,
Flag_sets_sign_flag = Node::_last_flag << 6,
Flag_clears_carry_flag = Node::_last_flag << 7,
Flag_clears_parity_flag = Node::_last_flag << 8,
Flag_clears_zero_flag = Node::_last_flag << 9,
Flag_clears_overflow_flag = Node::_last_flag << 10,
Flag_clears_sign_flag = Node::_last_flag << 11,
_last_flag = Flag_clears_sign_flag
};
};

@ -7649,7 +7649,7 @@ instruct addI_rReg(rRegI dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (AddI dst src));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
format %{ "addl $dst, $src\t# int" %}
ins_encode %{
__ addl($dst$$Register, $src$$Register);
@ -7661,6 +7661,7 @@ instruct addI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
%{
match(Set dst (AddI dst src));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
format %{ "addl $dst, $src\t# int" %}
ins_encode %{
@ -7673,6 +7674,7 @@ instruct addI_rReg_mem(rRegI dst, memory src, rFlagsReg cr)
%{
match(Set dst (AddI dst (LoadI src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(150); // XXX
format %{ "addl $dst, $src\t# int" %}
@ -7686,6 +7688,7 @@ instruct addI_mem_rReg(memory dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (StoreI dst (AddI (LoadI dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(150); // XXX
format %{ "addl $dst, $src\t# int" %}
@ -7699,6 +7702,8 @@ instruct addI_mem_imm(memory dst, immI src, rFlagsReg cr)
%{
match(Set dst (StoreI dst (AddI (LoadI dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(125); // XXX
format %{ "addl $dst, $src\t# int" %}
@ -7819,6 +7824,7 @@ instruct addL_rReg(rRegL dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (AddL dst src));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
format %{ "addq $dst, $src\t# long" %}
ins_encode %{
@ -7831,6 +7837,7 @@ instruct addL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
%{
match(Set dst (AddL dst src));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
format %{ "addq $dst, $src\t# long" %}
ins_encode %{
@ -7843,6 +7850,7 @@ instruct addL_rReg_mem(rRegL dst, memory src, rFlagsReg cr)
%{
match(Set dst (AddL dst (LoadL src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(150); // XXX
format %{ "addq $dst, $src\t# long" %}
@ -7856,6 +7864,7 @@ instruct addL_mem_rReg(memory dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (StoreL dst (AddL (LoadL dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(150); // XXX
format %{ "addq $dst, $src\t# long" %}
@ -7869,6 +7878,7 @@ instruct addL_mem_imm(memory dst, immL32 src, rFlagsReg cr)
%{
match(Set dst (StoreL dst (AddL (LoadL dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(125); // XXX
format %{ "addq $dst, $src\t# long" %}
@ -7989,6 +7999,7 @@ instruct addP_rReg(rRegP dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (AddP dst src));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
format %{ "addq $dst, $src\t# ptr" %}
ins_encode %{
@ -8001,6 +8012,7 @@ instruct addP_rReg_imm(rRegP dst, immL32 src, rFlagsReg cr)
%{
match(Set dst (AddP dst src));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
format %{ "addq $dst, $src\t# ptr" %}
ins_encode %{
@ -8554,6 +8566,7 @@ instruct subI_rReg(rRegI dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (SubI dst src));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
format %{ "subl $dst, $src\t# int" %}
ins_encode %{
@ -8566,6 +8579,7 @@ instruct subI_rReg_mem(rRegI dst, memory src, rFlagsReg cr)
%{
match(Set dst (SubI dst (LoadI src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(150);
format %{ "subl $dst, $src\t# int" %}
@ -8579,6 +8593,7 @@ instruct subI_mem_rReg(memory dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (StoreI dst (SubI (LoadI dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(150);
format %{ "subl $dst, $src\t# int" %}
@ -8592,6 +8607,7 @@ instruct subL_rReg(rRegL dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (SubL dst src));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
format %{ "subq $dst, $src\t# long" %}
ins_encode %{
@ -8604,6 +8620,7 @@ instruct subL_rReg_mem(rRegL dst, memory src, rFlagsReg cr)
%{
match(Set dst (SubL dst (LoadL src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(150);
format %{ "subq $dst, $src\t# long" %}
@ -8617,6 +8634,7 @@ instruct subL_mem_rReg(memory dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (StoreL dst (SubL (LoadL dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_carry_flag, PD::Flag_sets_parity_flag);
ins_cost(150);
format %{ "subq $dst, $src\t# long" %}
@ -8643,6 +8661,7 @@ instruct negI_rReg(rRegI dst, immI_0 zero, rFlagsReg cr)
%{
match(Set dst (SubI zero dst));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag);
format %{ "negl $dst\t# int" %}
ins_encode %{
@ -8655,6 +8674,7 @@ instruct negI_rReg_2(rRegI dst, rFlagsReg cr)
%{
match(Set dst (NegI dst));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag);
format %{ "negl $dst\t# int" %}
ins_encode %{
@ -8667,6 +8687,7 @@ instruct negI_mem(memory dst, immI_0 zero, rFlagsReg cr)
%{
match(Set dst (StoreI dst (SubI zero (LoadI dst))));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag);
format %{ "negl $dst\t# int" %}
ins_encode %{
@ -8679,6 +8700,7 @@ instruct negL_rReg(rRegL dst, immL0 zero, rFlagsReg cr)
%{
match(Set dst (SubL zero dst));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag);
format %{ "negq $dst\t# long" %}
ins_encode %{
@ -8691,6 +8713,7 @@ instruct negL_rReg_2(rRegL dst, rFlagsReg cr)
%{
match(Set dst (NegL dst));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag);
format %{ "negq $dst\t# int" %}
ins_encode %{
@ -8703,6 +8726,7 @@ instruct negL_mem(memory dst, immL0 zero, rFlagsReg cr)
%{
match(Set dst (StoreL dst (SubL zero (LoadL dst))));
effect(KILL cr);
flag(PD::Flag_sets_overflow_flag, PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag);
format %{ "negq $dst\t# long" %}
ins_encode %{
@ -9184,6 +9208,7 @@ instruct sarI_rReg_CL(rRegI dst, rcx_RegI shift, rFlagsReg cr)
predicate(!VM_Version::supports_bmi2());
match(Set dst (RShiftI dst shift));
effect(KILL cr);
format %{ "sarl $dst, $shift" %}
ins_encode %{
__ sarl($dst$$Register);
@ -9838,6 +9863,7 @@ instruct andI_rReg(rRegI dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (AndI dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "andl $dst, $src\t# int" %}
ins_encode %{
@ -9914,6 +9940,7 @@ instruct andI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
%{
match(Set dst (AndI dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "andl $dst, $src\t# int" %}
ins_encode %{
@ -9927,6 +9954,7 @@ instruct andI_rReg_mem(rRegI dst, memory src, rFlagsReg cr)
%{
match(Set dst (AndI dst (LoadI src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "andl $dst, $src\t# int" %}
@ -9941,6 +9969,7 @@ instruct andB_mem_rReg(memory dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (StoreB dst (AndI (LoadB dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "andb $dst, $src\t# byte" %}
@ -9954,6 +9983,7 @@ instruct andI_mem_rReg(memory dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (StoreI dst (AndI (LoadI dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "andl $dst, $src\t# int" %}
@ -9968,6 +9998,7 @@ instruct andI_mem_imm(memory dst, immI src, rFlagsReg cr)
%{
match(Set dst (StoreI dst (AndI (LoadI dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(125);
format %{ "andl $dst, $src\t# int" %}
@ -9982,6 +10013,7 @@ instruct andnI_rReg_rReg_mem(rRegI dst, rRegI src1, memory src2, immI_M1 minus_1
match(Set dst (AndI (XorI src1 minus_1) (LoadI src2)));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(125);
format %{ "andnl $dst, $src1, $src2" %}
@ -9996,6 +10028,7 @@ instruct andnI_rReg_rReg_rReg(rRegI dst, rRegI src1, rRegI src2, immI_M1 minus_1
match(Set dst (AndI (XorI src1 minus_1) src2));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "andnl $dst, $src1, $src2" %}
@ -10009,6 +10042,7 @@ instruct blsiI_rReg_rReg(rRegI dst, rRegI src, immI_0 imm_zero, rFlagsReg cr) %{
match(Set dst (AndI (SubI imm_zero src) src));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag);
format %{ "blsil $dst, $src" %}
@ -10022,6 +10056,7 @@ instruct blsiI_rReg_mem(rRegI dst, memory src, immI_0 imm_zero, rFlagsReg cr) %{
match(Set dst (AndI (SubI imm_zero (LoadI src) ) (LoadI src) ));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag);
ins_cost(125);
format %{ "blsil $dst, $src" %}
@ -10037,6 +10072,7 @@ instruct blsmskI_rReg_mem(rRegI dst, memory src, immI_M1 minus_1, rFlagsReg cr)
match(Set dst (XorI (AddI (LoadI src) minus_1) (LoadI src) ) );
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_clears_zero_flag, PD::Flag_clears_overflow_flag);
ins_cost(125);
format %{ "blsmskl $dst, $src" %}
@ -10052,6 +10088,7 @@ instruct blsmskI_rReg_rReg(rRegI dst, rRegI src, immI_M1 minus_1, rFlagsReg cr)
match(Set dst (XorI (AddI src minus_1) src));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_clears_zero_flag, PD::Flag_clears_overflow_flag);
format %{ "blsmskl $dst, $src" %}
@ -10067,6 +10104,7 @@ instruct blsrI_rReg_rReg(rRegI dst, rRegI src, immI_M1 minus_1, rFlagsReg cr)
match(Set dst (AndI (AddI src minus_1) src) );
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag);
format %{ "blsrl $dst, $src" %}
@ -10082,6 +10120,7 @@ instruct blsrI_rReg_mem(rRegI dst, memory src, immI_M1 minus_1, rFlagsReg cr)
match(Set dst (AndI (AddI (LoadI src) minus_1) (LoadI src) ) );
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag);
ins_cost(125);
format %{ "blsrl $dst, $src" %}
@ -10099,6 +10138,7 @@ instruct orI_rReg(rRegI dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (OrI dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "orl $dst, $src\t# int" %}
ins_encode %{
@ -10112,6 +10152,7 @@ instruct orI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
%{
match(Set dst (OrI dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "orl $dst, $src\t# int" %}
ins_encode %{
@ -10125,6 +10166,7 @@ instruct orI_rReg_mem(rRegI dst, memory src, rFlagsReg cr)
%{
match(Set dst (OrI dst (LoadI src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "orl $dst, $src\t# int" %}
@ -10139,6 +10181,7 @@ instruct orB_mem_rReg(memory dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (StoreB dst (OrI (LoadB dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "orb $dst, $src\t# byte" %}
@ -10152,6 +10195,7 @@ instruct orI_mem_rReg(memory dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (StoreI dst (OrI (LoadI dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "orl $dst, $src\t# int" %}
@ -10166,6 +10210,7 @@ instruct orI_mem_imm(memory dst, immI src, rFlagsReg cr)
%{
match(Set dst (StoreI dst (OrI (LoadI dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(125);
format %{ "orl $dst, $src\t# int" %}
@ -10181,6 +10226,7 @@ instruct xorI_rReg(rRegI dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (XorI dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "xorl $dst, $src\t# int" %}
ins_encode %{
@ -10205,6 +10251,7 @@ instruct xorI_rReg_imm(rRegI dst, immI src, rFlagsReg cr)
%{
match(Set dst (XorI dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "xorl $dst, $src\t# int" %}
ins_encode %{
@ -10218,6 +10265,7 @@ instruct xorI_rReg_mem(rRegI dst, memory src, rFlagsReg cr)
%{
match(Set dst (XorI dst (LoadI src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "xorl $dst, $src\t# int" %}
@ -10232,6 +10280,7 @@ instruct xorB_mem_rReg(memory dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (StoreB dst (XorI (LoadB dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "xorb $dst, $src\t# byte" %}
@ -10245,6 +10294,7 @@ instruct xorI_mem_rReg(memory dst, rRegI src, rFlagsReg cr)
%{
match(Set dst (StoreI dst (XorI (LoadI dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "xorl $dst, $src\t# int" %}
@ -10259,6 +10309,7 @@ instruct xorI_mem_imm(memory dst, immI src, rFlagsReg cr)
%{
match(Set dst (StoreI dst (XorI (LoadI dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(125);
format %{ "xorl $dst, $src\t# int" %}
@ -10277,6 +10328,7 @@ instruct andL_rReg(rRegL dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (AndL dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "andq $dst, $src\t# long" %}
ins_encode %{
@ -10316,6 +10368,7 @@ instruct andL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
%{
match(Set dst (AndL dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "andq $dst, $src\t# long" %}
ins_encode %{
@ -10329,6 +10382,7 @@ instruct andL_rReg_mem(rRegL dst, memory src, rFlagsReg cr)
%{
match(Set dst (AndL dst (LoadL src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "andq $dst, $src\t# long" %}
@ -10343,6 +10397,7 @@ instruct andL_mem_rReg(memory dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (StoreL dst (AndL (LoadL dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "andq $dst, $src\t# long" %}
@ -10357,6 +10412,7 @@ instruct andL_mem_imm(memory dst, immL32 src, rFlagsReg cr)
%{
match(Set dst (StoreL dst (AndL (LoadL dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(125);
format %{ "andq $dst, $src\t# long" %}
@ -10388,6 +10444,7 @@ instruct andnL_rReg_rReg_mem(rRegL dst, rRegL src1, memory src2, immL_M1 minus_1
match(Set dst (AndL (XorL src1 minus_1) (LoadL src2)));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(125);
format %{ "andnq $dst, $src1, $src2" %}
@ -10402,6 +10459,7 @@ instruct andnL_rReg_rReg_rReg(rRegL dst, rRegL src1, rRegL src2, immL_M1 minus_1
match(Set dst (AndL (XorL src1 minus_1) src2));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "andnq $dst, $src1, $src2" %}
@ -10415,6 +10473,7 @@ instruct blsiL_rReg_rReg(rRegL dst, rRegL src, immL0 imm_zero, rFlagsReg cr) %{
match(Set dst (AndL (SubL imm_zero src) src));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag);
format %{ "blsiq $dst, $src" %}
@ -10428,6 +10487,7 @@ instruct blsiL_rReg_mem(rRegL dst, memory src, immL0 imm_zero, rFlagsReg cr) %{
match(Set dst (AndL (SubL imm_zero (LoadL src) ) (LoadL src) ));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag);
ins_cost(125);
format %{ "blsiq $dst, $src" %}
@ -10443,6 +10503,7 @@ instruct blsmskL_rReg_mem(rRegL dst, memory src, immL_M1 minus_1, rFlagsReg cr)
match(Set dst (XorL (AddL (LoadL src) minus_1) (LoadL src) ) );
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_clears_zero_flag, PD::Flag_clears_overflow_flag);
ins_cost(125);
format %{ "blsmskq $dst, $src" %}
@ -10458,6 +10519,7 @@ instruct blsmskL_rReg_rReg(rRegL dst, rRegL src, immL_M1 minus_1, rFlagsReg cr)
match(Set dst (XorL (AddL src minus_1) src));
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_clears_zero_flag, PD::Flag_clears_overflow_flag);
format %{ "blsmskq $dst, $src" %}
@ -10473,6 +10535,7 @@ instruct blsrL_rReg_rReg(rRegL dst, rRegL src, immL_M1 minus_1, rFlagsReg cr)
match(Set dst (AndL (AddL src minus_1) src) );
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag);
format %{ "blsrq $dst, $src" %}
@ -10488,6 +10551,7 @@ instruct blsrL_rReg_mem(rRegL dst, memory src, immL_M1 minus_1, rFlagsReg cr)
match(Set dst (AndL (AddL (LoadL src) minus_1) (LoadL src)) );
predicate(UseBMI1Instructions);
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_clears_overflow_flag);
ins_cost(125);
format %{ "blsrq $dst, $src" %}
@ -10505,6 +10569,7 @@ instruct orL_rReg(rRegL dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (OrL dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "orq $dst, $src\t# long" %}
ins_encode %{
@ -10517,6 +10582,7 @@ instruct orL_rReg(rRegL dst, rRegL src, rFlagsReg cr)
instruct orL_rReg_castP2X(rRegL dst, any_RegP src, rFlagsReg cr) %{
match(Set dst (OrL dst (CastP2X src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "orq $dst, $src\t# long" %}
ins_encode %{
@ -10531,6 +10597,7 @@ instruct orL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
%{
match(Set dst (OrL dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "orq $dst, $src\t# long" %}
ins_encode %{
@ -10544,6 +10611,7 @@ instruct orL_rReg_mem(rRegL dst, memory src, rFlagsReg cr)
%{
match(Set dst (OrL dst (LoadL src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "orq $dst, $src\t# long" %}
@ -10558,6 +10626,7 @@ instruct orL_mem_rReg(memory dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (StoreL dst (OrL (LoadL dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "orq $dst, $src\t# long" %}
@ -10572,6 +10641,7 @@ instruct orL_mem_imm(memory dst, immL32 src, rFlagsReg cr)
%{
match(Set dst (StoreL dst (OrL (LoadL dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(125);
format %{ "orq $dst, $src\t# long" %}
@ -10604,6 +10674,7 @@ instruct xorL_rReg(rRegL dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (XorL dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "xorq $dst, $src\t# long" %}
ins_encode %{
@ -10628,6 +10699,7 @@ instruct xorL_rReg_imm(rRegL dst, immL32 src, rFlagsReg cr)
%{
match(Set dst (XorL dst src));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
format %{ "xorq $dst, $src\t# long" %}
ins_encode %{
@ -10641,6 +10713,7 @@ instruct xorL_rReg_mem(rRegL dst, memory src, rFlagsReg cr)
%{
match(Set dst (XorL dst (LoadL src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "xorq $dst, $src\t# long" %}
@ -10655,6 +10728,7 @@ instruct xorL_mem_rReg(memory dst, rRegL src, rFlagsReg cr)
%{
match(Set dst (StoreL dst (XorL (LoadL dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(150);
format %{ "xorq $dst, $src\t# long" %}
@ -10669,6 +10743,7 @@ instruct xorL_mem_imm(memory dst, immL32 src, rFlagsReg cr)
%{
match(Set dst (StoreL dst (XorL (LoadL dst) src)));
effect(KILL cr);
flag(PD::Flag_sets_sign_flag, PD::Flag_sets_zero_flag, PD::Flag_sets_parity_flag, PD::Flag_clears_overflow_flag, PD::Flag_clears_carry_flag);
ins_cost(125);
format %{ "xorq $dst, $src\t# long" %}
@ -13863,6 +13938,24 @@ peephole
peepreplace (leaL_rReg_immI2_peep());
%}
// These peephole rules matches instructions which set flags and are followed by a testI/L_reg
// The test instruction is redudanent in case the downstream instuctions (like JCC or CMOV) only use flags that are already set by the previous instruction
//int variant
peephole
%{
peepmatch (testI_reg);
peepprocedure (test_may_remove);
%}
//long variant
peephole
%{
peepmatch (testL_reg);
peepprocedure (test_may_remove);
%}
//----------SMARTSPILL RULES---------------------------------------------------
// These must follow all instruction definitions as they use the names
// defined in the instructions definitions.

@ -229,6 +229,7 @@ void ADLParser::instr_parse(void) {
else if (!strcmp(ident, "opcode")) instr->_opcode = opcode_parse(instr);
else if (!strcmp(ident, "size")) instr->_size = size_parse(instr);
else if (!strcmp(ident, "effect")) effect_parse(instr);
else if (!strcmp(ident, "flag")) instr->_flag = flag_parse(instr);
else if (!strcmp(ident, "expand")) instr->_exprule = expand_parse(instr);
else if (!strcmp(ident, "rewrite")) instr->_rewrule = rewrite_parse();
else if (!strcmp(ident, "constraint")) {
@ -4126,6 +4127,46 @@ void ADLParser::effect_parse(InstructForm *instr) {
}
//-------------------------------flag_parse------------------------------------
Flag* ADLParser::flag_parse(InstructForm *instr) {
char* ident = nullptr;
Flag* result = nullptr;
skipws(); // Skip whitespace
if (_curchar != '(') {
parse_err(SYNERR, "missing '(' in flag definition\n");
return nullptr;
}
do {
next_char();
skipws();
if (_curchar == ')') break;
ident = get_ident();
if (ident == nullptr) {
parse_err(SYNERR, "flag name expected at %c\n", _curchar);
return nullptr;
}
Flag* newflag = new Flag(ident);
if (result == nullptr) result = newflag;
else result->append_flag(newflag);
if (_AD._adl_debug > 1) fprintf(stderr, "\tFlag Name: %s\n", ident);
skipws();
} while (_curchar == ',');
if (_curchar != ')') parse_err(SYNERR, "missing ')'\n");
else {
next_char(); // set current character position past the close paren
}
// Debug Stuff
if (_curchar != ';') {
parse_err(SYNERR, "missing ';' in Flag definition\n");
}
// Skip ';'
next_char();
return result;
}
//------------------------------expand_parse-----------------------------------
ExpandRule* ADLParser::expand_parse(InstructForm *instr) {
char *ident, *ident2;

@ -47,6 +47,7 @@ class Encode;
class Attribute;
class Effect;
class ExpandRule;
class Flag;
class RewriteRule;
class Constraint;
class ConstructRule;
@ -177,6 +178,7 @@ protected:
FormatRule *format_parse(void); // Parse format rule
FormatRule *template_parse(void); // Parse format rule
void effect_parse(InstructForm *instr); // Parse effect rule
Flag *flag_parse(InstructForm *instr); // Parse flag rule
ExpandRule *expand_parse(InstructForm *instr); // Parse expand rule
RewriteRule *rewrite_parse(void); // Parse rewrite rule
Constraint *constraint_parse(void); // Parse constraint rule

@ -54,6 +54,7 @@ class MatchRule;
class Attribute;
class Effect;
class ExpandRule;
class Flag;
class RewriteRule;
class ConstructRule;
class FormatRule;
@ -237,6 +238,7 @@ public:
EXP,
REW,
EFF,
FLG,
RDEF,
RCL,
ACL,

@ -44,6 +44,7 @@ class MatchRule;
class Attribute;
class Effect;
class ExpandRule;
class Flag;
class RewriteRule;
class ConstructRule;
class FormatRule;

@ -52,6 +52,7 @@ InstructForm::InstructForm(const char *id, bool ideal_only)
_format = nullptr;
_peephole = nullptr;
_ins_pipe = nullptr;
_flag = nullptr;
_uniq_idx = nullptr;
_num_uniq = 0;
_cisc_spill_operand = Not_cisc_spillable;// Which operand may cisc-spill
@ -86,6 +87,7 @@ InstructForm::InstructForm(const char *id, InstructForm *instr, MatchRule *rule)
_format = instr->_format;
_peephole = instr->_peephole;
_ins_pipe = instr->_ins_pipe;
_flag = instr->_flag;
_uniq_idx = instr->_uniq_idx;
_num_uniq = instr->_num_uniq;
_cisc_spill_operand = Not_cisc_spillable; // Which operand may cisc-spill
@ -1893,6 +1895,34 @@ void Effect::output(FILE *fp) { // Write info to output files
fprintf(fp,"Effect: %s\n", (_name?_name:""));
}
//---------------------------------Flag----------------------------------------
Flag::Flag(const char *name) : _name(name), _next(nullptr) {
_ftype = Form::FLG;
}
Flag::~Flag() {
}
void Flag::append_flag(Flag *next_flag) {
if( _next == nullptr ) {
_next = next_flag;
} else {
_next->append_flag( next_flag );
}
}
Flag* Flag::next() {
return _next;
}
void Flag::dump() {
output(stderr);
}
void Flag::output(FILE *fp) { // Write info to output files
fprintf(fp,"Flag: %s\n", (_name?_name:""));
}
//------------------------------ExpandRule-------------------------------------
ExpandRule::ExpandRule() : _expand_instrs(),
_newopconst(cmpstr, hashstr, Form::arena) {

@ -45,6 +45,7 @@ class MatchRule;
class Attribute;
class Effect;
class ExpandRule;
class Flag;
class RewriteRule;
class ConstructRule;
class FormatRule;
@ -108,6 +109,7 @@ public:
FormatRule *_format; // Format for assembly generation
Peephole *_peephole; // List of peephole rules for instruction
const char *_ins_pipe; // Instruction Scheduling description class
Flag *_flag; // List of Flags that should be set by default for this node
uint *_uniq_idx; // Indexes of unique operands
uint _uniq_idx_length; // Length of _uniq_idx array
@ -515,6 +517,26 @@ public:
void output(FILE *fp); // Write info to output files
};
//---------------------------------Flag----------------------------------------
class Flag : public Form {
private:
Flag* _next;
public:
const char *_name; // Name of the flag (See Node::<flag_name> or Node::Pd::<flag_name>
// Public Methods
Flag(const char *name); // Constructor
~Flag(); // Destructor
// Append a flag rule for the same instruction
void append_flag(Flag *next_flag);
Flag* next();
void dump(); // Debug printer
void output(FILE *fp); // Write info to output files
};
//------------------------------RewriteRule------------------------------------
class RewriteRule : public Form {
private:

@ -1469,10 +1469,14 @@ void ArchDesc::definePeephole(FILE *fp, InstructForm *node) {
// End of scope for this peephole's constraints
fprintf(fp, " }\n");
} else {
const char* replace_inst = nullptr;
preplace->next_instruction(replace_inst);
// Generate the target instruction
fprintf(fp, " auto replacing = [](){ return static_cast<MachNode*>(new %sNode()); };\n", replace_inst);
if (preplace != nullptr) {
const char *replace_inst = nullptr;
preplace->next_instruction(replace_inst);
// Generate the target instruction
fprintf(fp, " auto replacing = [](){ return static_cast<MachNode*>(new %sNode()); };\n", replace_inst);
} else {
fprintf(fp, " auto replacing = nullptr;\n");
}
// Call the precedure
fprintf(fp, " bool replacement = Peephole::%s(block, block_index, cfg_, ra_, replacing", pprocedure->name());
@ -4010,6 +4014,22 @@ void ArchDesc::buildMachNode(FILE *fp_cpp, InstructForm *inst, const char *inden
fprintf(fp_cpp, " );\n");
// #####
}
if (inst->_flag != nullptr) {
Flag* node = inst->_flag;
const char* prefix = "Node::";
bool node_flags_set = false;
do {
if (!node_flags_set) {
fprintf(fp_cpp, "%s node->add_flag(%s%s", indent, prefix, node->_name);
node_flags_set = true;
} else {
fprintf(fp_cpp, " | %s%s", prefix, node->_name);
}
} while ((node = node->next()) != nullptr);
if (node_flags_set) {
fprintf(fp_cpp, ");\n");
}
}
// Fill in the bottom_type where requested
if (inst->captures_bottom_type(_globalNames)) {

@ -0,0 +1,183 @@
/*
* Copyright (c) 2023, 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.
*/
package compiler.c2.irTests;
import jdk.test.lib.Asserts;
import compiler.lib.ir_framework.*;
import java.util.Random;
import jdk.test.lib.Utils;
/*
* @test
* @summary Test that unnessercary test instructions are not present in the final code
* @bug 8312213
* @library /test/lib /
* @requires vm.compiler2.enabled
* @requires os.arch == "x86_64" | os.arch == "amd64"
* @run driver compiler.c2.irTests.TestTestRemovalPeephole
*/
public class TestTestRemovalPeephole {
static volatile boolean field;
public static void main(String[] args) {
TestFramework.run();
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testIntAddtionEquals0(int x, int y) {
int result = x + y;
if (result == 0) {
field = true;
return true;
}
return false;
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testIntAddtionNotEquals0(int x, int y) {
int result = x + y;
if (result != 0) {
field = true;
return true;
}
return false;
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testLongAddtionEquals0(long x, long y) {
long result = x + y;
if (result == 0) {
field = true;
return true;
}
return false;
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testLongAddtionNotEquals0(long x, long y) {
long result = x + y;
if (result != 0) {
field = true;
return true;
}
return false;
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testIntOrEquals0(int x, int y) {
int result = x | y;
if (result == 0) {
field = true;
return true;
}
return false;
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testIntOrNotEquals0(int x, int y) {
int result = x | y;
if (result != 0) {
field = true;
return true;
}
return false;
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testLongOrEquals0(long x, long y) {
long result = x | y;
if (result == 0) {
field = true;
return true;
}
return false;
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testLongOrNotEquals0(long x, long y) {
long result = x | y;
if (result != 0) {
field = true;
return true;
}
return false;
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testIntOrGreater0(int x, int y) {
int result = x | y;
if (result > 0) {
field = true;
return true;
}
return false;
}
@Test
@Arguments({Argument.RANDOM_EACH, Argument.RANDOM_EACH})
@IR(failOn = {IRNode.X86_TESTI_REG, IRNode.X86_TESTL_REG}, phase = CompilePhase.FINAL_CODE)
public boolean testLongOrGreater0(long x, long y) {
long result = x | y;
if (result > 0) {
field = true;
return true;
}
return false;
}
@DontCompile
public void assertResult(int x, int y) {
Asserts.assertEQ((x + y) == 0, testIntAddtionEquals0(x, y));
Asserts.assertEQ((x + y) != 0, testIntAddtionNotEquals0(x, y));
Asserts.assertEQ((x | y) == 0, testIntOrEquals0(x, y));
Asserts.assertEQ((x | y) != 0, testIntOrNotEquals0(x, y));
Asserts.assertEQ((x | y) > 0, testIntOrGreater0(x, y));
}
@DontCompile
public void assertResult(long x, long y) {
Asserts.assertEQ((x + y) == 0, testLongAddtionEquals0(x, y));
Asserts.assertEQ((x + y) != 0, testLongAddtionNotEquals0(x, y));
Asserts.assertEQ((x | y) == 0, testLongOrEquals0(x, y));
Asserts.assertEQ((x | y) != 0, testLongOrNotEquals0(x, y));
Asserts.assertEQ((x | y) > 0, testLongOrGreater0(x, y));
}
}

@ -2119,6 +2119,16 @@ public class IRNode {
machOnlyNameRegex(X86_LOCK_XADDL, "xaddL");
}
public static final String X86_TESTI_REG = PREFIX + "X86_TESTI_REG" + POSTFIX;
static {
machOnlyNameRegex(X86_TESTI_REG, "testI_reg");
}
public static final String X86_TESTL_REG = PREFIX + "X86_TESTL_REG" + POSTFIX;
static {
machOnlyNameRegex(X86_TESTL_REG, "testL_reg");
}
/*
* Utility methods to set up IR_NODE_MAPPINGS.
*/

@ -0,0 +1,193 @@
/*
* Copyright (c) 2023, 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.
*/
package org.openjdk.bench.vm.compiler.x86;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(value = 2)
@Warmup(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 4, time = 2, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
public class TestRemovalPeephole {
long[] valuesLong1;
long[] valuesLong2;
int[] valuesInt1;
int[] valuesInt2;
long valueLong1;
long valueLong2;
int valueInt1;
int valueInt2;
@Setup
public void setup() {
Random random = new Random(42);
valuesLong1 = new long[128];
valuesLong2 = new long[128];
for (int i = 0; i < valuesLong1.length; i++) {
valuesLong1[i] = random.nextLong();
}
for (int i = 0; i < valuesLong2.length; i++) {
valuesLong2[i] = random.nextLong();
}
valuesInt1 = new int[128];
valuesInt2 = new int[128];
for (int i = 0; i < valuesInt1.length; i++) {
valuesInt1[i] = random.nextInt();
}
for (int i = 0; i < valuesInt2.length; i++) {
valuesInt2[i] = random.nextInt();
}
valueLong1 = random.nextLong();
valueLong2 = random.nextLong();
valueInt1 = random.nextInt();
valueInt2 = random.nextInt();
}
@Benchmark
public void benchmarkAndTestFusableInt(Blackhole bh) {
for (int i = 0; i < valuesInt1.length; i++) {
int value1 = valuesInt1[i];
int value2 = valuesInt2[i];
int withAnd1 = value1 & 0xF;
int withAnd2 = value2 & 0xF;
bh.consume(withAnd1 > 0x0 && withAnd2 > 0x0 && withAnd1 < 0xF && withAnd2 < 0xF);
}
}
@Benchmark
public void benchmarkAndTestFusableLong(Blackhole bh) {
for (int i = 0; i < valuesLong1.length; i++) {
long value1 = valuesLong1[i];
long value2 = valuesLong2[i];
long withAnd1 = value1 & 0xFFFFFFFFFFL;
long withAnd2 = value2 & 0xFFFFFFFFFFL;
bh.consume(withAnd1 > 0x0L && withAnd2 > 0x0L && withAnd1 < 0xFFFFFFFFFFL && withAnd2 < 0xFFFFFFFFFFL);
}
}
@Benchmark
public void benchmarkOrTestFusableInt(Blackhole bh) {
for (int i = 0; i < valuesInt1.length; i++) {
int value1 = valuesInt1[i];
int value2 = valuesInt2[i];
int withAnd1 = value1 | 0xF;
int withAnd2 = value2 | 0xF;
bh.consume(withAnd1 > 0x0 && withAnd2 > 0x0 && withAnd1 < 0xF && withAnd2 < 0xF);
}
}
@Benchmark
public void benchmarkOrTestFusableLong(Blackhole bh) {
for (int i = 0; i < valuesLong1.length; i++) {
long value1 = valuesLong1[i];
long value2 = valuesLong2[i];
long withAnd1 = value1 | 0xFFFFFFFFFFL;
long withAnd2 = value2 | 0xFFFFFFFFFFL;
bh.consume(withAnd1 > 0x0L && withAnd2 > 0x0L && withAnd1 < 0xFFFFFFFFFFL && withAnd2 < 0xFFFFFFFFFFL);
}
}
@Benchmark
public void benchmarkXorTestFusableInt(Blackhole bh) {
for (int i = 0; i < valuesInt1.length; i++) {
int value1 = valuesInt1[i];
int value2 = valuesInt2[i];
int withAnd1 = value1 ^ 0xF;
int withAnd2 = value2 ^ 0xF;
bh.consume(withAnd1 > 0x0 && withAnd2 > 0x0 && withAnd1 < 0xF && withAnd2 < 0xF);
}
}
@Benchmark
public void benchmarkXorTestFusableLong(Blackhole bh) {
for (int i = 0; i < valuesLong1.length; i++) {
long value1 = valuesLong1[i];
long value2 = valuesLong2[i];
long withAnd1 = value1 ^ 0xFFFFFFFFFFL;
long withAnd2 = value2 ^ 0xFFFFFFFFFFL;
bh.consume(withAnd1 > 0x0L && withAnd2 > 0x0L && withAnd1 < 0xFFFFFFFFFFL && withAnd2 < 0xFFFFFFFFFFL);
}
}
@Benchmark
public void benchmarkAndTestFusableIntSingle(Blackhole bh) {
int withAnd1 = valueInt1 & 0xF;
int withAnd2 = valueInt2 & 0xF;
bh.consume(withAnd1 > 0x0 && withAnd2 > 0x0 && withAnd1 < 0xF && withAnd2 < 0xF);
}
@Benchmark
public void benchmarkAndTestFusableLongSingle(Blackhole bh) {
long withAnd1 = valueLong1 & 0xFFFFFFFFFFL;
long withAnd2 = valueLong2 & 0xFFFFFFFFFFL;
bh.consume(withAnd1 > 0x0L && withAnd2 > 0x0L && withAnd1 < 0xFFFFFFFFFFL && withAnd2 < 0xFFFFFFFFFFL);
}
@Benchmark
public void benchmarkOrTestFusableIntSingle(Blackhole bh) {
int withAnd1 = valueInt1 | 0xF;
int withAnd2 = valueInt2 | 0xF;
bh.consume(withAnd1 > 0x0 && withAnd2 > 0x0 && withAnd1 < 0xF && withAnd2 < 0xF);
}
@Benchmark
public void benchmarkOrTestFusableLongSingle(Blackhole bh) {
long withAnd1 = valueLong1 | 0xFFFFFFFFFFL;
long withAnd2 = valueLong2 | 0xFFFFFFFFFFL;
bh.consume(withAnd1 > 0x0L && withAnd2 > 0x0L && withAnd1 < 0xFFFFFFFFFFL && withAnd2 < 0xFFFFFFFFFFL);
}
@Benchmark
public void benchmarkXorTestFusableIntSingle(Blackhole bh) {
int withAnd1 = valueInt1 ^ 0xF;
int withAnd2 = valueInt2 ^ 0xF;
bh.consume(withAnd1 > 0x0 && withAnd2 > 0x0 && withAnd1 < 0xF && withAnd2 < 0xF);
}
@Benchmark
public void benchmarkXorTestFusableLongSingle(Blackhole bh) {
long withAnd1 = valueLong1 ^ 0xFFFFFFFFFFL;
long withAnd2 = valueLong2 ^ 0xFFFFFFFFFFL;
bh.consume(withAnd1 > 0x0L && withAnd2 > 0x0L && withAnd1 < 0xFFFFFFFFFFL && withAnd2 < 0xFFFFFFFFFFL);
}
}