1 /*
  2  * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #ifndef SHARE_OOPS_LAYOUTKIND_HPP
 26 #define SHARE_OOPS_LAYOUTKIND_HPP
 27 
 28 #include "memory/allStatic.hpp"
 29 #include "utilities/globalDefinitions.hpp"
 30 #include "utilities/ostream.hpp"
 31 
 32 // LayoutKind is an enum used to indicate which layout has been used for a given value field.
 33 // Each layout has its own properties and its own access protocol that is detailed below.
 34 //
 35 // REFERENCE : This layout uses a pointer to a heap allocated instance (no flattening).
 36 //             When used, field_flags().is_flat() is false . The field can be nullable or
 37 //             null-restricted, in the later case, field_flags().is_null_free_inline_type() is true.
 38 //             In case of a null-restricted field, putfield  and putstatic  must perform a null-check
 39 //             before writing a new value. Still for null-restricted fields, if getfield reads a null pointer
 40 //             from the receiver, it means that the field was not initialized yet, and getfield must substitute
 41 //             the null reference with the default value of the field's class.
 42 // NULL_FREE_NON_ATOMIC_FLAT : This layout is the simplest form of flattening. Any field embedded inside the flat field
 43 //             can be accessed independently. The field is null-restricted, meaning putfield must perform a
 44 //             null-check before performing a field update.
 45 // NULL_FREE_ATOMIC_FLAT : This flat layout is designed for atomic updates, with size and alignment that make use of
 46 //             atomic instructions possible. All accesses, reads and writes, must be performed atomically.
 47 //             The field is null-restricted, meaning putfield must perform a null-check before performing a
 48 //             field update.
 49 // NULLABLE_ATOMIC_FLAT : This is the flat layout designed for JEP 401. It is designed for atomic updates,
 50 //             with size and alignment that make use of atomic instructions possible. All accesses, reads and
 51 //             writes, must be performed atomically. The layout includes a null marker which indicates if the
 52 //             field's value must be considered as null or not. The null marker is a byte, with the value zero
 53 //             meaning the field's value is null, and a non-zero value meaning the field's value is not null.
 54 //             A getfield must check the value of the null marker before returning a value. If the null marker
 55 //             is zero, getfield  must return the null reference, otherwise it returns the field's value read
 56 //             from the receiver. When a putfield writes a non-null value to such field, the update, including
 57 //             the field's value and the null marker, must be performed in a single atomic operation. If the
 58 //             source of the value is a heap allocated instance of the field's class, it is allowed to set the
 59 //             null marker to non-zero in the heap allocated instance before copying the value to the receiver
 60 //             (the BUFFERED layout used in heap allocated values guarantees that the space for the null marker
 61 //             is included, but has no meaning for the heap allocated instance which is always non-null, and that
 62 //             the whole payload is correctly aligned for atomic operations). When a putfield writes null to such
 63 //             field, the null marker must be set to zero. However, if the field contains oops, those oops must be
 64 //             cleared too in order to prevent memory leaks. In order to simplify such operation, value classes
 65 //             supporting a NULLABLE_ATOMIC_FLAT layout have a pre-allocated reset value instance, filled with
 66 //             zeros, which can be used to simply overwrite the whole flat field and reset everything (oops and
 67 //             null marker). The reset value instance is needed because the VM needs an instance guaranteed to
 68 //             always be filled with zeros, and the default value could have its null marker set to non-zero if
 69 //             it is used as a source to update a NULLABLE_ATOMIC_FLAT field.
 70 // NULLABLE_NON_ATOMIC_FLAT: This is a special layout, only used for strict final non-static fields. Because strict
 71 //             final non-static fields cannot be updated after the call to the super constructor, there's no
 72 //             concurrency issue on those fields, so they can be flattened even if they are nullable. During the
 73 //             construction of the instance, the uninitializedThis reference cannot escape before the call to
 74 //             the super's constructor, so no concurrent reads are possible when the field is initialized. After
 75 //             the call to the super's constructor, no update is possible because the field is strict and final,
 76 //             so no write possible during a read. This field has a null marker similar to the one of the
 77 //             NULLABLE_ATOMIC_FLAT layout. However, there's no requirement to read the null marker and the
 78 //             rest of the value atomically. If the null marker indicates a non-null value, the fields of the
 79 //             field's value can be read independently. Same rules for a putfield, no atomicity requirement,
 80 //             as long as all fields and the null marker are up to date at the end of the putfield.
 81 // BUFFERED:   This layout is only used in heap buffered instances of a value class. It is computed to be compatible
 82 //             in size and alignment with all other flat layouts supported by the value class.
 83 //
 84 //
 85 // IMPORTANT: The REFERENCE layout must always be associated with the numerical value zero, because the implementation
 86 // of the lava.lang.invoke.MemberName class relies on this property.
 87 
 88 enum class LayoutKind : uint32_t {
 89   REFERENCE                 = 0,    // indirection to a heap allocated instance
 90   BUFFERED                  = 1,    // layout used in heap allocated standalone instances
 91   NULL_FREE_NON_ATOMIC_FLAT = 2,    // flat, null-free (no null marker), no guarantee of atomic updates
 92   NULL_FREE_ATOMIC_FLAT     = 3,    // flat, null-free, size compatible with atomic updates, alignment requirement is equal to the size
 93   NULLABLE_ATOMIC_FLAT      = 4,    // flat, include a null marker, plus same size/alignment properties as ATOMIC layout
 94   NULLABLE_NON_ATOMIC_FLAT  = 5,    // flat, include a null marker, non-atomic, only used for strict final non-static fields
 95   UNKNOWN                   = 6     // used for uninitialized fields of type LayoutKind
 96 };
 97 
 98 class outputStream;
 99 
100 class LayoutKindHelper : AllStatic {
101  public:
102   static LayoutKind get_copy_layout(LayoutKind src, LayoutKind dst) {
103     assert(src == dst || src == LayoutKind::BUFFERED || dst == LayoutKind::BUFFERED,
104            "Only same or from/to BUFFERED is supported. src: %s, dst: %s",
105            layout_kind_as_string(src), layout_kind_as_string(dst));
106     return src == LayoutKind::BUFFERED ? dst : src;
107   }
108 
109   static bool is_flat(LayoutKind lk) {
110     assert(lk != LayoutKind::UNKNOWN, "Sanity check");
111     return lk == LayoutKind::NULL_FREE_NON_ATOMIC_FLAT ||
112            lk == LayoutKind::NULL_FREE_ATOMIC_FLAT ||
113            lk == LayoutKind::NULLABLE_ATOMIC_FLAT ||
114            lk == LayoutKind::NULLABLE_NON_ATOMIC_FLAT;
115   }
116   static bool is_atomic_flat(LayoutKind lk) {
117     return lk == LayoutKind::NULL_FREE_ATOMIC_FLAT ||
118            lk == LayoutKind::NULLABLE_ATOMIC_FLAT;
119   }
120   static bool is_nullable_flat(LayoutKind lk) {
121     return lk == LayoutKind::NULLABLE_ATOMIC_FLAT ||
122            lk == LayoutKind::NULLABLE_NON_ATOMIC_FLAT;
123   }
124   static bool is_null_free_flat(LayoutKind lk) {
125     return lk == LayoutKind::NULL_FREE_ATOMIC_FLAT ||
126            lk == LayoutKind::NULL_FREE_NON_ATOMIC_FLAT;
127   }
128   static const char* layout_kind_as_string(LayoutKind lk);
129 
130   static void print_on(LayoutKind lk, outputStream* st) NOT_DEBUG_RETURN;
131 };
132 
133 #endif // SHARE_OOPS_LAYOUTKIND_HPP