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