blob: 78f693c2b01b3884649a573ef05f5c495211c597 [file] [log] [blame]
Steve Block6ded16b2010-05-10 14:33:55 +01001// Copyright 2010 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
Steve Block6ded16b2010-05-10 14:33:55 +010028#include "v8.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010029
30#include "ast.h"
31#include "compiler.h"
32#include "ic.h"
33#include "macro-assembler.h"
34#include "stub-cache.h"
Steve Block6ded16b2010-05-10 14:33:55 +010035#include "type-info.h"
Ben Murdochb0fe1622011-05-05 13:52:32 +010036
37#include "ic-inl.h"
Steve Block6ded16b2010-05-10 14:33:55 +010038#include "objects-inl.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000039
40namespace v8 {
41namespace internal {
42
Steve Block6ded16b2010-05-10 14:33:55 +010043
44TypeInfo TypeInfo::TypeFromValue(Handle<Object> value) {
45 TypeInfo info;
46 if (value->IsSmi()) {
47 info = TypeInfo::Smi();
48 } else if (value->IsHeapNumber()) {
49 info = TypeInfo::IsInt32Double(HeapNumber::cast(*value)->value())
50 ? TypeInfo::Integer32()
51 : TypeInfo::Double();
52 } else if (value->IsString()) {
53 info = TypeInfo::String();
54 } else {
55 info = TypeInfo::Unknown();
56 }
57 return info;
58}
59
Steve Blocka7e24c12009-10-30 11:49:00 +000060
Ben Murdochb8e0da22011-05-16 14:20:40 +010061STATIC_ASSERT(DEFAULT_STRING_STUB == Code::kNoExtraICState);
62
63
64TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code,
65 Handle<Context> global_context) {
66 global_context_ = global_context;
Steve Block44f0eee2011-05-26 01:26:41 +010067 PopulateMap(code);
68 ASSERT(reinterpret_cast<Address>(*dictionary_.location()) != kHandleZapValue);
Ben Murdochb0fe1622011-05-05 13:52:32 +010069}
70
71
Steve Block44f0eee2011-05-26 01:26:41 +010072Handle<Object> TypeFeedbackOracle::GetInfo(int pos) {
73 int entry = dictionary_->FindEntry(pos);
74 return entry != NumberDictionary::kNotFound
75 ? Handle<Object>(dictionary_->ValueAt(entry))
76 : Isolate::Current()->factory()->undefined_value();
Ben Murdochb0fe1622011-05-05 13:52:32 +010077}
78
79
80bool TypeFeedbackOracle::LoadIsMonomorphic(Property* expr) {
Steve Block44f0eee2011-05-26 01:26:41 +010081 Handle<Object> map_or_code(GetInfo(expr->position()));
82 if (map_or_code->IsMap()) return true;
83 if (map_or_code->IsCode()) {
84 Handle<Code> code(Code::cast(*map_or_code));
85 return code->kind() == Code::KEYED_EXTERNAL_ARRAY_LOAD_IC &&
86 code->FindFirstMap() != NULL;
87 }
88 return false;
Ben Murdochb0fe1622011-05-05 13:52:32 +010089}
90
91
Steve Block44f0eee2011-05-26 01:26:41 +010092bool TypeFeedbackOracle::StoreIsMonomorphic(Assignment* expr) {
93 Handle<Object> map_or_code(GetInfo(expr->position()));
94 if (map_or_code->IsMap()) return true;
95 if (map_or_code->IsCode()) {
96 Handle<Code> code(Code::cast(*map_or_code));
97 return code->kind() == Code::KEYED_EXTERNAL_ARRAY_STORE_IC &&
98 code->FindFirstMap() != NULL;
99 }
100 return false;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100101}
102
103
104bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) {
Steve Block44f0eee2011-05-26 01:26:41 +0100105 Handle<Object> value = GetInfo(expr->position());
Ben Murdochb8e0da22011-05-16 14:20:40 +0100106 return value->IsMap() || value->IsSmi();
Ben Murdochb0fe1622011-05-05 13:52:32 +0100107}
108
109
110Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) {
111 ASSERT(LoadIsMonomorphic(expr));
Steve Block44f0eee2011-05-26 01:26:41 +0100112 Handle<Object> map_or_code(
113 Handle<HeapObject>::cast(GetInfo(expr->position())));
114 if (map_or_code->IsCode()) {
115 Handle<Code> code(Code::cast(*map_or_code));
116 return Handle<Map>(code->FindFirstMap());
117 }
118 return Handle<Map>(Map::cast(*map_or_code));
Ben Murdochb0fe1622011-05-05 13:52:32 +0100119}
120
121
122Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType(Assignment* expr) {
123 ASSERT(StoreIsMonomorphic(expr));
Steve Block44f0eee2011-05-26 01:26:41 +0100124 Handle<HeapObject> map_or_code(
125 Handle<HeapObject>::cast(GetInfo(expr->position())));
126 if (map_or_code->IsCode()) {
127 Handle<Code> code(Code::cast(*map_or_code));
128 return Handle<Map>(code->FindFirstMap());
129 }
130 return Handle<Map>(Map::cast(*map_or_code));
Ben Murdochb0fe1622011-05-05 13:52:32 +0100131}
132
133
Ben Murdochb0fe1622011-05-05 13:52:32 +0100134ZoneMapList* TypeFeedbackOracle::LoadReceiverTypes(Property* expr,
135 Handle<String> name) {
136 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL);
137 return CollectReceiverTypes(expr->position(), name, flags);
138}
139
140
141ZoneMapList* TypeFeedbackOracle::StoreReceiverTypes(Assignment* expr,
142 Handle<String> name) {
143 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, NORMAL);
144 return CollectReceiverTypes(expr->position(), name, flags);
145}
146
147
148ZoneMapList* TypeFeedbackOracle::CallReceiverTypes(Call* expr,
149 Handle<String> name) {
150 int arity = expr->arguments()->length();
Ben Murdochb8e0da22011-05-16 14:20:40 +0100151 // Note: these flags won't let us get maps from stubs with
152 // non-default extra ic state in the megamorphic case. In the more
153 // important monomorphic case the map is obtained directly, so it's
154 // not a problem until we decide to emit more polymorphic code.
155 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
156 NORMAL,
157 Code::kNoExtraICState,
158 OWN_MAP,
159 NOT_IN_LOOP,
160 arity);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100161 return CollectReceiverTypes(expr->position(), name, flags);
162}
163
164
Ben Murdochb8e0da22011-05-16 14:20:40 +0100165CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) {
Steve Block44f0eee2011-05-26 01:26:41 +0100166 Handle<Object> value = GetInfo(expr->position());
Ben Murdochb8e0da22011-05-16 14:20:40 +0100167 if (!value->IsSmi()) return RECEIVER_MAP_CHECK;
168 CheckType check = static_cast<CheckType>(Smi::cast(*value)->value());
169 ASSERT(check != RECEIVER_MAP_CHECK);
170 return check;
171}
172
Steve Block44f0eee2011-05-26 01:26:41 +0100173ExternalArrayType TypeFeedbackOracle::GetKeyedLoadExternalArrayType(
174 Property* expr) {
175 Handle<Object> stub = GetInfo(expr->position());
176 ASSERT(stub->IsCode());
177 return Code::cast(*stub)->external_array_type();
178}
179
180ExternalArrayType TypeFeedbackOracle::GetKeyedStoreExternalArrayType(
181 Assignment* expr) {
182 Handle<Object> stub = GetInfo(expr->position());
183 ASSERT(stub->IsCode());
184 return Code::cast(*stub)->external_array_type();
185}
Ben Murdochb8e0da22011-05-16 14:20:40 +0100186
187Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck(
188 CheckType check) {
189 JSFunction* function = NULL;
190 switch (check) {
191 case RECEIVER_MAP_CHECK:
192 UNREACHABLE();
193 break;
194 case STRING_CHECK:
195 function = global_context_->string_function();
196 break;
197 case NUMBER_CHECK:
198 function = global_context_->number_function();
199 break;
200 case BOOLEAN_CHECK:
201 function = global_context_->boolean_function();
202 break;
203 }
204 ASSERT(function != NULL);
205 return Handle<JSObject>(JSObject::cast(function->instance_prototype()));
206}
207
208
Ben Murdochb0fe1622011-05-05 13:52:32 +0100209bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) {
Steve Block44f0eee2011-05-26 01:26:41 +0100210 return *GetInfo(expr->position()) ==
211 Isolate::Current()->builtins()->builtin(id);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100212}
213
214
Steve Block1e0659c2011-05-24 12:43:12 +0100215TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) {
Steve Block44f0eee2011-05-26 01:26:41 +0100216 Handle<Object> object = GetInfo(expr->position());
Ben Murdochb0fe1622011-05-05 13:52:32 +0100217 TypeInfo unknown = TypeInfo::Unknown();
218 if (!object->IsCode()) return unknown;
219 Handle<Code> code = Handle<Code>::cast(object);
220 if (!code->is_compare_ic_stub()) return unknown;
221
222 CompareIC::State state = static_cast<CompareIC::State>(code->compare_state());
223 switch (state) {
224 case CompareIC::UNINITIALIZED:
225 // Uninitialized means never executed.
226 // TODO(fschneider): Introduce a separate value for never-executed ICs.
227 return unknown;
228 case CompareIC::SMIS:
229 return TypeInfo::Smi();
230 case CompareIC::HEAP_NUMBERS:
231 return TypeInfo::Number();
232 case CompareIC::OBJECTS:
233 // TODO(kasperl): We really need a type for JS objects here.
234 return TypeInfo::NonPrimitive();
235 case CompareIC::GENERIC:
236 default:
237 return unknown;
238 }
239}
240
241
Steve Block1e0659c2011-05-24 12:43:12 +0100242TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) {
Steve Block44f0eee2011-05-26 01:26:41 +0100243 Handle<Object> object = GetInfo(expr->position());
Ben Murdochb0fe1622011-05-05 13:52:32 +0100244 TypeInfo unknown = TypeInfo::Unknown();
245 if (!object->IsCode()) return unknown;
246 Handle<Code> code = Handle<Code>::cast(object);
247 if (code->is_binary_op_stub()) {
248 BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>(
249 code->binary_op_type());
250 switch (type) {
251 case BinaryOpIC::UNINIT_OR_SMI:
252 return TypeInfo::Smi();
253 case BinaryOpIC::DEFAULT:
254 return (expr->op() == Token::DIV || expr->op() == Token::MUL)
255 ? TypeInfo::Double()
256 : TypeInfo::Integer32();
257 case BinaryOpIC::HEAP_NUMBERS:
258 return TypeInfo::Double();
259 default:
260 return unknown;
261 }
262 } else if (code->is_type_recording_binary_op_stub()) {
263 TRBinaryOpIC::TypeInfo type = static_cast<TRBinaryOpIC::TypeInfo>(
264 code->type_recording_binary_op_type());
265 TRBinaryOpIC::TypeInfo result_type = static_cast<TRBinaryOpIC::TypeInfo>(
266 code->type_recording_binary_op_result_type());
267
268 switch (type) {
269 case TRBinaryOpIC::UNINITIALIZED:
270 // Uninitialized means never executed.
271 // TODO(fschneider): Introduce a separate value for never-executed ICs
272 return unknown;
273 case TRBinaryOpIC::SMI:
274 switch (result_type) {
275 case TRBinaryOpIC::UNINITIALIZED:
276 case TRBinaryOpIC::SMI:
277 return TypeInfo::Smi();
278 case TRBinaryOpIC::INT32:
279 return TypeInfo::Integer32();
280 case TRBinaryOpIC::HEAP_NUMBER:
281 return TypeInfo::Double();
282 default:
283 return unknown;
284 }
285 case TRBinaryOpIC::INT32:
286 if (expr->op() == Token::DIV ||
287 result_type == TRBinaryOpIC::HEAP_NUMBER) {
288 return TypeInfo::Double();
289 }
290 return TypeInfo::Integer32();
291 case TRBinaryOpIC::HEAP_NUMBER:
292 return TypeInfo::Double();
293 case TRBinaryOpIC::STRING:
294 case TRBinaryOpIC::GENERIC:
295 return unknown;
296 default:
297 return unknown;
298 }
299 }
300 return unknown;
301}
302
Ben Murdochb8e0da22011-05-16 14:20:40 +0100303
Ben Murdochb0fe1622011-05-05 13:52:32 +0100304TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) {
Steve Block44f0eee2011-05-26 01:26:41 +0100305 Handle<Object> object = GetInfo(clause->position());
Ben Murdochb0fe1622011-05-05 13:52:32 +0100306 TypeInfo unknown = TypeInfo::Unknown();
307 if (!object->IsCode()) return unknown;
308 Handle<Code> code = Handle<Code>::cast(object);
309 if (!code->is_compare_ic_stub()) return unknown;
310
311 CompareIC::State state = static_cast<CompareIC::State>(code->compare_state());
312 switch (state) {
313 case CompareIC::UNINITIALIZED:
314 // Uninitialized means never executed.
315 // TODO(fschneider): Introduce a separate value for never-executed ICs.
316 return unknown;
317 case CompareIC::SMIS:
318 return TypeInfo::Smi();
319 case CompareIC::HEAP_NUMBERS:
320 return TypeInfo::Number();
321 case CompareIC::OBJECTS:
322 // TODO(kasperl): We really need a type for JS objects here.
323 return TypeInfo::NonPrimitive();
324 case CompareIC::GENERIC:
325 default:
326 return unknown;
327 }
328}
329
330
Ben Murdochb0fe1622011-05-05 13:52:32 +0100331ZoneMapList* TypeFeedbackOracle::CollectReceiverTypes(int position,
332 Handle<String> name,
333 Code::Flags flags) {
Steve Block44f0eee2011-05-26 01:26:41 +0100334 Isolate* isolate = Isolate::Current();
335 Handle<Object> object = GetInfo(position);
Ben Murdochb8e0da22011-05-16 14:20:40 +0100336 if (object->IsUndefined() || object->IsSmi()) return NULL;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100337
Steve Block44f0eee2011-05-26 01:26:41 +0100338 if (*object == isolate->builtins()->builtin(Builtins::kStoreIC_GlobalProxy)) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100339 // TODO(fschneider): We could collect the maps and signal that
340 // we need a generic store (or load) here.
341 ASSERT(Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC);
342 return NULL;
343 } else if (object->IsMap()) {
344 ZoneMapList* types = new ZoneMapList(1);
345 types->Add(Handle<Map>::cast(object));
346 return types;
347 } else if (Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) {
348 ZoneMapList* types = new ZoneMapList(4);
349 ASSERT(object->IsCode());
Steve Block44f0eee2011-05-26 01:26:41 +0100350 isolate->stub_cache()->CollectMatchingMaps(types, *name, flags);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100351 return types->length() > 0 ? types : NULL;
352 } else {
353 return NULL;
354 }
355}
356
357
358void TypeFeedbackOracle::PopulateMap(Handle<Code> code) {
Steve Block44f0eee2011-05-26 01:26:41 +0100359 Isolate* isolate = Isolate::Current();
360 HandleScope scope(isolate);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100361
362 const int kInitialCapacity = 16;
363 List<int> code_positions(kInitialCapacity);
364 List<int> source_positions(kInitialCapacity);
365 CollectPositions(*code, &code_positions, &source_positions);
366
Steve Block44f0eee2011-05-26 01:26:41 +0100367 ASSERT(dictionary_.is_null()); // Only initialize once.
368 dictionary_ = isolate->factory()->NewNumberDictionary(
369 code_positions.length());
370
Ben Murdochb0fe1622011-05-05 13:52:32 +0100371 int length = code_positions.length();
372 ASSERT(source_positions.length() == length);
373 for (int i = 0; i < length; i++) {
Steve Block44f0eee2011-05-26 01:26:41 +0100374 HandleScope loop_scope(isolate);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100375 RelocInfo info(code->instruction_start() + code_positions[i],
376 RelocInfo::CODE_TARGET, 0);
377 Handle<Code> target(Code::GetCodeFromTargetAddress(info.target_address()));
378 int position = source_positions[i];
379 InlineCacheState state = target->ic_state();
380 Code::Kind kind = target->kind();
Steve Block44f0eee2011-05-26 01:26:41 +0100381 Handle<Object> value;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100382 if (kind == Code::BINARY_OP_IC ||
383 kind == Code::TYPE_RECORDING_BINARY_OP_IC ||
384 kind == Code::COMPARE_IC) {
385 // TODO(kasperl): Avoid having multiple ICs with the same
386 // position by making sure that we have position information
387 // recorded for all binary ICs.
Steve Block44f0eee2011-05-26 01:26:41 +0100388 int entry = dictionary_->FindEntry(position);
389 if (entry == NumberDictionary::kNotFound) {
390 value = target;
Ben Murdochb0fe1622011-05-05 13:52:32 +0100391 }
392 } else if (state == MONOMORPHIC) {
Steve Block44f0eee2011-05-26 01:26:41 +0100393 if (kind == Code::KEYED_EXTERNAL_ARRAY_LOAD_IC ||
394 kind == Code::KEYED_EXTERNAL_ARRAY_STORE_IC) {
395 value = target;
396 } else if (target->kind() != Code::CALL_IC ||
Ben Murdochb8e0da22011-05-16 14:20:40 +0100397 target->check_type() == RECEIVER_MAP_CHECK) {
Steve Block44f0eee2011-05-26 01:26:41 +0100398 Map* map = target->FindFirstMap();
399 if (map == NULL) {
400 value = target;
Ben Murdochb8e0da22011-05-16 14:20:40 +0100401 } else {
Steve Block44f0eee2011-05-26 01:26:41 +0100402 value = Handle<Map>(map);
Ben Murdochb8e0da22011-05-16 14:20:40 +0100403 }
Ben Murdochb0fe1622011-05-05 13:52:32 +0100404 } else {
Ben Murdochb8e0da22011-05-16 14:20:40 +0100405 ASSERT(target->kind() == Code::CALL_IC);
406 CheckType check = target->check_type();
407 ASSERT(check != RECEIVER_MAP_CHECK);
Steve Block44f0eee2011-05-26 01:26:41 +0100408 value = Handle<Object>(Smi::FromInt(check));
Ben Murdochb0fe1622011-05-05 13:52:32 +0100409 }
410 } else if (state == MEGAMORPHIC) {
Steve Block44f0eee2011-05-26 01:26:41 +0100411 value = target;
412 }
413
414 if (!value.is_null()) {
415 Handle<NumberDictionary> new_dict =
416 isolate->factory()->DictionaryAtNumberPut(
417 dictionary_, position, value);
418 dictionary_ = loop_scope.CloseAndEscape(new_dict);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100419 }
420 }
Steve Block44f0eee2011-05-26 01:26:41 +0100421 // Allocate handle in the parent scope.
422 dictionary_ = scope.CloseAndEscape(dictionary_);
Ben Murdochb0fe1622011-05-05 13:52:32 +0100423}
424
425
426void TypeFeedbackOracle::CollectPositions(Code* code,
427 List<int>* code_positions,
428 List<int>* source_positions) {
429 AssertNoAllocation no_allocation;
430 int position = 0;
431 // Because the ICs we use for global variables access in the full
432 // code generator do not have any meaningful positions, we avoid
433 // collecting those by filtering out contextual code targets.
434 int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) |
435 RelocInfo::kPositionMask;
436 for (RelocIterator it(code, mask); !it.done(); it.next()) {
437 RelocInfo* info = it.rinfo();
438 RelocInfo::Mode mode = info->rmode();
439 if (RelocInfo::IsCodeTarget(mode)) {
440 Code* target = Code::GetCodeFromTargetAddress(info->target_address());
441 if (target->is_inline_cache_stub()) {
442 InlineCacheState state = target->ic_state();
443 Code::Kind kind = target->kind();
444 if (kind == Code::BINARY_OP_IC) {
445 if (target->binary_op_type() == BinaryOpIC::GENERIC) continue;
446 } else if (kind == Code::TYPE_RECORDING_BINARY_OP_IC) {
447 if (target->type_recording_binary_op_type() ==
448 TRBinaryOpIC::GENERIC) {
449 continue;
450 }
451 } else if (kind == Code::COMPARE_IC) {
452 if (target->compare_state() == CompareIC::GENERIC) continue;
453 } else {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100454 if (state != MONOMORPHIC && state != MEGAMORPHIC) continue;
455 }
456 code_positions->Add(
457 static_cast<int>(info->pc() - code->instruction_start()));
458 source_positions->Add(position);
459 }
460 } else {
461 ASSERT(RelocInfo::IsPosition(mode));
462 position = static_cast<int>(info->data());
463 }
464 }
465}
466
Steve Blocka7e24c12009-10-30 11:49:00 +0000467} } // namespace v8::internal