OLD | NEW |
1 // Copyright 2015 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/base/utils/random-number-generator.h" | 5 #include "src/base/utils/random-number-generator.h" |
| 6 #include "src/ic/stub-cache.h" |
6 #include "src/interface-descriptors.h" | 7 #include "src/interface-descriptors.h" |
7 #include "src/isolate.h" | 8 #include "src/isolate.h" |
8 #include "test/cctest/compiler/function-tester.h" | 9 #include "test/cctest/compiler/function-tester.h" |
9 | 10 |
10 namespace v8 { | 11 namespace v8 { |
11 namespace internal { | 12 namespace internal { |
12 | 13 |
13 using compiler::FunctionTester; | 14 using compiler::FunctionTester; |
14 using compiler::Node; | 15 using compiler::Node; |
15 | 16 |
(...skipping 18 matching lines...) Expand all Loading... |
34 Code::ComputeFlags(Code::STUB), "test"), | 35 Code::ComputeFlags(Code::STUB), "test"), |
35 scope_(isolate) {} | 36 scope_(isolate) {} |
36 | 37 |
37 // Test generating code for a JS function (e.g. builtins). | 38 // Test generating code for a JS function (e.g. builtins). |
38 CodeStubAssemblerTester(Isolate* isolate, int parameter_count) | 39 CodeStubAssemblerTester(Isolate* isolate, int parameter_count) |
39 : ZoneHolder(isolate), | 40 : ZoneHolder(isolate), |
40 CodeStubAssembler(isolate, ZoneHolder::zone(), parameter_count, | 41 CodeStubAssembler(isolate, ZoneHolder::zone(), parameter_count, |
41 Code::ComputeFlags(Code::FUNCTION), "test"), | 42 Code::ComputeFlags(Code::FUNCTION), "test"), |
42 scope_(isolate) {} | 43 scope_(isolate) {} |
43 | 44 |
| 45 // This constructor is intended to be used for creating code objects with |
| 46 // specific flags. |
| 47 CodeStubAssemblerTester(Isolate* isolate, Code::Flags flags) |
| 48 : ZoneHolder(isolate), |
| 49 CodeStubAssembler(isolate, ZoneHolder::zone(), 0, flags, "test"), |
| 50 scope_(isolate) {} |
| 51 |
| 52 Handle<Code> GenerateCodeCloseAndEscape() { |
| 53 return scope_.CloseAndEscape(GenerateCode()); |
| 54 } |
| 55 |
44 private: | 56 private: |
45 HandleScope scope_; | 57 HandleScope scope_; |
46 LocalContext context_; | 58 LocalContext context_; |
47 }; | 59 }; |
48 | 60 |
49 TEST(SimpleSmiReturn) { | 61 TEST(SimpleSmiReturn) { |
50 Isolate* isolate(CcTest::InitIsolateOnce()); | 62 Isolate* isolate(CcTest::InitIsolateOnce()); |
51 VoidDescriptor descriptor(isolate); | 63 VoidDescriptor descriptor(isolate); |
52 CodeStubAssemblerTester m(isolate, descriptor); | 64 CodeStubAssemblerTester m(isolate, descriptor); |
53 m.Return(m.SmiTag(m.Int32Constant(37))); | 65 m.Return(m.SmiTag(m.Int32Constant(37))); |
(...skipping 1058 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1112 m.Goto(&block1); | 1124 m.Goto(&block1); |
1113 | 1125 |
1114 m.Bind(&block3); | 1126 m.Bind(&block3); |
1115 var_object.Bind(m.IntPtrConstant(66)); | 1127 var_object.Bind(m.IntPtrConstant(66)); |
1116 m.Goto(&block1); | 1128 m.Goto(&block1); |
1117 } | 1129 } |
1118 m.Bind(&block1); | 1130 m.Bind(&block1); |
1119 CHECK(!m.GenerateCode().is_null()); | 1131 CHECK(!m.GenerateCode().is_null()); |
1120 } | 1132 } |
1121 | 1133 |
| 1134 namespace { |
| 1135 |
| 1136 void TestStubCacheOffsetCalculation(StubCache::Table table, |
| 1137 Code::Kind handler_kind) { |
| 1138 Isolate* isolate(CcTest::InitIsolateOnce()); |
| 1139 const int param_count = 2; |
| 1140 CodeStubAssemblerTester m(isolate, param_count); |
| 1141 |
| 1142 Code::Flags code_flags = |
| 1143 Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(handler_kind)); |
| 1144 { |
| 1145 Node* name = m.Parameter(0); |
| 1146 Node* map = m.Parameter(1); |
| 1147 Node* primary_offset = m.StubCachePrimaryOffset(name, code_flags, map); |
| 1148 Node* result; |
| 1149 if (table == StubCache::kPrimary) { |
| 1150 result = primary_offset; |
| 1151 } else { |
| 1152 CHECK_EQ(StubCache::kSecondary, table); |
| 1153 result = m.StubCacheSecondaryOffset(name, code_flags, primary_offset); |
| 1154 } |
| 1155 m.Return(m.SmiFromWord32(result)); |
| 1156 } |
| 1157 |
| 1158 Handle<Code> code = m.GenerateCode(); |
| 1159 FunctionTester ft(code, param_count); |
| 1160 |
| 1161 Factory* factory = isolate->factory(); |
| 1162 Handle<Name> names[] = { |
| 1163 factory->NewSymbol(), |
| 1164 factory->InternalizeUtf8String("a"), |
| 1165 factory->InternalizeUtf8String("bb"), |
| 1166 factory->InternalizeUtf8String("ccc"), |
| 1167 factory->NewPrivateSymbol(), |
| 1168 factory->InternalizeUtf8String("dddd"), |
| 1169 factory->InternalizeUtf8String("eeeee"), |
| 1170 factory->InternalizeUtf8String("name"), |
| 1171 factory->NewSymbol(), |
| 1172 factory->NewPrivateSymbol(), |
| 1173 }; |
| 1174 |
| 1175 Handle<Map> maps[] = { |
| 1176 Handle<Map>(nullptr, isolate), |
| 1177 factory->cell_map(), |
| 1178 Map::Create(isolate, 0), |
| 1179 factory->meta_map(), |
| 1180 factory->code_map(), |
| 1181 Map::Create(isolate, 0), |
| 1182 factory->hash_table_map(), |
| 1183 factory->symbol_map(), |
| 1184 factory->string_map(), |
| 1185 Map::Create(isolate, 0), |
| 1186 factory->sloppy_arguments_elements_map(), |
| 1187 }; |
| 1188 |
| 1189 for (int name_index = 0; name_index < arraysize(names); name_index++) { |
| 1190 Handle<Name> name = names[name_index]; |
| 1191 for (int map_index = 0; map_index < arraysize(maps); map_index++) { |
| 1192 Handle<Map> map = maps[map_index]; |
| 1193 |
| 1194 int expected_result; |
| 1195 { |
| 1196 int primary_offset = |
| 1197 StubCache::PrimaryOffsetForTesting(*name, code_flags, *map); |
| 1198 if (table == StubCache::kPrimary) { |
| 1199 expected_result = primary_offset; |
| 1200 } else { |
| 1201 expected_result = StubCache::SecondaryOffsetForTesting( |
| 1202 *name, code_flags, primary_offset); |
| 1203 } |
| 1204 } |
| 1205 Handle<Object> result = ft.Call(name, map).ToHandleChecked(); |
| 1206 |
| 1207 Smi* expected = Smi::FromInt(expected_result & Smi::kMaxValue); |
| 1208 CHECK_EQ(expected, Smi::cast(*result)); |
| 1209 } |
| 1210 } |
| 1211 } |
| 1212 |
| 1213 } // namespace |
| 1214 |
| 1215 TEST(StubCachePrimaryOffsetLoadIC) { |
| 1216 TestStubCacheOffsetCalculation(StubCache::kPrimary, Code::LOAD_IC); |
| 1217 } |
| 1218 |
| 1219 TEST(StubCachePrimaryOffsetStoreIC) { |
| 1220 TestStubCacheOffsetCalculation(StubCache::kPrimary, Code::STORE_IC); |
| 1221 } |
| 1222 |
| 1223 TEST(StubCacheSecondaryOffsetLoadIC) { |
| 1224 TestStubCacheOffsetCalculation(StubCache::kSecondary, Code::LOAD_IC); |
| 1225 } |
| 1226 |
| 1227 TEST(StubCacheSecondaryOffsetStoreIC) { |
| 1228 TestStubCacheOffsetCalculation(StubCache::kSecondary, Code::STORE_IC); |
| 1229 } |
| 1230 |
| 1231 namespace { |
| 1232 |
| 1233 Handle<Code> CreateCodeWithFlags(Code::Flags flags) { |
| 1234 Isolate* isolate(CcTest::InitIsolateOnce()); |
| 1235 CodeStubAssemblerTester m(isolate, flags); |
| 1236 m.Return(m.UndefinedConstant()); |
| 1237 return m.GenerateCodeCloseAndEscape(); |
| 1238 } |
| 1239 |
| 1240 } // namespace |
| 1241 |
| 1242 TEST(TryProbeStubCache) { |
| 1243 typedef CodeStubAssembler::Label Label; |
| 1244 typedef CodeStubAssembler::Variable Variable; |
| 1245 Isolate* isolate(CcTest::InitIsolateOnce()); |
| 1246 const int param_count = 3; |
| 1247 CodeStubAssemblerTester m(isolate, param_count); |
| 1248 |
| 1249 Code::Flags flags_to_query = |
| 1250 Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(Code::LOAD_IC)); |
| 1251 |
| 1252 StubCache stub_cache(isolate); |
| 1253 stub_cache.Clear(); |
| 1254 |
| 1255 { |
| 1256 Node* receiver = m.Parameter(0); |
| 1257 Node* name = m.Parameter(1); |
| 1258 Node* expected_handler = m.Parameter(2); |
| 1259 |
| 1260 Label passed(&m), failed(&m); |
| 1261 |
| 1262 Variable var_handler(&m, MachineRepresentation::kTagged); |
| 1263 Label if_handler(&m), if_miss(&m); |
| 1264 |
| 1265 m.TryProbeStubCache(&stub_cache, flags_to_query, receiver, name, |
| 1266 &if_handler, &var_handler, &if_miss); |
| 1267 m.Bind(&if_handler); |
| 1268 m.BranchIfWordEqual(expected_handler, var_handler.value(), &passed, |
| 1269 &failed); |
| 1270 |
| 1271 m.Bind(&if_miss); |
| 1272 m.BranchIfWordEqual(expected_handler, m.IntPtrConstant(0), &passed, |
| 1273 &failed); |
| 1274 |
| 1275 m.Bind(&passed); |
| 1276 m.Return(m.BooleanConstant(true)); |
| 1277 |
| 1278 m.Bind(&failed); |
| 1279 m.Return(m.BooleanConstant(false)); |
| 1280 } |
| 1281 |
| 1282 Handle<Code> code = m.GenerateCode(); |
| 1283 FunctionTester ft(code, param_count); |
| 1284 |
| 1285 std::vector<Handle<Name>> names; |
| 1286 std::vector<Handle<JSObject>> receivers; |
| 1287 std::vector<Handle<Code>> handlers; |
| 1288 |
| 1289 base::RandomNumberGenerator rand_gen(FLAG_random_seed); |
| 1290 |
| 1291 Factory* factory = isolate->factory(); |
| 1292 |
| 1293 // Generate some number of names. |
| 1294 for (int i = 0; i < StubCache::kPrimaryTableSize / 7; i++) { |
| 1295 Handle<Name> name; |
| 1296 switch (rand_gen.NextInt(3)) { |
| 1297 case 0: { |
| 1298 // Generate string. |
| 1299 std::stringstream ss; |
| 1300 ss << "s" << std::hex |
| 1301 << (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize); |
| 1302 name = factory->InternalizeUtf8String(ss.str().c_str()); |
| 1303 break; |
| 1304 } |
| 1305 case 1: { |
| 1306 // Generate number string. |
| 1307 std::stringstream ss; |
| 1308 ss << (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize); |
| 1309 name = factory->InternalizeUtf8String(ss.str().c_str()); |
| 1310 break; |
| 1311 } |
| 1312 case 2: { |
| 1313 // Generate symbol. |
| 1314 name = factory->NewSymbol(); |
| 1315 break; |
| 1316 } |
| 1317 default: |
| 1318 UNREACHABLE(); |
| 1319 } |
| 1320 names.push_back(name); |
| 1321 } |
| 1322 |
| 1323 // Generate some number of receiver maps and receivers. |
| 1324 for (int i = 0; i < StubCache::kSecondaryTableSize / 2; i++) { |
| 1325 Handle<Map> map = Map::Create(isolate, 0); |
| 1326 receivers.push_back(factory->NewJSObjectFromMap(map)); |
| 1327 } |
| 1328 |
| 1329 // Generate some number of handlers. |
| 1330 for (int i = 0; i < StubCache::kSecondaryTableSize; i++) { |
| 1331 Code::Kind code_kind; |
| 1332 switch (rand_gen.NextInt(4)) { |
| 1333 case 0: |
| 1334 code_kind = Code::LOAD_IC; |
| 1335 break; |
| 1336 case 1: |
| 1337 code_kind = Code::KEYED_LOAD_IC; |
| 1338 break; |
| 1339 case 2: |
| 1340 code_kind = Code::STORE_IC; |
| 1341 break; |
| 1342 case 3: |
| 1343 code_kind = Code::KEYED_STORE_IC; |
| 1344 break; |
| 1345 default: |
| 1346 UNREACHABLE(); |
| 1347 } |
| 1348 Code::Flags flags = |
| 1349 Code::RemoveHolderFromFlags(Code::ComputeHandlerFlags(code_kind)); |
| 1350 handlers.push_back(CreateCodeWithFlags(flags)); |
| 1351 } |
| 1352 |
| 1353 // Ensure that GC does happen because from now on we are going to fill our |
| 1354 // own stub cache instance with raw values. |
| 1355 DisallowHeapAllocation no_gc; |
| 1356 |
| 1357 // Populate {stub_cache}. |
| 1358 const int N = StubCache::kPrimaryTableSize + StubCache::kSecondaryTableSize; |
| 1359 for (int i = 0; i < N; i++) { |
| 1360 int index = rand_gen.NextInt(); |
| 1361 Handle<Name> name = names[index % names.size()]; |
| 1362 Handle<JSObject> receiver = receivers[index % receivers.size()]; |
| 1363 Handle<Code> handler = handlers[index % handlers.size()]; |
| 1364 stub_cache.Set(*name, receiver->map(), *handler); |
| 1365 } |
| 1366 |
| 1367 // Perform some queries. |
| 1368 bool queried_existing = false; |
| 1369 bool queried_non_existing = false; |
| 1370 for (int i = 0; i < N; i++) { |
| 1371 int index = rand_gen.NextInt(); |
| 1372 Handle<Name> name = names[index % names.size()]; |
| 1373 Handle<JSObject> receiver = receivers[index % receivers.size()]; |
| 1374 Code* handler = stub_cache.Get(*name, receiver->map(), flags_to_query); |
| 1375 if (handler == nullptr) { |
| 1376 queried_non_existing = true; |
| 1377 } else { |
| 1378 queried_existing = true; |
| 1379 } |
| 1380 |
| 1381 Handle<Code> expected_handler(handler, isolate); |
| 1382 ft.CheckTrue(receiver, name, expected_handler); |
| 1383 } |
| 1384 |
| 1385 for (int i = 0; i < N; i++) { |
| 1386 int index1 = rand_gen.NextInt(); |
| 1387 int index2 = rand_gen.NextInt(); |
| 1388 Handle<Name> name = names[index1 % names.size()]; |
| 1389 Handle<JSObject> receiver = receivers[index2 % receivers.size()]; |
| 1390 Code* handler = stub_cache.Get(*name, receiver->map(), flags_to_query); |
| 1391 if (handler == nullptr) { |
| 1392 queried_non_existing = true; |
| 1393 } else { |
| 1394 queried_existing = true; |
| 1395 } |
| 1396 |
| 1397 Handle<Code> expected_handler(handler, isolate); |
| 1398 ft.CheckTrue(receiver, name, expected_handler); |
| 1399 } |
| 1400 // Ensure we performed both kind of queries. |
| 1401 CHECK(queried_existing && queried_non_existing); |
| 1402 } |
| 1403 |
1122 } // namespace internal | 1404 } // namespace internal |
1123 } // namespace v8 | 1405 } // namespace v8 |
OLD | NEW |