8316918: Optimize conversions duplicated across phi nodes

Reviewed-by: thartmann, kvn
This commit is contained in:
Jasmine Karthikeyan 2023-10-16 12:52:01 +00:00 committed by Tobias Hartmann
parent 668d4b077f
commit 36993aea9a
8 changed files with 575 additions and 98 deletions
src/hotspot/share
test
hotspot/jtreg/compiler
micro/org/openjdk/bench/vm/compiler

@ -1883,6 +1883,17 @@ static Node* split_flow_path(PhaseGVN *phase, PhiNode *phi) {
return phi;
}
// Returns the BasicType of a given convert node and a type, with special handling to ensure that conversions to
// and from half float will return the SHORT basic type, as that wouldn't be returned typically from TypeInt.
static BasicType get_convert_type(Node* convert, const Type* type) {
int convert_op = convert->Opcode();
if (type->isa_int() && (convert_op == Op_ConvHF2F || convert_op == Op_ConvF2HF)) {
return T_SHORT;
}
return type->basic_type();
}
//=============================================================================
//------------------------------simple_data_loop_check-------------------------
// Try to determining if the phi node in a simple safe/unsafe data loop.
@ -2557,6 +2568,41 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) {
}
#endif
// Try to convert a Phi with two duplicated convert nodes into a phi of the pre-conversion type and the convert node
// proceeding the phi, to de-duplicate the convert node and compact the IR.
if (can_reshape && progress == nullptr) {
ConvertNode* convert = in(1)->isa_Convert();
if (convert != nullptr) {
int conv_op = convert->Opcode();
bool ok = true;
// Check the rest of the inputs
for (uint i = 2; i < req(); i++) {
// Make sure that all inputs are of the same type of convert node
if (in(i)->Opcode() != conv_op) {
ok = false;
break;
}
}
if (ok) {
// Find the local bottom type to set as the type of the phi
const Type* source_type = Type::get_const_basic_type(convert->in_type()->basic_type());
const Type* dest_type = convert->bottom_type();
PhiNode* newphi = new PhiNode(in(0), source_type, nullptr);
// Set inputs to the new phi be the inputs of the convert
for (uint i = 1; i < req(); i++) {
newphi->init_req(i, in(i)->in(1));
}
phase->is_IterGVN()->register_new_node_with_optimizer(newphi, this);
return ConvertNode::create_convert(get_convert_type(convert, source_type), get_convert_type(convert, dest_type), newphi);
}
}
}
// Phi (VB ... VB) => VB (Phi ...) (Phi ...)
if (EnableVectorReboxing && can_reshape && progress == nullptr && type()->isa_oopptr()) {
progress = merge_through_phi(this, phase->is_IterGVN());

@ -88,6 +88,54 @@ Node* Conv2BNode::Ideal(PhaseGVN* phase, bool can_reshape) {
return nullptr;
}
uint ConvertNode::ideal_reg() const {
return _type->ideal_reg();
}
Node* ConvertNode::create_convert(BasicType source, BasicType target, Node* input) {
if (source == T_INT) {
if (target == T_LONG) {
return new ConvI2LNode(input);
} else if (target == T_FLOAT) {
return new ConvI2FNode(input);
} else if (target == T_DOUBLE) {
return new ConvI2DNode(input);
}
} else if (source == T_LONG) {
if (target == T_INT) {
return new ConvL2INode(input);
} else if (target == T_FLOAT) {
return new ConvL2FNode(input);
} else if (target == T_DOUBLE) {
return new ConvL2DNode(input);
}
} else if (source == T_FLOAT) {
if (target == T_INT) {
return new ConvF2INode(input);
} else if (target == T_LONG) {
return new ConvF2LNode(input);
} else if (target == T_DOUBLE) {
return new ConvF2DNode(input);
} else if (target == T_SHORT) {
return new ConvF2HFNode(input);
}
} else if (source == T_DOUBLE) {
if (target == T_INT) {
return new ConvD2INode(input);
} else if (target == T_LONG) {
return new ConvD2LNode(input);
} else if (target == T_FLOAT) {
return new ConvD2FNode(input);
}
} else if (source == T_SHORT) {
if (target == T_FLOAT) {
return new ConvHF2FNode(input);
}
}
assert(false, "Couldn't create conversion for type %s to %s", type2name(source), type2name(target));
return nullptr;
}
// The conversions operations are all Alpha sorted. Please keep it that way!
//=============================================================================
@ -193,8 +241,9 @@ const Type* ConvF2DNode::Value(PhaseGVN* phase) const {
const Type* ConvF2HFNode::Value(PhaseGVN* phase) const {
const Type *t = phase->type( in(1) );
if (t == Type::TOP) return Type::TOP;
if (t == Type::FLOAT) return TypeInt::SHORT;
if (StubRoutines::f2hf_adr() == nullptr) return bottom_type();
if (t == Type::FLOAT || StubRoutines::f2hf_adr() == nullptr) {
return TypeInt::SHORT;
}
const TypeF *tf = t->is_float_constant();
return TypeInt::make( StubRoutines::f2hf(tf->getf()) );
@ -263,14 +312,15 @@ Node *ConvF2LNode::Ideal(PhaseGVN *phase, bool can_reshape) {
const Type* ConvHF2FNode::Value(PhaseGVN* phase) const {
const Type *t = phase->type( in(1) );
if (t == Type::TOP) return Type::TOP;
if (t == TypeInt::SHORT) return Type::FLOAT;
if (StubRoutines::hf2f_adr() == nullptr) return bottom_type();
if (t == TypeInt::SHORT || StubRoutines::hf2f_adr() == nullptr) {
return Type::FLOAT;
}
const TypeInt *ti = t->is_int();
if (ti->is_con()) {
return TypeF::make( StubRoutines::hf2f(ti->get_con()) );
}
return bottom_type();
return Type::FLOAT;
}
//=============================================================================
@ -280,7 +330,7 @@ const Type* ConvI2DNode::Value(PhaseGVN* phase) const {
if( t == Type::TOP ) return Type::TOP;
const TypeInt *ti = t->is_int();
if( ti->is_con() ) return TypeD::make( (double)ti->get_con() );
return bottom_type();
return Type::DOUBLE;
}
//=============================================================================
@ -290,7 +340,7 @@ const Type* ConvI2FNode::Value(PhaseGVN* phase) const {
if( t == Type::TOP ) return Type::TOP;
const TypeInt *ti = t->is_int();
if( ti->is_con() ) return TypeF::make( (float)ti->get_con() );
return bottom_type();
return Type::FLOAT;
}
//------------------------------Identity---------------------------------------
@ -710,7 +760,7 @@ const Type* ConvL2DNode::Value(PhaseGVN* phase) const {
if( t == Type::TOP ) return Type::TOP;
const TypeLong *tl = t->is_long();
if( tl->is_con() ) return TypeD::make( (double)tl->get_con() );
return bottom_type();
return Type::DOUBLE;
}
//=============================================================================
@ -720,7 +770,7 @@ const Type* ConvL2FNode::Value(PhaseGVN* phase) const {
if( t == Type::TOP ) return Type::TOP;
const TypeLong *tl = t->is_long();
if( tl->is_con() ) return TypeF::make( (float)tl->get_con() );
return bottom_type();
return Type::FLOAT;
}
//=============================================================================

@ -42,193 +42,190 @@ class Conv2BNode : public Node {
virtual uint ideal_reg() const { return Op_RegI; }
};
class ConvertNode : public TypeNode {
protected:
ConvertNode(const Type* t, Node* input) : TypeNode(t, 2) {
init_class_id(Class_Convert);
init_req(1, input);
}
public:
virtual const Type* in_type() const = 0;
virtual uint ideal_reg() const;
// Create a convert node for a given input and output type.
// Conversions to and from half float are specified via T_SHORT.
static Node* create_convert(BasicType source, BasicType target, Node* input);
};
// The conversions operations are all Alpha sorted. Please keep it that way!
//------------------------------ConvD2FNode------------------------------------
// Convert double to float
class ConvD2FNode : public Node {
class ConvD2FNode : public ConvertNode {
public:
ConvD2FNode( Node *in1 ) : Node(0,in1) {}
ConvD2FNode(Node* in1) : ConvertNode(Type::FLOAT,in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return Type::FLOAT; }
virtual const Type* in_type() const { return Type::DOUBLE; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node* Identity(PhaseGVN* phase);
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual uint ideal_reg() const { return Op_RegF; }
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
};
//------------------------------ConvD2INode------------------------------------
// Convert Double to Integer
class ConvD2INode : public Node {
class ConvD2INode : public ConvertNode {
public:
ConvD2INode( Node *in1 ) : Node(0,in1) {}
ConvD2INode(Node* in1) : ConvertNode(TypeInt::INT,in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return TypeInt::INT; }
virtual const Type* in_type() const { return Type::DOUBLE; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node* Identity(PhaseGVN* phase);
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual uint ideal_reg() const { return Op_RegI; }
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
};
//------------------------------ConvD2LNode------------------------------------
// Convert Double to Long
class ConvD2LNode : public Node {
class ConvD2LNode : public ConvertNode {
public:
ConvD2LNode( Node *dbl ) : Node(0,dbl) {}
ConvD2LNode(Node* in1) : ConvertNode(TypeLong::LONG, in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return TypeLong::LONG; }
virtual const Type* in_type() const { return Type::DOUBLE; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node* Identity(PhaseGVN* phase);
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual uint ideal_reg() const { return Op_RegL; }
};
class RoundDNode : public Node {
public:
RoundDNode( Node *dbl ) : Node(0,dbl) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return TypeLong::LONG; }
virtual uint ideal_reg() const { return Op_RegL; }
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
};
//------------------------------ConvF2DNode------------------------------------
// Convert Float to a Double.
class ConvF2DNode : public Node {
class ConvF2DNode : public ConvertNode {
public:
ConvF2DNode( Node *in1 ) : Node(0,in1) {}
ConvF2DNode(Node* in1) : ConvertNode(Type::DOUBLE,in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return Type::DOUBLE; }
virtual const Type* in_type() const { return Type::FLOAT; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual uint ideal_reg() const { return Op_RegD; }
};
//------------------------------ConvF2HFNode------------------------------------
// Convert Float to Halffloat
class ConvF2HFNode : public Node {
class ConvF2HFNode : public ConvertNode {
public:
ConvF2HFNode( Node *in1 ) : Node(0,in1) {}
ConvF2HFNode(Node* in1) : ConvertNode(TypeInt::SHORT, in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return TypeInt::SHORT; }
virtual const Type* in_type() const { return TypeInt::FLOAT; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual uint ideal_reg() const { return Op_RegI; }
};
//------------------------------ConvF2INode------------------------------------
// Convert float to integer
class ConvF2INode : public Node {
public:
ConvF2INode( Node *in1 ) : Node(0,in1) {}
class ConvF2INode : public ConvertNode {
public:
ConvF2INode(Node* in1) : ConvertNode(TypeInt::INT, in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return TypeInt::INT; }
virtual const Type* in_type() const { return TypeInt::FLOAT; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node* Identity(PhaseGVN* phase);
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual uint ideal_reg() const { return Op_RegI; }
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
};
//------------------------------ConvF2LNode------------------------------------
// Convert float to long
class ConvF2LNode : public Node {
public:
ConvF2LNode( Node *in1 ) : Node(0,in1) {}
class ConvF2LNode : public ConvertNode {
public:
ConvF2LNode(Node* in1) : ConvertNode(TypeLong::LONG, in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return TypeLong::LONG; }
virtual const Type* in_type() const { return TypeInt::FLOAT; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node* Identity(PhaseGVN* phase);
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual uint ideal_reg() const { return Op_RegL; }
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
};
//------------------------------ConvHF2FNode------------------------------------
// Convert Halffloat to float
class ConvHF2FNode : public Node {
public:
ConvHF2FNode( Node *in1 ) : Node(0,in1) {}
class ConvHF2FNode : public ConvertNode {
public:
ConvHF2FNode(Node* in1) : ConvertNode(Type::FLOAT, in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return Type::FLOAT; }
virtual const Type* in_type() const { return TypeInt::SHORT; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual uint ideal_reg() const { return Op_RegF; }
};
//------------------------------ConvI2DNode------------------------------------
// Convert Integer to Double
class ConvI2DNode : public Node {
public:
ConvI2DNode( Node *in1 ) : Node(0,in1) {}
class ConvI2DNode : public ConvertNode {
public:
ConvI2DNode(Node* in1) : ConvertNode(Type::DOUBLE, in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return Type::DOUBLE; }
virtual const Type* in_type() const { return TypeInt::INT; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual uint ideal_reg() const { return Op_RegD; }
};
//------------------------------ConvI2FNode------------------------------------
// Convert Integer to Float
class ConvI2FNode : public Node {
public:
ConvI2FNode( Node *in1 ) : Node(0,in1) {}
class ConvI2FNode : public ConvertNode {
public:
ConvI2FNode(Node* in1) : ConvertNode(Type::FLOAT, in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return Type::FLOAT; }
virtual const Type* in_type() const { return TypeInt::INT; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node* Identity(PhaseGVN* phase);
virtual uint ideal_reg() const { return Op_RegF; }
};
class RoundFNode : public Node {
public:
RoundFNode( Node *in1 ) : Node(0,in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return TypeInt::INT; }
virtual uint ideal_reg() const { return Op_RegI; }
};
//------------------------------ConvI2LNode------------------------------------
// Convert integer to long
class ConvI2LNode : public TypeNode {
class ConvI2LNode : public ConvertNode {
public:
ConvI2LNode(Node *in1, const TypeLong* t = TypeLong::INT)
: TypeNode(t, 2)
{ init_req(1, in1); }
ConvI2LNode(Node* in1, const TypeLong* t = TypeLong::INT) : ConvertNode(t, in1) {}
virtual int Opcode() const;
virtual const Type* in_type() const { return TypeInt::INT; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
virtual Node* Identity(PhaseGVN* phase);
virtual uint ideal_reg() const { return Op_RegL; }
};
//------------------------------ConvL2DNode------------------------------------
// Convert Long to Double
class ConvL2DNode : public Node {
public:
ConvL2DNode( Node *in1 ) : Node(0,in1) {}
class ConvL2DNode : public ConvertNode {
public:
ConvL2DNode(Node* in1) : ConvertNode(Type::DOUBLE, in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return Type::DOUBLE; }
virtual const Type* in_type() const { return TypeLong::LONG; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual uint ideal_reg() const { return Op_RegD; }
};
//------------------------------ConvL2FNode------------------------------------
// Convert Long to Float
class ConvL2FNode : public Node {
public:
ConvL2FNode( Node *in1 ) : Node(0,in1) {}
class ConvL2FNode : public ConvertNode {
public:
ConvL2FNode(Node* in1) : ConvertNode(Type::FLOAT, in1) {}
virtual int Opcode() const;
virtual const Type *bottom_type() const { return Type::FLOAT; }
virtual const Type* in_type() const { return TypeLong::LONG; }
virtual const Type* Value(PhaseGVN* phase) const;
virtual uint ideal_reg() const { return Op_RegF; }
};
//------------------------------ConvL2INode------------------------------------
// Convert long to integer
class ConvL2INode : public TypeNode {
class ConvL2INode : public ConvertNode {
public:
ConvL2INode(Node *in1, const TypeInt* t = TypeInt::INT)
: TypeNode(t, 2) {
init_req(1, in1);
}
ConvL2INode(Node* in1, const TypeInt* t = TypeInt::INT) : ConvertNode(t, in1) {}
virtual int Opcode() const;
virtual const Type* in_type() const { return TypeLong::LONG; }
virtual Node* Identity(PhaseGVN* phase);
virtual const Type* Value(PhaseGVN* phase) const;
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
};
class RoundDNode : public Node {
public:
RoundDNode(Node* in1) : Node(nullptr, in1) {}
virtual int Opcode() const;
virtual const Type* bottom_type() const { return TypeLong::LONG; }
virtual uint ideal_reg() const { return Op_RegL; }
};
class RoundFNode : public Node {
public:
RoundFNode(Node* in1) : Node(nullptr, in1) {}
virtual int Opcode() const;
virtual const Type* bottom_type() const { return TypeInt::INT; }
virtual uint ideal_reg() const { return Op_RegI; }
};

@ -70,6 +70,7 @@ class CodeBuffer;
class ConstraintCastNode;
class ConNode;
class ConINode;
class ConvertNode;
class CompareAndSwapNode;
class CompareAndExchangeNode;
class CountedLoopNode;
@ -731,6 +732,7 @@ public:
DEFINE_CLASS_ID(Con, Type, 8)
DEFINE_CLASS_ID(ConI, Con, 0)
DEFINE_CLASS_ID(SafePointScalarMerge, Type, 9)
DEFINE_CLASS_ID(Convert, Type, 10)
DEFINE_CLASS_ID(Proj, Node, 3)
@ -889,6 +891,7 @@ public:
DEFINE_CLASS_QUERY(ClearArray)
DEFINE_CLASS_QUERY(CMove)
DEFINE_CLASS_QUERY(Cmp)
DEFINE_CLASS_QUERY(Convert)
DEFINE_CLASS_QUERY(CountedLoop)
DEFINE_CLASS_QUERY(CountedLoopEnd)
DEFINE_CLASS_QUERY(DecodeNarrowPtr)

@ -1493,6 +1493,7 @@
declare_c2_type(CastPPNode, ConstraintCastNode) \
declare_c2_type(CheckCastPPNode, TypeNode) \
declare_c2_type(Conv2BNode, Node) \
declare_c2_type(ConvertNode, TypeNode) \
declare_c2_type(ConvD2FNode, Node) \
declare_c2_type(ConvD2INode, Node) \
declare_c2_type(ConvD2LNode, Node) \

@ -0,0 +1,155 @@
/*
* 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 patterns involving duplicated conversion nodes behind phi are properly optimized.
* @bug 8316918
* @library /test/lib /
* @requires vm.compiler2.enabled
* @run driver compiler.c2.irTests.TestPhiDuplicatedConversion
*/
public class TestPhiDuplicatedConversion {
public static void main(String[] args) {
TestFramework.run();
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static float int2Float(boolean c, int a, int b) {
return c ? a : b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static double int2Double(boolean c, int a, int b) {
return c ? a : b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static long int2Long(boolean c, int a, int b) {
return c ? a : b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static int float2Int(boolean c, float a, float b) {
return c ? (int)a : (int)b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static double float2Double(boolean c, float a, float b) {
return c ? (double)a : (double)b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static long float2Long(boolean c, float a, float b) {
return c ? (long)a : (long)b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static int double2Int(boolean c, double a, double b) {
return c ? (int)a : (int)b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static float double2Float(boolean c, double a, double b) {
return c ? (float)a : (float)b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static long double2Long(boolean c, double a, double b) {
return c ? (long)a : (long)b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static float long2Float(boolean c, long a, long b) {
return c ? a : b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static double long2Double(boolean c, long a, long b) {
return c ? a : b;
}
@Test
@IR(counts = {IRNode.CONV, "1"})
public static int long2Int(boolean c, long a, long b) {
return c ? (int)a : (int)b;
}
@Test
@IR(counts = {IRNode.CONV, "1"}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"})
public static short float2HalfFloat(boolean c, float a, float b) {
return c ? Float.floatToFloat16(a) : Float.floatToFloat16(b);
}
@Test
@IR(counts = {IRNode.CONV, "1"}, applyIfCPUFeatureOr = {"avx", "true", "asimd", "true"})
public static float halfFloat2Float(boolean c, short a, short b) {
return c ? Float.float16ToFloat(a) : Float.float16ToFloat(b);
}
@Run(test = {"int2Float", "int2Double", "int2Long",
"float2Int", "float2Double", "float2Long",
"double2Int", "double2Float", "double2Long",
"long2Float", "long2Double", "long2Int",
"float2HalfFloat", "halfFloat2Float"})
public void runTests() {
assertResults(true, 10, 20, 3.14f, -1.6f, 3.1415, -1.618, 30L, 400L, Float.floatToFloat16(10.5f), Float.floatToFloat16(20.5f));
assertResults(false, 10, 20, 3.14f, -1.6f, 3.1415, -1.618, 30L, 400L, Float.floatToFloat16(10.5f), Float.floatToFloat16(20.5f));
}
@DontCompile
public void assertResults(boolean c, int intA, int intB, float floatA, float floatB, double doubleA, double doubleB, long longA, long longB, short halfFloatA, short halfFloatB) {
Asserts.assertEQ(c ? (float)intA : (float)intB, int2Float(c, intA, intB));
Asserts.assertEQ(c ? (double)intA : (double)intB, int2Double(c, intA, intB));
Asserts.assertEQ(c ? (long)intA : (long)intB, int2Long(c, intA, intB));
Asserts.assertEQ(c ? (int)floatA : (int)floatB, float2Int(c, floatA, floatB));
Asserts.assertEQ(c ? (double)floatA : (double)floatB, float2Double(c, floatA, floatB));
Asserts.assertEQ(c ? (long)floatA : (long)floatB, float2Long(c, floatA, floatB));
Asserts.assertEQ(c ? (int)doubleA : (int)doubleB, double2Int(c, doubleA, doubleB));
Asserts.assertEQ(c ? (float)doubleA : (float)doubleB, double2Float(c, doubleA, doubleB));
Asserts.assertEQ(c ? (long)doubleA : (long)doubleB, double2Long(c, doubleA, doubleB));
Asserts.assertEQ(c ? (float)longA : (float)longB, long2Float(c, longA, longB));
Asserts.assertEQ(c ? (double)longA : (double)longB, long2Double(c, longA, longB));
Asserts.assertEQ(c ? (int)longA : (int)longB, long2Int(c, longA, longB));
Asserts.assertEQ(c ? Float.floatToFloat16(floatA) : Float.floatToFloat16(floatB), float2HalfFloat(c, floatA, floatB));
Asserts.assertEQ(c ? Float.float16ToFloat(halfFloatA) : Float.float16ToFloat(halfFloatB), halfFloat2Float(c, halfFloatA, halfFloatB));
}
}

@ -442,6 +442,11 @@ public class IRNode {
beforeMatchingNameRegex(COMPRESS_BITS, "CompressBits");
}
public static final String CONV = PREFIX + "CONV" + POSTFIX;
static {
beforeMatchingNameRegex(CONV, "Conv");
}
public static final String CONV_I2L = PREFIX + "CONV_I2L" + POSTFIX;
static {
beforeMatchingNameRegex(CONV_I2L, "ConvI2L");

@ -0,0 +1,220 @@
/*
* 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;
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)
@Measurement(iterations = 4, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Warmup(iterations = 3, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
public class PhiDuplicatedConversion {
public static final int SIZE = 300;
// Ints
@Benchmark
public void testInt2Float(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(i2f(state.bools[i], state.ints[i], state.ints[SIZE - 1 - i]));
}
}
@Benchmark
public void testInt2Double(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(i2d(state.bools[i], state.ints[i], state.ints[SIZE - 1 - i]));
}
}
@Benchmark
public void testInt2Long(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(i2l(state.bools[i], state.ints[i], state.ints[SIZE - 1 - i]));
}
}
// Floats
@Benchmark
public void testFloat2Int(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(f2i(state.bools[i], state.floats[i], state.floats[SIZE - 1 - i]));
}
}
@Benchmark
public void testFloat2Double(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(f2d(state.bools[i], state.floats[i], state.floats[SIZE - 1 - i]));
}
}
@Benchmark
public void testFloat2Long(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(f2l(state.bools[i], state.floats[i], state.floats[SIZE - 1 - i]));
}
}
// Doubles
@Benchmark
public void testDouble2Int(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(d2i(state.bools[i], state.doubles[i], state.doubles[SIZE - 1 - i]));
}
}
@Benchmark
public void testDouble2Float(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(d2f(state.bools[i], state.doubles[i], state.doubles[SIZE - 1 - i]));
}
}
@Benchmark
public void testDouble2Long(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(d2l(state.bools[i], state.doubles[i], state.doubles[SIZE - 1 - i]));
}
}
// Longs
@Benchmark
public void testLong2Float(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(l2f(state.bools[i], state.longs[i], state.longs[SIZE - 1 - i]));
}
}
@Benchmark
public void testLong2Double(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(l2d(state.bools[i], state.longs[i], state.longs[SIZE - 1 - i]));
}
}
@Benchmark
public void testLong2Int(Blackhole blackhole, BenchState state) {
for (int i = 0; i < SIZE; i++) {
blackhole.consume(l2i(state.bools[i], state.longs[i], state.longs[SIZE - 1 - i]));
}
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static float i2f(boolean c, int a, int b) {
return c ? a : b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static double i2d(boolean c, int a, int b) {
return c ? a : b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static long i2l(boolean c, int a, int b) {
return c ? a : b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static int f2i(boolean c, float a, float b) {
return c ? (int)a : (int)b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static double f2d(boolean c, float a, float b) {
return c ? (double)a : (double)b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static long f2l(boolean c, float a, float b) {
return c ? (long)a : (long)b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static int d2i(boolean c, double a, double b) {
return c ? (int)a : (int)b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static float d2f(boolean c, double a, double b) {
return c ? (float)a : (float)b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static long d2l(boolean c, double a, double b) {
return c ? (long)a : (long)b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static float l2f(boolean c, long a, long b) {
return c ? a : b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static double l2d(boolean c, long a, long b) {
return c ? a : b;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public static int l2i(boolean c, long a, long b) {
return c ? (int)a : (int)b;
}
@State(Scope.Benchmark)
public static class BenchState {
public boolean[] bools;
public int[] ints;
public long[] longs;
public float[] floats;
public double[] doubles;
public BenchState() {
}
@Setup(Level.Iteration)
public void setup() {
Random random = new Random(1000);
bools = new boolean[SIZE];
ints = new int[SIZE];
longs = new long[SIZE];
floats = new float[SIZE];
doubles = new double[SIZE];
for (int i = 0; i < SIZE; i++) {
bools[i] = random.nextBoolean();
ints[i] = random.nextInt(100);
longs[i] = random.nextLong(100);
floats[i] = random.nextFloat(100);
doubles[i] = random.nextDouble(100);
}
}
}
}