1/*
2 * Copyright (c) 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#include "ecmascript/global_env.h"
17#include "ecmascript/tests/test_helper.h"
18
19using namespace panda::ecmascript;
20
21namespace panda::test {
22class BarrierTest : public BaseTestWithScope<true> {
23};
24
25HWTEST_F_L0(BarrierTest, YoungToYoungBatchCopy)
26{
27    ObjectFactory* factory = thread->GetEcmaVM()->GetFactory();
28    uint32_t arrayLength = 10;
29    JSHandle<TaggedArray> srcArray = factory->NewTaggedArray(arrayLength);
30    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
31    for (uint32_t i = 0; i < arrayLength; i++) {
32        JSHandle<JSFunction> newFun = factory->NewJSFunction(env);
33        srcArray->Set(thread, i, newFun);
34    }
35
36    JSHandle<TaggedArray> dstArray = factory->NewTaggedArray(arrayLength);
37    std::set<uintptr_t> NewToEdenBeforeCopy;
38    std::set<uintptr_t> LocalToShareBeforeCopy;
39
40    Region* dstRegion = Region::ObjectAddressToRange(dstArray.GetObject<TaggedArray>());
41    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
42        NewToEdenBeforeCopy.emplace(ToUintPtr(mem));
43        return true;
44    });
45    dstRegion->IterateAllLocalToShareBits([&LocalToShareBeforeCopy](void* mem) {
46        LocalToShareBeforeCopy.emplace(ToUintPtr(mem));
47        return true;
48    });
49
50    JSTaggedValue* to = reinterpret_cast<JSTaggedValue*>(ToUintPtr(dstArray->GetData()));
51    JSTaggedValue* from = reinterpret_cast<JSTaggedValue*>(ToUintPtr(srcArray->GetData()));
52    Barriers::CopyObject<true, false>(thread, to, from, arrayLength);
53
54    // young to young, all the bitset should not be changed.
55    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
56        EXPECT_TRUE(NewToEdenBeforeCopy.count(ToUintPtr(mem)));
57        return true;
58    });
59    dstRegion->IterateAllLocalToShareBits([&LocalToShareBeforeCopy](void* mem) {
60        EXPECT_TRUE(LocalToShareBeforeCopy.count(ToUintPtr(mem)));
61        return true;
62    });
63
64    // check
65    for (uint32_t i = 0; i < arrayLength; i++) {
66        EXPECT_EQ(dstArray->Get(thread, i), srcArray->Get(thread, i));
67    }
68}
69
70HWTEST_F_L0(BarrierTest, BatchCopyNoBarrier)
71{
72    ObjectFactory* factory = thread->GetEcmaVM()->GetFactory();
73    uint32_t arrayLength = 10;
74    JSHandle<TaggedArray> srcArray = factory->NewTaggedArray(arrayLength);
75    for (uint32_t i = 0; i < arrayLength; i++) {
76        srcArray->Set(thread, i, JSTaggedValue(i));
77    }
78
79    JSHandle<TaggedArray> dstArray = factory->NewTaggedArray(arrayLength);
80    std::set<uintptr_t> NewToEdenBeforeCopy;
81    std::set<uintptr_t> LocalToShareBeforeCopy;
82
83    Region* dstRegion = Region::ObjectAddressToRange(dstArray.GetObject<TaggedArray>());
84    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
85        NewToEdenBeforeCopy.emplace(ToUintPtr(mem));
86        return true;
87    });
88    dstRegion->IterateAllLocalToShareBits([&LocalToShareBeforeCopy](void* mem) {
89        LocalToShareBeforeCopy.emplace(ToUintPtr(mem));
90        return true;
91    });
92
93    JSTaggedValue* to = reinterpret_cast<JSTaggedValue*>(ToUintPtr(dstArray->GetData()));
94    JSTaggedValue* from = reinterpret_cast<JSTaggedValue*>(ToUintPtr(srcArray->GetData()));
95    Barriers::CopyObjectPrimitive<false>(to, from, arrayLength);
96
97    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
98        EXPECT_TRUE(NewToEdenBeforeCopy.count(ToUintPtr(mem)));
99        return true;
100    });
101    dstRegion->IterateAllLocalToShareBits([&LocalToShareBeforeCopy](void* mem) {
102        EXPECT_TRUE(LocalToShareBeforeCopy.count(ToUintPtr(mem)));
103        return true;
104    });
105
106    // check
107    for (uint32_t i = 0; i < arrayLength; i++) {
108        EXPECT_EQ(dstArray->Get(thread, i), srcArray->Get(thread, i));
109    }
110
111    JSHandle<TaggedArray> dstArray2 = factory->NewTaggedArray(arrayLength);
112    JSTaggedValue* to2 = reinterpret_cast<JSTaggedValue*>(ToUintPtr(dstArray2->GetData()));
113    JSTaggedValue* from2 = reinterpret_cast<JSTaggedValue*>(ToUintPtr(srcArray->GetData()));
114    // barrier should also work for no heap value
115    Barriers::CopyObject<true, false>(thread, to2, from2, arrayLength);
116    // check
117    for (uint32_t i = 0; i < arrayLength; i++) {
118        EXPECT_EQ(dstArray2->Get(thread, i), srcArray->Get(thread, i));
119    }
120}
121
122HWTEST_F_L0(BarrierTest, LocalToShareBatchCopy)
123{
124    ObjectFactory* factory = thread->GetEcmaVM()->GetFactory();
125    uint32_t arrayLength = 10;
126    JSHandle<TaggedArray> srcArray = factory->NewTaggedArray(arrayLength);
127    for (uint32_t i = 0; i < arrayLength; i++) {
128        JSHandle<EcmaString> str = factory->NewFromStdString(std::to_string(i) + "_" + std::to_string(i));
129        // string longer than 1 will be in sweepable shared heap
130        srcArray->Set(thread, i, str);
131    }
132
133    JSHandle<TaggedArray> dstArray = factory->NewTaggedArray(arrayLength);
134    std::set<uintptr_t> NewToEdenBeforeCopy;
135    std::set<uintptr_t> LocalToShareBeforeCopy;
136
137    Region* dstRegion = Region::ObjectAddressToRange(dstArray.GetObject<TaggedArray>());
138    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
139        NewToEdenBeforeCopy.emplace(ToUintPtr(mem));
140        return true;
141    });
142    dstRegion->IterateAllLocalToShareBits([&LocalToShareBeforeCopy](void* mem) {
143        LocalToShareBeforeCopy.emplace(ToUintPtr(mem));
144        return true;
145    });
146
147    JSTaggedValue* to = reinterpret_cast<JSTaggedValue*>(ToUintPtr(dstArray->GetData()));
148    JSTaggedValue* from = reinterpret_cast<JSTaggedValue*>(ToUintPtr(srcArray->GetData()));
149    Barriers::CopyObject<true, false>(thread, to, from, arrayLength);
150
151    std::set<uintptr_t> LocalToShareSlot;
152    for (uint32_t i = 0; i < arrayLength; i++) {
153        LocalToShareSlot.insert(ToUintPtr(dstArray->GetData() + i));
154    }
155    // young to young, all the bitset should not be changed.
156    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
157        EXPECT_TRUE(NewToEdenBeforeCopy.count(ToUintPtr(mem)));
158        return true;
159    });
160    dstRegion->IterateAllLocalToShareBits([&LocalToShareSlot, &LocalToShareBeforeCopy, &dstArray, arrayLength](
161        void* mem) {
162            if (!LocalToShareBeforeCopy.count(ToUintPtr(mem))) {
163                EXPECT_GE(ToUintPtr(mem), ToUintPtr(dstArray->GetData()));
164                EXPECT_LT(ToUintPtr(mem), ToUintPtr(dstArray->GetData()+arrayLength));
165                LocalToShareSlot.erase(ToUintPtr(mem));
166            } else {
167                EXPECT_TRUE(LocalToShareBeforeCopy.count(ToUintPtr(mem)));
168            }
169            return true;
170        });
171    EXPECT_TRUE(LocalToShareSlot.empty());
172    // check
173    for (uint32_t i = 0; i < arrayLength; i++) {
174        EXPECT_EQ(dstArray->Get(thread, i), srcArray->Get(thread, i));
175    }
176}
177
178HWTEST_F_L0(BarrierTest, LocalToReadOnlyShareBatchCopy)
179{
180    ObjectFactory* factory = thread->GetEcmaVM()->GetFactory();
181    uint32_t arrayLength = 10;
182    JSHandle<TaggedArray> srcArray = factory->NewTaggedArray(arrayLength);
183    for (uint32_t i = 0; i < arrayLength; i++) {
184        JSHandle<EcmaString> str = factory->NewFromStdString(std::to_string(i));
185        // One byte string is in readonly
186        srcArray->Set(thread, i, str);
187    }
188
189    JSHandle<TaggedArray> dstArray = factory->NewTaggedArray(arrayLength);
190    std::set<uintptr_t> NewToEdenBeforeCopy;
191    std::set<uintptr_t> LocalToShareBeforeCopy;
192
193    Region* dstRegion = Region::ObjectAddressToRange(dstArray.GetObject<TaggedArray>());
194    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
195        NewToEdenBeforeCopy.emplace(ToUintPtr(mem));
196        return true;
197    });
198    dstRegion->IterateAllLocalToShareBits([&LocalToShareBeforeCopy](void* mem) {
199        LocalToShareBeforeCopy.emplace(ToUintPtr(mem));
200        return true;
201    });
202
203    JSTaggedValue* to = reinterpret_cast<JSTaggedValue*>(ToUintPtr(dstArray->GetData()));
204    JSTaggedValue* from = reinterpret_cast<JSTaggedValue*>(ToUintPtr(srcArray->GetData()));
205    Barriers::CopyObject<true, false>(thread, to, from, arrayLength);
206
207    std::set<uintptr_t> LocalToShareSlot;
208    for (uint32_t i = 0; i < arrayLength; i++) {
209        LocalToShareSlot.insert(ToUintPtr(dstArray->GetData() + i));
210    }
211    // young to young, all the bitset should not be changed.
212    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
213        EXPECT_TRUE(NewToEdenBeforeCopy.count(ToUintPtr(mem)));
214        return true;
215    });
216    dstRegion->IterateAllLocalToShareBits([&LocalToShareSlot, &LocalToShareBeforeCopy](
217        void* mem) {
218            EXPECT_FALSE(LocalToShareSlot.count(ToUintPtr(mem)));
219            EXPECT_TRUE(LocalToShareBeforeCopy.count(ToUintPtr(mem)));
220            return true;
221        });
222    // check
223    for (uint32_t i = 0; i < arrayLength; i++) {
224        EXPECT_EQ(dstArray->Get(thread, i), srcArray->Get(thread, i));
225    }
226}
227
228HWTEST_F_L0(BarrierTest, LocalToShareMixBatchCopy)
229{
230    ObjectFactory* factory = thread->GetEcmaVM()->GetFactory();
231    uint32_t arrayLength = 10;
232    JSHandle<TaggedArray> srcArray = factory->NewTaggedArray(arrayLength);
233    for (uint32_t i = 0; i < arrayLength; i++) {
234        if (i % 2 == 0) {
235            JSHandle<EcmaString> str = factory->NewFromStdString(std::to_string(i) + "_" + std::to_string(i));
236            // One byte string is in readonly
237            srcArray->Set(thread, i, str);
238        } else {
239            srcArray->Set(thread, i, JSTaggedValue(i));
240        }
241    }
242
243    JSHandle<TaggedArray> dstArray = factory->NewTaggedArray(arrayLength);
244    std::set<uintptr_t> NewToEdenBeforeCopy;
245    std::set<uintptr_t> LocalToShareBeforeCopy;
246
247    Region* dstRegion = Region::ObjectAddressToRange(dstArray.GetObject<TaggedArray>());
248    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
249        NewToEdenBeforeCopy.emplace(ToUintPtr(mem));
250        return true;
251    });
252    dstRegion->IterateAllLocalToShareBits([&LocalToShareBeforeCopy](void* mem) {
253        LocalToShareBeforeCopy.emplace(ToUintPtr(mem));
254        return true;
255    });
256
257    JSTaggedValue* to = reinterpret_cast<JSTaggedValue*>(ToUintPtr(dstArray->GetData()));
258    JSTaggedValue* from = reinterpret_cast<JSTaggedValue*>(ToUintPtr(srcArray->GetData()));
259    Barriers::CopyObject<true, false>(thread, to, from, arrayLength);
260
261    std::set<uintptr_t> LocalToShareSlot;
262    for (uint32_t i = 0; i < arrayLength; i++) {
263        if (i % 2 == 0) {
264            LocalToShareSlot.insert(ToUintPtr(dstArray->GetData() + i));
265        }
266    }
267    // young to young, all the bitset should not be changed.
268    dstRegion->IterateAllNewToEdenBits([&NewToEdenBeforeCopy](void* mem) {
269        EXPECT_TRUE(NewToEdenBeforeCopy.count(ToUintPtr(mem)));
270        return true;
271    });
272    dstRegion->IterateAllLocalToShareBits([&LocalToShareSlot, &LocalToShareBeforeCopy, &dstArray, arrayLength](
273        void* mem) {
274            if (!LocalToShareBeforeCopy.count(ToUintPtr(mem))) {
275                EXPECT_GE(ToUintPtr(mem), ToUintPtr(dstArray->GetData()));
276                EXPECT_LT(ToUintPtr(mem), ToUintPtr(dstArray->GetData()+arrayLength));
277                LocalToShareSlot.erase(ToUintPtr(mem));
278            } else {
279                EXPECT_TRUE(LocalToShareBeforeCopy.count(ToUintPtr(mem)));
280            }
281            return true;
282        });
283    EXPECT_TRUE(LocalToShareSlot.empty());
284    // check
285    for (uint32_t i = 0; i < arrayLength; i++) {
286        EXPECT_EQ(dstArray->Get(thread, i), srcArray->Get(thread, i));
287    }
288}
289
290HWTEST_F_L0(BarrierTest, OldToNewBatchCopy)
291{
292    ObjectFactory* factory = thread->GetEcmaVM()->GetFactory();
293    uint32_t arrayLength = 10;
294    // length 50 will be in old
295    JSHandle<TaggedArray> srcArray = factory->NewOldSpaceTaggedArray(arrayLength);
296    JSHandle<TaggedArray> dstArray = factory->NewOldSpaceTaggedArray(arrayLength);
297
298    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
299    JSHandle<JSFunction> newFun = factory->NewJSFunction(env); // in young
300    for (uint32_t i = 0; i < 10; i++) {
301        srcArray->Set(thread, i, newFun);
302    }
303
304    std::set<uintptr_t> OldToNewSlot;
305    for (uint32_t i = 0; i < arrayLength; i++) {
306        if (Region::ObjectAddressToRange(srcArray->Get(thread, i).GetTaggedObject())->InYoungSpace()) {
307            OldToNewSlot.insert(ToUintPtr(dstArray->GetData() + i));
308        }
309    }
310    EXPECT_TRUE(!OldToNewSlot.empty());
311
312    std::set<uintptr_t> OldToNewBeforeCopy;
313    std::set<uintptr_t> LocalToShareBeforeCopy;
314
315    Region* dstRegion = Region::ObjectAddressToRange(dstArray.GetObject<TaggedArray>());
316    dstRegion->IterateAllOldToNewBits([&OldToNewBeforeCopy](void* mem) {
317        OldToNewBeforeCopy.emplace(ToUintPtr(mem));
318        return true;
319    });
320    dstRegion->IterateAllLocalToShareBits([&LocalToShareBeforeCopy](void* mem) {
321        LocalToShareBeforeCopy.emplace(ToUintPtr(mem));
322        return true;
323    });
324
325    JSTaggedValue* to = reinterpret_cast<JSTaggedValue*>(ToUintPtr(dstArray->GetData()));
326    JSTaggedValue* from = reinterpret_cast<JSTaggedValue*>(ToUintPtr(srcArray->GetData()));
327    Barriers::CopyObject<true, false>(thread, to, from, arrayLength);
328
329    // young to young, all the bitset should not be changed.
330    dstRegion->IterateAllNewToEdenBits([&OldToNewSlot, &OldToNewBeforeCopy, &dstArray, arrayLength](void* mem) {
331        if (!OldToNewBeforeCopy.count(ToUintPtr(mem))) {
332            EXPECT_GE(ToUintPtr(mem), ToUintPtr(dstArray->GetData()));
333            EXPECT_LT(ToUintPtr(mem), ToUintPtr(dstArray->GetData()+arrayLength));
334            OldToNewSlot.erase(ToUintPtr(mem));
335        } else {
336            EXPECT_TRUE(OldToNewBeforeCopy.count(ToUintPtr(mem)));
337        }
338        return true;
339    });
340    dstRegion->IterateAllLocalToShareBits([&LocalToShareBeforeCopy](void* mem) {
341        EXPECT_TRUE(LocalToShareBeforeCopy.count(ToUintPtr(mem)));
342        return true;
343    });
344    EXPECT_TRUE(OldToNewSlot.empty());
345    // check
346    for (uint32_t i = 0; i < arrayLength; i++) {
347        EXPECT_EQ(dstArray->Get(thread, i), srcArray->Get(thread, i));
348    }
349}
350} // namespace panda::ecmascript
351