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) { | |
adamk
2016/06/06 19:08:29
This test seems to flake consistently on Linux nos
| |
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 |