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