1 /*
2  * Copyright (c) 1997, 2020, 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_UTILITIES_ALIGN_HPP
26 #define SHARE_UTILITIES_ALIGN_HPP
27 
28 #include "metaprogramming/enableIf.hpp"
29 #include "utilities/debug.hpp"
30 #include "utilities/globalDefinitions.hpp"
31 #include "utilities/powerOfTwo.hpp"
32 #include <type_traits>
33 
34 // Compute mask to use for aligning to or testing alignment.
35 // The alignment must be a power of 2. Returns alignment - 1, which is
36 // a mask with all bits set below alignment's single bit.
37 template<typename T, ENABLE_IF(std::is_integral<T>::value)>
alignment_mask(T alignment)38 static constexpr T alignment_mask(T alignment) {
39   assert(is_power_of_2(alignment),
40          "must be a power of 2: " UINT64_FORMAT, (uint64_t)alignment);
41   return alignment - 1;
42 }
43 
44 // Some "integral" constant alignments are defined via enum.
45 template<typename T, ENABLE_IF(std::is_enum<T>::value)>
alignment_mask(T alignment)46 static constexpr auto alignment_mask(T alignment) {
47   return alignment_mask(static_cast<std::underlying_type_t<T>>(alignment));
48 }
49 
50 // Align integers and check for alignment.
51 // The is_integral filtering here is not for disambiguation with the T*
52 // overloads; if those match then they are a better match.  Rather, the
53 // is_integral filtering is to prevent back-sliding on the use of enums
54 // as "integral" constants that need aligning.
55 
56 template<typename T, typename A, ENABLE_IF(std::is_integral<T>::value)>
is_aligned(T size,A alignment)57 constexpr bool is_aligned(T size, A alignment) {
58   return (size & alignment_mask(alignment)) == 0;
59 }
60 
61 template<typename T, typename A, ENABLE_IF(std::is_integral<T>::value)>
align_down(T size,A alignment)62 constexpr T align_down(T size, A alignment) {
63   // Convert mask to T before logical_not.  Otherwise, if alignment is unsigned
64   // and smaller than T, the result of the logical_not will be zero-extended
65   // by integral promotion, and upper bits of size will be discarded.
66   T result = size & ~T(alignment_mask(alignment));
67   assert(is_aligned(result, alignment),
68          "must be aligned: " UINT64_FORMAT, (uint64_t)result);
69   return result;
70 }
71 
72 template<typename T, typename A, ENABLE_IF(std::is_integral<T>::value)>
align_up(T size,A alignment)73 constexpr T align_up(T size, A alignment) {
74   T adjusted = size + alignment_mask(alignment);
75   return align_down(adjusted, alignment);
76 }
77 
78 // Align down with a lower bound. If the aligning results in 0, return 'alignment'.
79 template <typename T, typename A>
align_down_bounded(T size,A alignment)80 constexpr T align_down_bounded(T size, A alignment) {
81   T aligned_size = align_down(size, alignment);
82   return (aligned_size > 0) ? aligned_size : T(alignment);
83 }
84 
85 // Align pointers and check for alignment.
86 
87 template <typename T, typename A>
align_up(T * ptr,A alignment)88 inline T* align_up(T* ptr, A alignment) {
89   return (T*)align_up((uintptr_t)ptr, alignment);
90 }
91 
92 template <typename T, typename A>
align_down(T * ptr,A alignment)93 inline T* align_down(T* ptr, A alignment) {
94   return (T*)align_down((uintptr_t)ptr, alignment);
95 }
96 
97 template <typename T, typename A>
is_aligned(T * ptr,A alignment)98 inline bool is_aligned(T* ptr, A alignment) {
99   return is_aligned((uintptr_t)ptr, alignment);
100 }
101 
102 // Align metaspace objects by rounding up to natural word boundary
103 template <typename T>
align_metadata_size(T size)104 inline T align_metadata_size(T size) {
105   return align_up(size, 1);
106 }
107 
108 // Align objects in the Java Heap by rounding up their size, in HeapWord units.
109 template <typename T>
align_object_size(T word_size)110 inline T align_object_size(T word_size) {
111   return align_up(word_size, MinObjAlignment);
112 }
113 
is_object_aligned(size_t word_size)114 inline bool is_object_aligned(size_t word_size) {
115   return is_aligned(word_size, MinObjAlignment);
116 }
117 
is_object_aligned(const void * addr)118 inline bool is_object_aligned(const void* addr) {
119   return is_aligned(addr, MinObjAlignmentInBytes);
120 }
121 
122 // Pad out certain offsets to jlong alignment, in HeapWord units.
123 template <typename T>
align_object_offset(T offset)124 constexpr T align_object_offset(T offset) {
125   return align_up(offset, HeapWordsPerLong);
126 }
127 
128 // Clamp an address to be within a specific page
129 // 1. If addr is on the page it is returned as is
130 // 2. If addr is above the page_address the start of the *next* page will be returned
131 // 3. Otherwise, if addr is below the page_address the start of the page will be returned
132 template <typename T>
clamp_address_in_page(T * addr,T * page_address,size_t page_size)133 inline T* clamp_address_in_page(T* addr, T* page_address, size_t page_size) {
134   if (align_down(addr, page_size) == align_down(page_address, page_size)) {
135     // address is in the specified page, just return it as is
136     return addr;
137   } else if (addr > page_address) {
138     // address is above specified page, return start of next page
139     return align_down(page_address, page_size) + page_size;
140   } else {
141     // address is below specified page, return start of page
142     return align_down(page_address, page_size);
143   }
144 }
145 
146 #endif // SHARE_UTILITIES_ALIGN_HPP
147