• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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