8316918: Optimize conversions duplicated across phi nodes
Reviewed-by: thartmann, kvn
This commit is contained in:
parent
668d4b077f
commit
36993aea9a
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user