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