1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef PANDA_LIBPANDABASE_MEM_GC_BARRIER_H
17 #define PANDA_LIBPANDABASE_MEM_GC_BARRIER_H
18
19 #include "utils/bit_field.h"
20
21 #include <atomic>
22 #include <cstdint>
23 #include <functional>
24 #include <variant>
25
26 namespace ark::mem {
27
28 /// Represents Pre and Post barrier
29 enum BarrierPosition : uint8_t {
30 BARRIER_POSITION_PRE = 0x1, // Should be inserted before each store/load when reference stored/loaded
31 BARRIER_POSITION_POST = 0x0, // Should be inserted after each store/load when reference stored/loaded
32 };
33
34 /// Indicates if barrier for store or load
35 enum BarrierActionType : uint8_t {
36 WRITE_BARRIER = 0x1, // Should be used around store
37 READ_BARRIER = 0x0, // Should be used around load
38 };
39
40 namespace internal {
41 constexpr uint8_t BARRIER_POS_OFFSET = 0U; // offset in bits for encoding position of barrier(pre or post)
42 constexpr uint8_t BARRIER_WRB_FLAG_OFFSET = 1U; // offset in bits for WRB flag
43 } // namespace internal
44
EncodeBarrierType(uint8_t value,BarrierPosition position,BarrierActionType actionType)45 constexpr uint8_t EncodeBarrierType(uint8_t value, BarrierPosition position, BarrierActionType actionType)
46 {
47 // NOLINTNEXTLINE(hicpp-signed-bitwise)
48 return (value << 2U) | (position << internal::BARRIER_POS_OFFSET) |
49 (actionType << internal::BARRIER_WRB_FLAG_OFFSET);
50 }
51
52 /**
53 * Should help to encode barrier for the compiler.
54 * PreWrite barrier can be used for avoiding lost object problem.
55 * PostWrite barrier used for tracking intergenerational or interregion references
56 */
57 enum BarrierType : uint8_t {
58 PRE_WRB_NONE = EncodeBarrierType(1U, BarrierPosition::BARRIER_POSITION_PRE, BarrierActionType::WRITE_BARRIER),
59 PRE_RB_NONE = EncodeBarrierType(1U, BarrierPosition::BARRIER_POSITION_PRE, BarrierActionType::READ_BARRIER),
60 POST_WRB_NONE = EncodeBarrierType(1U, BarrierPosition::BARRIER_POSITION_POST, BarrierActionType::WRITE_BARRIER),
61 POST_RB_NONE = EncodeBarrierType(1U, BarrierPosition::BARRIER_POSITION_POST, BarrierActionType::READ_BARRIER),
62 /**
63 * Pre barrier for SATB.
64 * Pseudocode:
65 * load PRE_WRITE_BARRIER_ADDR -> concurrent_marking
66 * if (UNLIKELY(concurrent_marking)) {
67 * load obj.field -> pre_val // note: if store volatile - we need to have volatile load here
68 * if (pre_val != nullptr) {
69 * call STORE_IN_BUFF_TO_MARK_FUNC(pre_val);
70 * }
71 * }
72 * store obj.field <- new_val // STORE for which barrier generated
73 *
74 * Runtime should provide these parameters:
75 * PRE_WRITE_BARRIER_ADDR - address of pre WRB function (!= nullptr) ---> indicates that we have concurrent
76 * marking on
77 * STORE_IN_BUFF_TO_MARK_FUNC - address of function to store replaced reference
78 */
79 PRE_SATB_BARRIER = EncodeBarrierType(2U, BarrierPosition::BARRIER_POSITION_PRE, BarrierActionType::WRITE_BARRIER),
80 /**
81 * Post barrier. Intergenerational barrier for GCs with explicit continuous young gen space. Unconditional.
82 * Can be fully encoded by compiler
83 * Pseudocode:
84 * store obj.field <- new_val // STORE for which barrier generated
85 * load AddressOf(MIN_ADDR) -> min_addr
86 * load AddressOf(CARD_TABLE_ADDR) -> card_table_addr
87 * card_index = (AddressOf(obj) - min_addr) >> CARD_BITS // shift right
88 * card_addr = card_table_addr + card_index
89 * store card_addr <- DIRTY_VAL
90 *
91 * Runtime should provide these parameters:
92 * MIN_ADDR - minimal address used by runtime (it is required only to support 64-bit addresses)
93 * CARD_TABLE_ADDR - address of the start of card table raw data array
94 * CARD_BITS - how many bits covered by one card (probably it will be a literal)
95 * DIRTY_VAL - some literal representing dirty card
96 *
97 * Note if store if to expensive on the architecture(for example in multithreading environment) -
98 * consider to create conditional barrier, ie check that card is not dirty before store
99 */
100 POST_INTERGENERATIONAL_BARRIER =
101 EncodeBarrierType(3U, BarrierPosition::BARRIER_POSITION_POST, BarrierActionType::WRITE_BARRIER),
102 /**
103 * Inter-region barrier. For GCs without explicit continuous young gen space.
104 * Pseudocode:
105 * store obj.field <- new_val // STORE for which barrier generated
106 * // Check if new_val is nullptr first - then we don't need a barrier
107 * if (new_val == null) {
108 * return
109 * }
110 * // Check if new_val and address of field is in different regions
111 * // (each region contain 2^REGION_SIZE_BITS and aligned with 2^REGION_SIZE_BITS bytes)
112 * if ((AddressOf(obj) XOR AddressOf(new_val)) >> REGION_SIZE_BITS) != 0) {
113 * call UPDATE_CARD_FUNC(obj, new_val);
114 * }
115 *
116 * Runtime should provide these parameters:
117 * REGION_SIZE_BITS - log2 of the size of region
118 * UPDATE_CARD_FUNC - function which updates card corresponding to the obj.field
119 */
120 POST_INTERREGION_BARRIER =
121 EncodeBarrierType(4U, BarrierPosition::BARRIER_POSITION_POST, BarrierActionType::WRITE_BARRIER),
122 /* Note: cosider two-level card table for pre-barrier */
123 };
124
IsPreBarrier(BarrierType barrierType)125 constexpr bool IsPreBarrier(BarrierType barrierType)
126 {
127 return BitField<uint8_t, internal::BARRIER_POS_OFFSET, 1>::Get(barrierType) ==
128 BarrierPosition::BARRIER_POSITION_PRE;
129 }
130
IsPostBarrier(BarrierType barrierType)131 constexpr bool IsPostBarrier(BarrierType barrierType)
132 {
133 return BitField<uint8_t, internal::BARRIER_POS_OFFSET, 1>::Get(barrierType) ==
134 BarrierPosition::BARRIER_POSITION_POST;
135 }
136
IsWriteBarrier(BarrierType barrierType)137 constexpr bool IsWriteBarrier(BarrierType barrierType)
138 {
139 return BitField<uint8_t, internal::BARRIER_WRB_FLAG_OFFSET, 1>::Get(barrierType) ==
140 BarrierActionType::WRITE_BARRIER;
141 }
142
IsReadBarrier(BarrierType barrierType)143 constexpr bool IsReadBarrier(BarrierType barrierType)
144 {
145 return BitField<uint8_t, internal::BARRIER_WRB_FLAG_OFFSET, 1>::Get(barrierType) == BarrierActionType::READ_BARRIER;
146 }
147
148 static_assert(IsPreBarrier(BarrierType::PRE_SATB_BARRIER));
149 static_assert(IsWriteBarrier(BarrierType::PRE_SATB_BARRIER));
150 static_assert(IsPostBarrier(BarrierType::POST_INTERGENERATIONAL_BARRIER));
151 static_assert(IsWriteBarrier(BarrierType::POST_INTERGENERATIONAL_BARRIER));
152 static_assert(IsPostBarrier(BarrierType::POST_INTERREGION_BARRIER));
153 static_assert(IsWriteBarrier(BarrierType::POST_INTERREGION_BARRIER));
154
IsEmptyBarrier(BarrierType barrierType)155 constexpr bool IsEmptyBarrier(BarrierType barrierType)
156 {
157 return (barrierType == BarrierType::PRE_WRB_NONE) || (barrierType == BarrierType::POST_WRB_NONE) ||
158 (barrierType == BarrierType::PRE_RB_NONE) || (barrierType == BarrierType::POST_RB_NONE);
159 }
160
161 using ObjRefProcessFunc = void (*)(void *);
162 using ObjTwoRefProcessFunc = void (*)(const void *, const void *);
163
164 enum class BarrierOperandType {
165 ADDRESS = 0, // just an address (void*)
166 PRE_WRITE_BARRIER_ADDRESS, // contains nullptr or address of function (during concurrent marking) with this
167 // sig: void foo(void* );
168 UINT8_ADDRESS, // contains address of uint8_t value
169 FUNC_WITH_OBJ_REF_ADDRESS, // contains address of function with this sig: void foo(void* );
170 UINT8_LITERAL, // contains uint8_t value
171 FUNC_WITH_TWO_OBJ_REF_ADDRESSES, // contains address of function with this sig: void foo(void* , void* );
172 };
173
174 using BarrierOperandValue = std::variant<void *, bool *, std::atomic<ObjRefProcessFunc> *, uint8_t *, ObjRefProcessFunc,
175 uint8_t, ObjTwoRefProcessFunc>;
176
177 class BarrierOperand {
178 public:
179 // NOLINTNEXTLINE(modernize-pass-by-value)
BarrierOperand(BarrierOperandType barrierOperandType,BarrierOperandValue barrierOperandValue)180 BarrierOperand(BarrierOperandType barrierOperandType, BarrierOperandValue barrierOperandValue)
181 : barrierOperandType_(barrierOperandType), barrierOperandValue_(barrierOperandValue)
182 {
183 }
184
GetType()185 inline BarrierOperandType GetType() const
186 {
187 return barrierOperandType_;
188 }
189
GetValue()190 inline BarrierOperandValue GetValue() const
191 {
192 return barrierOperandValue_;
193 }
194
195 virtual ~BarrierOperand() = default;
196
197 DEFAULT_COPY_SEMANTIC(BarrierOperand);
198 DEFAULT_MOVE_SEMANTIC(BarrierOperand);
199
200 private:
201 BarrierOperandType barrierOperandType_;
202 BarrierOperandValue barrierOperandValue_;
203 };
204
205 } // namespace ark::mem
206
207 #endif // PANDA_LIBPANDABASE_MEM_GC_BARRIER_H
208