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