Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "test/cctest/cctest.h"
6 :
7 : #include "src/base/utils/random-number-generator.h"
8 : #include "src/ic/accessor-assembler.h"
9 : #include "src/ic/stub-cache.h"
10 : #include "src/objects-inl.h"
11 : #include "test/cctest/compiler/code-assembler-tester.h"
12 : #include "test/cctest/compiler/function-tester.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 :
17 : using compiler::CodeAssemblerTester;
18 : using compiler::FunctionTester;
19 : using compiler::Node;
20 :
21 : namespace {
22 :
23 12 : void TestStubCacheOffsetCalculation(StubCache::Table table) {
24 12 : Isolate* isolate(CcTest::InitIsolateOnce());
25 : const int kNumParams = 2;
26 12 : CodeAssemblerTester data(isolate, kNumParams);
27 : AccessorAssembler m(data.state());
28 :
29 : {
30 12 : Node* name = m.Parameter(0);
31 12 : Node* map = m.Parameter(1);
32 : Node* primary_offset = m.StubCachePrimaryOffsetForTesting(name, map);
33 : Node* result;
34 12 : if (table == StubCache::kPrimary) {
35 : result = primary_offset;
36 : } else {
37 6 : CHECK_EQ(StubCache::kSecondary, table);
38 : result = m.StubCacheSecondaryOffsetForTesting(name, primary_offset);
39 : }
40 24 : m.Return(m.SmiTag(result));
41 : }
42 :
43 12 : Handle<Code> code = data.GenerateCode();
44 12 : FunctionTester ft(code, kNumParams);
45 :
46 : Factory* factory = isolate->factory();
47 : Handle<Name> names[] = {
48 : factory->NewSymbol(),
49 : factory->InternalizeUtf8String("a"),
50 : factory->InternalizeUtf8String("bb"),
51 : factory->InternalizeUtf8String("ccc"),
52 : factory->NewPrivateSymbol(),
53 : factory->InternalizeUtf8String("dddd"),
54 : factory->InternalizeUtf8String("eeeee"),
55 : factory->InternalizeUtf8String("name"),
56 : factory->NewSymbol(),
57 : factory->NewPrivateSymbol(),
58 120 : };
59 :
60 : Handle<Map> maps[] = {
61 : Handle<Map>(nullptr, isolate),
62 : factory->cell_map(),
63 : Map::Create(isolate, 0),
64 : factory->meta_map(),
65 : factory->code_map(),
66 : Map::Create(isolate, 0),
67 : factory->hash_table_map(),
68 : factory->symbol_map(),
69 : factory->string_map(),
70 : Map::Create(isolate, 0),
71 : factory->sloppy_arguments_elements_map(),
72 84 : };
73 :
74 132 : for (size_t name_index = 0; name_index < arraysize(names); name_index++) {
75 120 : Handle<Name> name = names[name_index];
76 1440 : for (size_t map_index = 0; map_index < arraysize(maps); map_index++) {
77 1320 : Handle<Map> map = maps[map_index];
78 :
79 : int expected_result;
80 : {
81 : int primary_offset = StubCache::PrimaryOffsetForTesting(*name, *map);
82 1320 : if (table == StubCache::kPrimary) {
83 : expected_result = primary_offset;
84 : } else {
85 : expected_result =
86 : StubCache::SecondaryOffsetForTesting(*name, primary_offset);
87 : }
88 : }
89 2640 : Handle<Object> result = ft.Call(name, map).ToHandleChecked();
90 :
91 1320 : Smi* expected = Smi::FromInt(expected_result & Smi::kMaxValue);
92 1320 : CHECK_EQ(expected, Smi::cast(*result));
93 : }
94 12 : }
95 12 : }
96 :
97 : } // namespace
98 :
99 23724 : TEST(StubCachePrimaryOffset) {
100 6 : TestStubCacheOffsetCalculation(StubCache::kPrimary);
101 6 : }
102 :
103 23724 : TEST(StubCacheSecondaryOffset) {
104 6 : TestStubCacheOffsetCalculation(StubCache::kSecondary);
105 6 : }
106 :
107 : namespace {
108 :
109 180 : Handle<Code> CreateCodeOfKind(Code::Kind kind) {
110 180 : Isolate* isolate(CcTest::InitIsolateOnce());
111 180 : CodeAssemblerTester data(isolate, kind);
112 180 : CodeStubAssembler m(data.state());
113 360 : m.Return(m.UndefinedConstant());
114 360 : return data.GenerateCodeCloseAndEscape();
115 : }
116 :
117 : } // namespace
118 :
119 23724 : TEST(TryProbeStubCache) {
120 : typedef CodeStubAssembler::Label Label;
121 : typedef CodeStubAssembler::Variable Variable;
122 6 : Isolate* isolate(CcTest::InitIsolateOnce());
123 : const int kNumParams = 3;
124 6 : CodeAssemblerTester data(isolate, kNumParams);
125 : AccessorAssembler m(data.state());
126 :
127 6 : StubCache stub_cache(isolate);
128 6 : stub_cache.Clear();
129 :
130 : {
131 6 : Node* receiver = m.Parameter(0);
132 6 : Node* name = m.Parameter(1);
133 6 : Node* expected_handler = m.Parameter(2);
134 :
135 6 : Label passed(&m), failed(&m);
136 :
137 12 : Variable var_handler(&m, MachineRepresentation::kTagged);
138 6 : Label if_handler(&m), if_miss(&m);
139 :
140 : m.TryProbeStubCache(&stub_cache, receiver, name, &if_handler, &var_handler,
141 6 : &if_miss);
142 6 : m.BIND(&if_handler);
143 12 : m.Branch(m.WordEqual(expected_handler, var_handler.value()), &passed,
144 12 : &failed);
145 :
146 6 : m.BIND(&if_miss);
147 12 : m.Branch(m.WordEqual(expected_handler, m.IntPtrConstant(0)), &passed,
148 12 : &failed);
149 :
150 6 : m.BIND(&passed);
151 12 : m.Return(m.BooleanConstant(true));
152 :
153 6 : m.BIND(&failed);
154 18 : m.Return(m.BooleanConstant(false));
155 : }
156 :
157 6 : Handle<Code> code = data.GenerateCode();
158 6 : FunctionTester ft(code, kNumParams);
159 :
160 : std::vector<Handle<Name>> names;
161 : std::vector<Handle<JSObject>> receivers;
162 : std::vector<Handle<Code>> handlers;
163 :
164 6 : base::RandomNumberGenerator rand_gen(FLAG_random_seed);
165 :
166 : Factory* factory = isolate->factory();
167 :
168 : // Generate some number of names.
169 1758 : for (int i = 0; i < StubCache::kPrimaryTableSize / 7; i++) {
170 : Handle<Name> name;
171 1752 : switch (rand_gen.NextInt(3)) {
172 : case 0: {
173 : // Generate string.
174 612 : std::stringstream ss;
175 612 : ss << "s" << std::hex
176 1224 : << (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize);
177 1224 : name = factory->InternalizeUtf8String(ss.str().c_str());
178 612 : break;
179 : }
180 : case 1: {
181 : // Generate number string.
182 594 : std::stringstream ss;
183 594 : ss << (rand_gen.NextInt(Smi::kMaxValue) % StubCache::kPrimaryTableSize);
184 1188 : name = factory->InternalizeUtf8String(ss.str().c_str());
185 594 : break;
186 : }
187 : case 2: {
188 : // Generate symbol.
189 546 : name = factory->NewSymbol();
190 546 : break;
191 : }
192 : default:
193 0 : UNREACHABLE();
194 : }
195 1752 : names.push_back(name);
196 : }
197 :
198 : // Generate some number of receiver maps and receivers.
199 1536 : for (int i = 0; i < StubCache::kSecondaryTableSize / 2; i++) {
200 1536 : Handle<Map> map = Map::Create(isolate, 0);
201 3072 : receivers.push_back(factory->NewJSObjectFromMap(map));
202 : }
203 :
204 : // Generate some number of handlers.
205 180 : for (int i = 0; i < 30; i++) {
206 360 : handlers.push_back(CreateCodeOfKind(Code::STUB));
207 : }
208 :
209 : // Ensure that GC does happen because from now on we are going to fill our
210 : // own stub cache instance with raw values.
211 : DisallowHeapAllocation no_gc;
212 :
213 : // Populate {stub_cache}.
214 : const int N = StubCache::kPrimaryTableSize + StubCache::kSecondaryTableSize;
215 15360 : for (int i = 0; i < N; i++) {
216 : int index = rand_gen.NextInt();
217 46080 : Handle<Name> name = names[index % names.size()];
218 46080 : Handle<JSObject> receiver = receivers[index % receivers.size()];
219 46080 : Handle<Code> handler = handlers[index % handlers.size()];
220 15360 : stub_cache.Set(*name, receiver->map(), *handler);
221 : }
222 :
223 : // Perform some queries.
224 : bool queried_existing = false;
225 : bool queried_non_existing = false;
226 15360 : for (int i = 0; i < N; i++) {
227 : int index = rand_gen.NextInt();
228 46080 : Handle<Name> name = names[index % names.size()];
229 46080 : Handle<JSObject> receiver = receivers[index % receivers.size()];
230 15360 : Object* handler = stub_cache.Get(*name, receiver->map());
231 15360 : if (handler == nullptr) {
232 : queried_non_existing = true;
233 : } else {
234 : queried_existing = true;
235 : }
236 :
237 : Handle<Object> expected_handler(handler, isolate);
238 15360 : ft.CheckTrue(receiver, name, expected_handler);
239 : }
240 :
241 15360 : for (int i = 0; i < N; i++) {
242 : int index1 = rand_gen.NextInt();
243 : int index2 = rand_gen.NextInt();
244 46080 : Handle<Name> name = names[index1 % names.size()];
245 46080 : Handle<JSObject> receiver = receivers[index2 % receivers.size()];
246 15360 : Object* handler = stub_cache.Get(*name, receiver->map());
247 15360 : if (handler == nullptr) {
248 : queried_non_existing = true;
249 : } else {
250 : queried_existing = true;
251 : }
252 :
253 : Handle<Object> expected_handler(handler, isolate);
254 15360 : ft.CheckTrue(receiver, name, expected_handler);
255 : }
256 : // Ensure we performed both kind of queries.
257 12 : CHECK(queried_existing && queried_non_existing);
258 6 : }
259 :
260 : } // namespace internal
261 71154 : } // namespace v8
|