8373248: C2: CastPP should not change the type of the oop

Reviewed-by: bmaillard, dfenacci, rcastanedalo, mchevalier
This commit is contained in:
Quan Anh Mai 2026-04-15 14:27:25 +00:00
parent e82f871871
commit a06f3cd469
5 changed files with 58 additions and 12 deletions

View File

@ -413,6 +413,43 @@ Node* CastLLNode::Ideal(PhaseGVN* phase, bool can_reshape) {
return nullptr;
}
// CastPPNodes are removed before matching, while alias classes are needed in global code motion.
// As a result, it is not valid for a CastPPNode to change the oop such that the derived pointers
// lie in different alias classes with and without the node. For example, a CastPPNode c may not
// cast an Object to a Bottom[], because later removal of c would affect the alias class of c's
// array length field (c + arrayOopDesc::length_offset_in_bytes()).
//
// This function verifies that a CastPPNode on an oop does not violate the aforementioned property.
//
// TODO 8382147: Currently, this verification only applies during the construction of a CastPPNode,
// we may want to apply the same verification during IGVN transformations, as well as final graph
// reshaping.
void CastPPNode::verify_type(const Type* in_type, const Type* out_type) {
#ifdef ASSERT
out_type = out_type->join(in_type);
if (in_type->empty() || out_type->empty()) {
return;
}
if (in_type == TypePtr::NULL_PTR || out_type == TypePtr::NULL_PTR) {
return;
}
if (!in_type->isa_oopptr() && !out_type->isa_oopptr()) {
return;
}
assert(in_type->isa_oopptr() && out_type->isa_oopptr(), "must be both oops or both non-oops");
if (in_type->isa_aryptr() && out_type->isa_aryptr()) {
const Type* e1 = in_type->is_aryptr()->elem();
const Type* e2 = out_type->is_aryptr()->elem();
assert(e1->basic_type() == e2->basic_type(), "must both be arrays of the same primitive type or both be oops arrays");
return;
}
assert(in_type->isa_instptr() && out_type->isa_instptr(), "must be both array oops or both non-array oops");
assert(in_type->is_instptr()->instance_klass() == out_type->is_instptr()->instance_klass(), "must not cast to a different type");
#endif // ASSERT
}
//------------------------------Value------------------------------------------
// Take 'join' of input and cast-up type, unless working with an Interface
const Type* CheckCastPPNode::Value(PhaseGVN* phase) const {
@ -440,6 +477,11 @@ const Type* CheckCastPPNode::Value(PhaseGVN* phase) const {
return result;
}
Node* CheckCastPPNode::pin_node_under_control_impl() const {
assert(_dependency.is_floating(), "already pinned");
return new CheckCastPPNode(in(0), in(1), bottom_type(), _dependency.with_pinned_dependency(), _extra_types);
}
//=============================================================================
//------------------------------Value------------------------------------------
const Type* CastX2PNode::Value(PhaseGVN* phase) const {

View File

@ -303,14 +303,18 @@ public:
//------------------------------CastPPNode-------------------------------------
// cast pointer to pointer (different type)
class CastPPNode: public ConstraintCastNode {
public:
CastPPNode (Node* ctrl, Node* n, const Type* t, const DependencyType& dependency = DependencyType::FloatingNarrowing, const TypeTuple* types = nullptr)
class CastPPNode : public ConstraintCastNode {
public:
CastPPNode(Node* ctrl, Node* n, const Type* t, const DependencyType& dependency = DependencyType::FloatingNarrowing, const TypeTuple* types = nullptr)
: ConstraintCastNode(ctrl, n, t, dependency, types) {
init_class_id(Class_CastPP);
verify_type(n->bottom_type(), t);
}
virtual int Opcode() const;
virtual uint ideal_reg() const { return Op_RegP; }
private:
static void verify_type(const Type* in_type, const Type* out_type);
};
//------------------------------CheckCastPPNode--------------------------------
@ -329,6 +333,7 @@ class CheckCastPPNode: public ConstraintCastNode {
private:
virtual bool depends_only_on_test_impl() const { return !type()->isa_rawptr() && ConstraintCastNode::depends_only_on_test_impl(); }
virtual Node* pin_node_under_control_impl() const;
};

View File

@ -4311,7 +4311,7 @@ Node* LibraryCallKit::generate_array_guard_common(Node* kls, RegionNode* region,
if (obj != nullptr && is_array_ctrl != nullptr && is_array_ctrl != top()) {
// Keep track of the fact that 'obj' is an array to prevent
// array specific accesses from floating above the guard.
*obj = _gvn.transform(new CastPPNode(is_array_ctrl, *obj, TypeAryPtr::BOTTOM));
*obj = _gvn.transform(new CheckCastPPNode(is_array_ctrl, *obj, TypeAryPtr::BOTTOM));
}
return ctrl;
}

View File

@ -1186,6 +1186,7 @@ public:
return nullptr;
}
assert(!res->depends_only_on_test(), "the result must not depends_only_on_test");
assert(Opcode() == res->Opcode(), "pinning must result in the same kind of node %s - %s", Name(), res->Name());
return res;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2026, 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
@ -455,14 +455,12 @@ void PhaseVector::expand_vunbox_node(VectorUnboxNode* vec_unbox) {
gvn.record_for_igvn(local_mem);
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
C2OptAccess access(gvn, ctrl, local_mem, decorators, T_OBJECT, obj, addr);
const Type* type = TypeOopPtr::make_from_klass(field->type()->as_klass());
vec_field_ld = bs->load_at(access, type);
}
// For proper aliasing, attach concrete payload type.
ciKlass* payload_klass = ciTypeArrayKlass::make(bt);
const Type* payload_type = TypeAryPtr::make_from_klass(payload_klass)->cast_to_ptr_type(TypePtr::NotNull);
vec_field_ld = gvn.transform(new CastPPNode(nullptr, vec_field_ld, payload_type));
// For proper aliasing, attach concrete payload type.
ciKlass* payload_klass = ciTypeArrayKlass::make(bt);
const Type* payload_type = TypeAryPtr::make_from_klass(payload_klass)->cast_to_ptr_type(TypePtr::NotNull);
vec_field_ld = bs->load_at(access, payload_type);
}
Node* adr = kit.array_element_address(vec_field_ld, gvn.intcon(0), bt);
const TypePtr* adr_type = adr->bottom_type()->is_ptr();