8387184: [PPC64] C1 logic operations should support generic constants

Reviewed-by: rrich, dbriemann
This commit is contained in:
Martin Doerr 2026-06-30 14:21:06 +00:00
parent 88d111e24e
commit fa2ca3d087
5 changed files with 85 additions and 115 deletions

View File

@ -75,23 +75,46 @@ int Assembler::branch_destination(int inst, int pos) {
return r;
}
// Low-level andi-one-instruction-macro.
void Assembler::andi(Register a, Register s, const long ui16) {
if (is_power_of_2(((unsigned long) ui16)+1)) {
// Low-level andi-one-instruction-macro. May clobber CR0.
void Assembler::andi(Register a, Register s, julong int_or_long_const) {
// Instructions which don't set CR0 are preferred.
if (int_or_long_const == 0) {
// should not be handled as pow2minus1
li(a, 0);
} else if (is_power_of_2(int_or_long_const + 1)) {
// pow2minus1
clrldi(a, s, 64 - log2i_exact((((unsigned long) ui16)+1)));
} else if (is_power_of_2((jlong) ui16)) {
// pow2
rlwinm(a, s, 0, 31 - log2i_exact((jlong) ui16), 31 - log2i_exact((jlong) ui16));
} else if (is_power_of_2((jlong)-ui16)) {
// negpow2
clrrdi(a, s, log2i_exact((jlong)-ui16));
clrldi(a, s, 64 - log2i_exact(int_or_long_const + 1));
} else if (is_power_of_2(-int_or_long_const)) {
// negpow2 (includes (julong)min_jlong)
clrrdi(a, s, log2i_exact(-int_or_long_const));
} else if (is_uimm((jlong)int_or_long_const, 32) && has_consecutive_ones(int_or_long_const)) {
// consecutive ones
rlwinm(a, s, 0, count_leading_zeros((uint32_t)int_or_long_const),
31 - count_trailing_zeros((uint32_t)int_or_long_const));
} else if (is_uimm((jlong)int_or_long_const, 16)) {
// side effect: clobbers CR0
andi_(a, s, int_or_long_const);
} else {
assert(is_uimm(ui16, 16), "must be 16-bit unsigned immediate");
andi_(a, s, ui16);
assert(is_uimm((jlong)int_or_long_const, 32) && (int_or_long_const & 0xFFFF) == 0,
"not encodable: " UINT64_FORMAT_X, int_or_long_const);
// side effect: clobbers CR0
andis_(a, s, int_or_long_const >> 16);
}
}
// Check if int_or_long_const is supported by Assembler::andi.
bool Assembler::andi_supports(julong int_or_long_const) {
// 16 bit always possible by andi_ (but other instructions are preferred)
if (is_uimm((jlong)int_or_long_const, 16)) return true;
// special cases 32 bit: higher 16 bit and consecutive ones are supported
if (is_uimm((jlong)int_or_long_const, 32) &&
((int_or_long_const & 0xFFFF) == 0 || has_consecutive_ones(int_or_long_const))) return true;
// special cases 64 bit: clrldi, clrrdi
return is_power_of_2(int_or_long_const + 1) || is_power_of_2(-int_or_long_const);
}
// RegisterOrConstant version.
void Assembler::ld(Register d, RegisterOrConstant roc, Register s1) {
if (roc.is_constant()) {

View File

@ -1048,6 +1048,13 @@ class Assembler : public AbstractAssembler {
return (julong)x < maxplus1;
}
// Test if x has exactly one consecutive range of one bits (e.g. 00111000)
static bool has_consecutive_ones(julong x) {
if (x == max_julong) return true;
if (x == 0) return false;
return is_power_of_2((x >> count_trailing_zeros(x)) + 1);
}
protected:
// helpers
@ -1606,7 +1613,8 @@ class Assembler : public AbstractAssembler {
inline void isel_0( Register d, ConditionRegister cr, Condition cc, Register b = noreg);
// PPC 1, section 3.3.11, Fixed-Point Logical Instructions
void andi( Register a, Register s, long ui16); // optimized version
void andi( Register a, Register s, julong int_or_long_const); // optimized version, may clobber CR0
static bool andi_supports(julong int_or_long_const);
inline void andi_( Register a, Register s, int ui16);
inline void andis_( Register a, Register s, int ui16);
inline void ori( Register a, Register s, int ui16);

View File

@ -1669,26 +1669,40 @@ void LIR_Assembler::logic_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr
d = dest->as_register_lo();
l = left->as_register_lo();
}
long uimms = (unsigned long)uimm >> 16,
uimmss = (unsigned long)uimm >> 32;
long uimms = (unsigned long)uimm >> 16;
switch (code) {
case lir_logic_and:
if (uimmss != 0 || (uimms != 0 && (uimm & 0xFFFF) != 0) || is_power_of_2(uimm)) {
__ andi(d, l, uimm); // special cases
} else if (uimms != 0) { __ andis_(d, l, uimms); }
else { __ andi_(d, l, uimm); }
if (Assembler::andi_supports(uimm)) {
__ andi(d, l, uimm); // includes andis_ and special cases
} else { // for operands which are not generated by LIRGenerator::do_LogicOp
__ load_const_optimized(R0, uimm);
__ andr(d, l, R0);
}
break;
case lir_logic_or:
if (uimms != 0) { assert((uimm & 0xFFFF) == 0, "sanity"); __ oris(d, l, uimms); }
else { __ ori(d, l, uimm); }
if (Assembler::is_uimm(uimm, 16)) {
__ ori(d, l, uimm);
} else if ((uimm & 0xFFFF) == 0 && Assembler::is_uimm(uimms, 16)) {
__ oris(d, l, uimms);
} else { // for operands which are not generated by LIRGenerator::do_LogicOp
__ load_const_optimized(R0, uimm);
__ orr(d, l, R0);
}
break;
case lir_logic_xor:
if (uimm == -1) { __ nand(d, l, l); } // special case
else if (uimms != 0) { assert((uimm & 0xFFFF) == 0, "sanity"); __ xoris(d, l, uimms); }
else { __ xori(d, l, uimm); }
if (Assembler::is_uimm(uimm, 16)) {
__ xori(d, l, uimm);
} else if ((uimm & 0xFFFF) == 0 && Assembler::is_uimm(uimms, 16)) {
__ xoris(d, l, uimms);
} else if (uimm == -1) {
__ nand(d, l, l); // special case
} else { // for operands which are not generated by LIRGenerator::do_LogicOp
__ load_const_optimized(R0, uimm);
__ xorr(d, l, R0);
}
break;
default: ShouldNotReachHere();

View File

@ -578,18 +578,13 @@ inline bool can_handle_logic_op_as_uimm(ValueType *type, Bytecodes::Code bc) {
Assembler::is_uimm((jlong)((julong)int_or_long_const >> 16), 16)) return true;
// see Assembler::andi
if (bc == Bytecodes::_iand &&
(is_power_of_2(int_or_long_const+1) ||
is_power_of_2(int_or_long_const) ||
is_power_of_2(-int_or_long_const))) return true;
if (bc == Bytecodes::_land &&
(is_power_of_2((unsigned long)int_or_long_const+1) ||
(Assembler::is_uimm(int_or_long_const, 32) && is_power_of_2(int_or_long_const)) ||
(int_or_long_const != min_jlong && is_power_of_2(-int_or_long_const)))) return true;
if ((bc == Bytecodes::_iand || bc == Bytecodes::_land))
return Assembler::andi_supports(int_or_long_const);
// special case: xor -1
if ((bc == Bytecodes::_ixor || bc == Bytecodes::_lxor) &&
int_or_long_const == -1) return true;
if ((bc == Bytecodes::_ixor || bc == Bytecodes::_lxor))
return (int_or_long_const == -1);
return false;
}

View File

@ -9155,61 +9155,14 @@ instruct andI_reg_reg(iRegIdst dst, iRegIsrc src1, iRegIsrc src2) %{
ins_pipe(pipe_class_default);
%}
// Left shifted Immediate And
instruct andI_reg_immIhi16(iRegIdst dst, iRegIsrc src1, immIhi16 src2, flagsRegCR0 cr0) %{
instruct andI_reg_immI(iRegIdst dst, iRegIsrc src1, immI src2, flagsRegCR0 cr0) %{
match(Set dst (AndI src1 src2));
predicate(Assembler::andi_supports((juint)(n->in(2)->get_int())));
effect(KILL cr0);
format %{ "ANDIS $dst, $src1, $src2.hi" %}
size(4);
ins_encode %{
__ andis_($dst$$Register, $src1$$Register, (int)((unsigned short)(($src2$$constant & 0xFFFF0000) >> 16)));
%}
ins_pipe(pipe_class_default);
%}
// Immediate And
instruct andI_reg_uimm16(iRegIdst dst, iRegIsrc src1, uimmI16 src2, flagsRegCR0 cr0) %{
match(Set dst (AndI src1 src2));
effect(KILL cr0);
format %{ "ANDI $dst, $src1, $src2" %}
size(4);
ins_encode %{
// FIXME: avoid andi_ ?
__ andi_($dst$$Register, $src1$$Register, $src2$$constant);
%}
ins_pipe(pipe_class_default);
%}
// Immediate And where the immediate is a negative power of 2.
instruct andI_reg_immInegpow2(iRegIdst dst, iRegIsrc src1, immInegpow2 src2) %{
match(Set dst (AndI src1 src2));
format %{ "ANDWI $dst, $src1, $src2" %}
size(4);
ins_encode %{
__ clrrdi($dst$$Register, $src1$$Register, log2i_exact(-(juint)$src2$$constant));
%}
ins_pipe(pipe_class_default);
%}
instruct andI_reg_immIpow2minus1(iRegIdst dst, iRegIsrc src1, immIpow2minus1 src2) %{
match(Set dst (AndI src1 src2));
format %{ "ANDWI $dst, $src1, $src2" %}
size(4);
ins_encode %{
__ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((juint)$src2$$constant + 1u));
%}
ins_pipe(pipe_class_default);
%}
instruct andI_reg_immIpowerOf2(iRegIdst dst, iRegIsrc src1, immIpowerOf2 src2) %{
match(Set dst (AndI src1 src2));
predicate(UseRotateAndMaskInstructionsPPC64);
format %{ "ANDWI $dst, $src1, $src2" %}
size(4);
ins_encode %{
int bitpos = 31 - log2i_exact((juint)$src2$$constant);
__ rlwinm($dst$$Register, $src1$$Register, 0, bitpos, bitpos);
__ andi($dst$$Register, $src1$$Register, (juint)$src2$$constant); // optimized version
%}
ins_pipe(pipe_class_default);
%}
@ -9227,50 +9180,27 @@ instruct andL_reg_reg(iRegLdst dst, iRegLsrc src1, iRegLsrc src2) %{
ins_pipe(pipe_class_default);
%}
// Immediate And long
instruct andL_reg_uimm16(iRegLdst dst, iRegLsrc src1, uimmL16 src2, flagsRegCR0 cr0) %{
instruct andL_reg_immL(iRegLdst dst, iRegLsrc src1, immL src2, flagsRegCR0 cr0) %{
match(Set dst (AndL src1 src2));
predicate(Assembler::andi_supports(n->in(2)->get_long()));
effect(KILL cr0);
format %{ "ANDI $dst, $src1, $src2 \t// long" %}
size(4);
ins_encode %{
// FIXME: avoid andi_ ?
__ andi_($dst$$Register, $src1$$Register, $src2$$constant);
%}
ins_pipe(pipe_class_default);
%}
// Immediate And Long where the immediate is a negative power of 2.
instruct andL_reg_immLnegpow2(iRegLdst dst, iRegLsrc src1, immLnegpow2 src2) %{
match(Set dst (AndL src1 src2));
format %{ "ANDDI $dst, $src1, $src2" %}
size(4);
ins_encode %{
__ clrrdi($dst$$Register, $src1$$Register, log2i_exact(-(julong)$src2$$constant));
%}
ins_pipe(pipe_class_default);
%}
instruct andL_reg_immLpow2minus1(iRegLdst dst, iRegLsrc src1, immLpow2minus1 src2) %{
match(Set dst (AndL src1 src2));
format %{ "ANDDI $dst, $src1, $src2" %}
size(4);
ins_encode %{
__ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((julong)$src2$$constant + 1ull));
__ andi($dst$$Register, $src1$$Register, $src2$$constant); // optimized version
%}
ins_pipe(pipe_class_default);
%}
// AndL + ConvL2I.
instruct convL2I_andL_reg_immLpow2minus1(iRegIdst dst, iRegLsrc src1, immLpow2minus1 src2) %{
instruct convL2I_andL_reg_immL(iRegIdst dst, iRegLsrc src1, immL src2, flagsRegCR0 cr0) %{
match(Set dst (ConvL2I (AndL src1 src2)));
ins_cost(DEFAULT_COST);
format %{ "ANDDI $dst, $src1, $src2 \t// long + l2i" %}
predicate(Assembler::andi_supports(n->in(1)->in(2)->get_long()));
effect(KILL cr0);
format %{ "ANDI $dst, $src1, $src2 \t// long + l2i" %}
size(4);
ins_encode %{
__ clrldi($dst$$Register, $src1$$Register, 64 - log2i_exact((julong)$src2$$constant + 1ull));
__ andi($dst$$Register, $src1$$Register, $src2$$constant); // optimized version
%}
ins_pipe(pipe_class_default);
%}