Line data Source code
1 : // Copyright 2012 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 "src/code-stubs.h"
6 :
7 : #include <memory>
8 :
9 : #include "src/assembler-inl.h"
10 : #include "src/bailout-reason.h"
11 : #include "src/code-factory.h"
12 : #include "src/code-stub-assembler.h"
13 : #include "src/crankshaft/hydrogen.h"
14 : #include "src/crankshaft/lithium.h"
15 : #include "src/field-index.h"
16 : #include "src/ic/ic.h"
17 : #include "src/objects-inl.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 :
22 :
23 23533 : static LChunk* OptimizeGraph(HGraph* graph) {
24 : DisallowHeapAllocation no_allocation;
25 : DisallowHandleAllocation no_handles;
26 : DisallowHandleDereference no_deref;
27 :
28 : DCHECK(graph != NULL);
29 23533 : BailoutReason bailout_reason = kNoReason;
30 23533 : if (!graph->Optimize(&bailout_reason)) {
31 0 : FATAL(GetBailoutReason(bailout_reason));
32 : }
33 23533 : LChunk* chunk = LChunk::NewChunk(graph);
34 23533 : if (chunk == NULL) {
35 0 : FATAL(GetBailoutReason(graph->info()->bailout_reason()));
36 : }
37 23533 : return chunk;
38 : }
39 :
40 :
41 47066 : class CodeStubGraphBuilderBase : public HGraphBuilder {
42 : public:
43 23533 : explicit CodeStubGraphBuilderBase(CompilationInfo* info, CodeStub* code_stub)
44 23533 : : HGraphBuilder(info, code_stub->GetCallInterfaceDescriptor(), false),
45 : arguments_length_(NULL),
46 : info_(info),
47 : code_stub_(code_stub),
48 : descriptor_(code_stub),
49 47066 : context_(NULL) {
50 : int parameter_count = GetParameterCount();
51 23533 : parameters_.reset(new HParameter*[parameter_count]);
52 23533 : }
53 : virtual bool BuildGraph();
54 :
55 : protected:
56 : virtual HValue* BuildCodeStub() = 0;
57 : int GetParameterCount() const { return descriptor_.GetParameterCount(); }
58 : int GetRegisterParameterCount() const {
59 : return descriptor_.GetRegisterParameterCount();
60 : }
61 : HParameter* GetParameter(int parameter) {
62 : DCHECK(parameter < GetParameterCount());
63 23533 : return parameters_[parameter];
64 : }
65 37177 : Representation GetParameterRepresentation(int parameter) {
66 : return RepresentationFromMachineType(
67 37177 : descriptor_.GetParameterType(parameter));
68 : }
69 : bool IsParameterCountRegister(int index) const {
70 : return descriptor_.GetRegisterParameter(index)
71 : .is(descriptor_.stack_parameter_count());
72 : }
73 : HValue* GetArgumentsLength() {
74 : // This is initialized in BuildGraph()
75 : DCHECK(arguments_length_ != NULL);
76 : return arguments_length_;
77 : }
78 : CompilationInfo* info() { return info_; }
79 : CodeStub* stub() { return code_stub_; }
80 581974 : HContext* context() { return context_; }
81 23587 : Isolate* isolate() { return info_->isolate(); }
82 :
83 : private:
84 : std::unique_ptr<HParameter* []> parameters_;
85 : HValue* arguments_length_;
86 : CompilationInfo* info_;
87 : CodeStub* code_stub_;
88 : CodeStubDescriptor descriptor_;
89 : HContext* context_;
90 : };
91 :
92 :
93 23533 : bool CodeStubGraphBuilderBase::BuildGraph() {
94 : // Update the static counter each time a new code stub is generated.
95 23533 : isolate()->counters()->code_stubs()->Increment();
96 :
97 23533 : if (FLAG_trace_hydrogen_stubs) {
98 0 : const char* name = CodeStub::MajorName(stub()->MajorKey());
99 0 : PrintF("-----------------------------------------------------------\n");
100 0 : PrintF("Compiling stub %s using hydrogen\n", name);
101 0 : isolate()->GetHTracer()->TraceCompilation(info());
102 : }
103 :
104 : int param_count = GetParameterCount();
105 : int register_param_count = GetRegisterParameterCount();
106 70599 : HEnvironment* start_environment = graph()->start_environment();
107 23533 : HBasicBlock* next_block = CreateBasicBlock(start_environment);
108 : Goto(next_block);
109 23533 : next_block->SetJoinId(BailoutId::StubEntry());
110 : set_current_block(next_block);
111 :
112 23533 : bool runtime_stack_params = descriptor_.stack_parameter_count().is_valid();
113 : HInstruction* stack_parameter_count = NULL;
114 60710 : for (int i = 0; i < param_count; ++i) {
115 37177 : Representation r = GetParameterRepresentation(i);
116 : HParameter* param;
117 37177 : if (i >= register_param_count) {
118 : param = Add<HParameter>(i - register_param_count,
119 0 : HParameter::STACK_PARAMETER, r);
120 : } else {
121 37177 : param = Add<HParameter>(i, HParameter::REGISTER_PARAMETER, r);
122 : }
123 37177 : start_environment->Bind(i, param);
124 74354 : parameters_[i] = param;
125 74354 : if (i < register_param_count && IsParameterCountRegister(i)) {
126 : param->set_type(HType::Smi());
127 : stack_parameter_count = param;
128 0 : arguments_length_ = stack_parameter_count;
129 : }
130 : }
131 :
132 : DCHECK(!runtime_stack_params || arguments_length_ != NULL);
133 23533 : if (!runtime_stack_params) {
134 : stack_parameter_count =
135 23533 : Add<HConstant>(param_count - register_param_count - 1);
136 : // graph()->GetConstantMinus1();
137 23533 : arguments_length_ = graph()->GetConstant0();
138 : }
139 :
140 23533 : context_ = Add<HContext>();
141 : start_environment->BindContext(context_);
142 23533 : start_environment->Bind(param_count, context_);
143 :
144 : Add<HSimulate>(BailoutId::StubEntry());
145 :
146 : NoObservableSideEffectsScope no_effects(this);
147 :
148 23533 : HValue* return_value = BuildCodeStub();
149 :
150 : // We might have extra expressions to pop from the stack in addition to the
151 : // arguments above.
152 : HInstruction* stack_pop_count = stack_parameter_count;
153 23533 : if (descriptor_.function_mode() == JS_FUNCTION_STUB_MODE) {
154 0 : if (!stack_parameter_count->IsConstant() &&
155 : descriptor_.hint_stack_parameter_count() < 0) {
156 0 : HInstruction* constant_one = graph()->GetConstant1();
157 0 : stack_pop_count = AddUncasted<HAdd>(stack_parameter_count, constant_one);
158 : stack_pop_count->ClearFlag(HValue::kCanOverflow);
159 : // TODO(mvstanton): verify that stack_parameter_count+1 really fits in a
160 : // smi.
161 : } else {
162 : int count = descriptor_.hint_stack_parameter_count();
163 0 : stack_pop_count = Add<HConstant>(count);
164 : }
165 : }
166 :
167 23533 : if (current_block() != NULL) {
168 : HReturn* hreturn_instruction = New<HReturn>(return_value,
169 23533 : stack_pop_count);
170 23533 : FinishCurrentBlock(hreturn_instruction);
171 : }
172 23533 : return true;
173 : }
174 :
175 :
176 : template <class Stub>
177 23533 : class CodeStubGraphBuilder: public CodeStubGraphBuilderBase {
178 : public:
179 : explicit CodeStubGraphBuilder(CompilationInfo* info, CodeStub* stub)
180 23533 : : CodeStubGraphBuilderBase(info, stub) {}
181 :
182 : typedef typename Stub::Descriptor Descriptor;
183 :
184 : protected:
185 19612 : virtual HValue* BuildCodeStub() {
186 39224 : if (casted_stub()->IsUninitialized()) {
187 0 : return BuildCodeUninitializedStub();
188 : } else {
189 19612 : return BuildCodeInitializedStub();
190 : }
191 : }
192 :
193 0 : virtual HValue* BuildCodeInitializedStub() {
194 0 : UNIMPLEMENTED();
195 : return NULL;
196 : }
197 :
198 0 : virtual HValue* BuildCodeUninitializedStub() {
199 : // Force a deopt that falls back to the runtime.
200 0 : HValue* undefined = graph()->GetConstantUndefined();
201 0 : IfBuilder builder(this);
202 : builder.IfNot<HCompareObjectEqAndBranch, HValue*>(undefined, undefined);
203 0 : builder.Then();
204 : builder.ElseDeopt(DeoptimizeReason::kForcedDeoptToRuntime);
205 0 : return undefined;
206 : }
207 :
208 43145 : Stub* casted_stub() { return static_cast<Stub*>(stub()); }
209 : };
210 :
211 :
212 8102 : Handle<Code> HydrogenCodeStub::GenerateLightweightMissCode(
213 : ExternalReference miss) {
214 16204 : Factory* factory = isolate()->factory();
215 :
216 : // Generate the new code.
217 8102 : MacroAssembler masm(isolate(), NULL, 256, CodeObjectRequired::kYes);
218 :
219 : {
220 : // Update the static counter each time a new code stub is generated.
221 8102 : isolate()->counters()->code_stubs()->Increment();
222 :
223 : // Generate the code for the stub.
224 : masm.set_generating_stub(true);
225 : // TODO(yangguo): remove this once we can serialize IC stubs.
226 : masm.enable_serializer();
227 : NoCurrentFrameScope scope(&masm);
228 8102 : GenerateLightweightMiss(&masm, miss);
229 : }
230 :
231 : // Create the code object.
232 : CodeDesc desc;
233 8102 : masm.GetCode(&desc);
234 :
235 : // Copy the generated code into a heap object.
236 : Handle<Code> new_object = factory->NewCode(
237 8102 : desc, GetCodeFlags(), masm.CodeObject(), NeedsImmovableCode());
238 16204 : return new_object;
239 : }
240 :
241 0 : Handle<Code> HydrogenCodeStub::GenerateRuntimeTailCall(
242 0 : CodeStubDescriptor* descriptor) {
243 0 : const char* name = CodeStub::MajorName(MajorKey());
244 0 : Zone zone(isolate()->allocator(), ZONE_NAME);
245 0 : CallInterfaceDescriptor interface_descriptor(GetCallInterfaceDescriptor());
246 : compiler::CodeAssemblerState state(isolate(), &zone, interface_descriptor,
247 0 : GetCodeFlags(), name);
248 0 : CodeStubAssembler assembler(&state);
249 : int total_params = interface_descriptor.GetStackParameterCount() +
250 0 : interface_descriptor.GetRegisterParameterCount();
251 0 : switch (total_params) {
252 : case 0:
253 : assembler.TailCallRuntime(descriptor->miss_handler_id(),
254 0 : assembler.Parameter(0));
255 0 : break;
256 : case 1:
257 : assembler.TailCallRuntime(descriptor->miss_handler_id(),
258 0 : assembler.Parameter(1), assembler.Parameter(0));
259 0 : break;
260 : case 2:
261 : assembler.TailCallRuntime(descriptor->miss_handler_id(),
262 : assembler.Parameter(2), assembler.Parameter(0),
263 0 : assembler.Parameter(1));
264 0 : break;
265 : case 3:
266 : assembler.TailCallRuntime(descriptor->miss_handler_id(),
267 : assembler.Parameter(3), assembler.Parameter(0),
268 0 : assembler.Parameter(1), assembler.Parameter(2));
269 0 : break;
270 : case 4:
271 : assembler.TailCallRuntime(descriptor->miss_handler_id(),
272 : assembler.Parameter(4), assembler.Parameter(0),
273 : assembler.Parameter(1), assembler.Parameter(2),
274 0 : assembler.Parameter(3));
275 0 : break;
276 : default:
277 0 : UNIMPLEMENTED();
278 : break;
279 : }
280 0 : return compiler::CodeAssembler::GenerateCode(&state);
281 : }
282 :
283 : template <class Stub>
284 31635 : static Handle<Code> DoGenerateCode(Stub* stub) {
285 55168 : Isolate* isolate = stub->isolate();
286 31635 : CodeStubDescriptor descriptor(stub);
287 :
288 31635 : if (FLAG_minimal && descriptor.has_miss_handler()) {
289 0 : return stub->GenerateRuntimeTailCall(&descriptor);
290 : }
291 :
292 : // If we are uninitialized we can use a light-weight stub to enter
293 : // the runtime that is significantly faster than using the standard
294 : // stub-failure deopt mechanism.
295 31635 : if (stub->IsUninitialized() && descriptor.has_miss_handler()) {
296 : DCHECK(!descriptor.stack_parameter_count().is_valid());
297 8102 : return stub->GenerateLightweightMissCode(descriptor.miss_handler());
298 : }
299 : base::ElapsedTimer timer;
300 23533 : if (FLAG_profile_hydrogen_code_stub_compilation) {
301 : timer.Start();
302 : }
303 47066 : Zone zone(isolate->allocator(), ZONE_NAME);
304 19666 : CompilationInfo info(CStrVector(CodeStub::MajorName(stub->MajorKey())),
305 90265 : isolate, &zone, stub->GetCodeFlags());
306 : // Parameter count is number of stack parameters.
307 : int parameter_count = descriptor.GetStackParameterCount();
308 23533 : if (descriptor.function_mode() == NOT_JS_FUNCTION_STUB_MODE) {
309 23533 : parameter_count--;
310 : }
311 : info.set_parameter_count(parameter_count);
312 : CodeStubGraphBuilder<Stub> builder(&info, stub);
313 23533 : LChunk* chunk = OptimizeGraph(builder.CreateGraph());
314 23533 : Handle<Code> code = chunk->Codegen();
315 23533 : if (FLAG_profile_hydrogen_code_stub_compilation) {
316 0 : OFStream os(stdout);
317 0 : os << "[Lazy compilation of " << stub << " took "
318 0 : << timer.Elapsed().InMillisecondsF() << " ms]" << std::endl;
319 : }
320 23533 : return code;
321 : }
322 :
323 : template <>
324 54 : HValue* CodeStubGraphBuilder<TransitionElementsKindStub>::BuildCodeStub() {
325 : ElementsKind const from_kind = casted_stub()->from_kind();
326 : ElementsKind const to_kind = casted_stub()->to_kind();
327 108 : HValue* const object = GetParameter(Descriptor::kObject);
328 : HValue* const map = GetParameter(Descriptor::kMap);
329 :
330 : // The {object} is known to be a JSObject (otherwise it wouldn't have elements
331 : // anyways).
332 : object->set_type(HType::JSObject());
333 :
334 : info()->MarkAsSavesCallerDoubles();
335 :
336 : DCHECK_IMPLIES(IsFastHoleyElementsKind(from_kind),
337 : IsFastHoleyElementsKind(to_kind));
338 :
339 54 : if (AllocationSite::GetMode(from_kind, to_kind) == TRACK_ALLOCATION_SITE) {
340 37 : Add<HTrapAllocationMemento>(object);
341 : }
342 :
343 54 : if (!IsSimpleMapChangeTransition(from_kind, to_kind)) {
344 54 : HInstruction* elements = AddLoadElements(object);
345 :
346 54 : IfBuilder if_objecthaselements(this);
347 : if_objecthaselements.IfNot<HCompareObjectEqAndBranch>(
348 54 : elements, Add<HConstant>(isolate()->factory()->empty_fixed_array()));
349 54 : if_objecthaselements.Then();
350 : {
351 : // Determine the elements capacity.
352 54 : HInstruction* elements_length = AddLoadFixedArrayLength(elements);
353 :
354 : // Determine the effective (array) length.
355 54 : IfBuilder if_objectisarray(this);
356 54 : if_objectisarray.If<HHasInstanceTypeAndBranch>(object, JS_ARRAY_TYPE);
357 54 : if_objectisarray.Then();
358 : {
359 : // The {object} is a JSArray, load the special "length" property.
360 : Push(Add<HLoadNamedField>(object, nullptr,
361 54 : HObjectAccess::ForArrayLength(from_kind)));
362 : }
363 54 : if_objectisarray.Else();
364 : {
365 : // The {object} is some other JSObject.
366 54 : Push(elements_length);
367 : }
368 54 : if_objectisarray.End();
369 : HValue* length = Pop();
370 :
371 : BuildGrowElementsCapacity(object, elements, from_kind, to_kind, length,
372 54 : elements_length);
373 : }
374 54 : if_objecthaselements.End();
375 : }
376 :
377 54 : Add<HStoreNamedField>(object, HObjectAccess::ForMap(), map);
378 :
379 54 : return object;
380 : }
381 :
382 :
383 54 : Handle<Code> TransitionElementsKindStub::GenerateCode() {
384 54 : return DoGenerateCode(this);
385 : }
386 :
387 : template <>
388 5856 : HValue* CodeStubGraphBuilder<BinaryOpICStub>::BuildCodeInitializedStub() {
389 : BinaryOpICState state = casted_stub()->state();
390 :
391 : HValue* left = GetParameter(Descriptor::kLeft);
392 : HValue* right = GetParameter(Descriptor::kRight);
393 :
394 5856 : AstType* left_type = state.GetLeftType();
395 5856 : AstType* right_type = state.GetRightType();
396 5856 : AstType* result_type = state.GetResultType();
397 :
398 : DCHECK(!left_type->Is(AstType::None()) && !right_type->Is(AstType::None()) &&
399 : (state.HasSideEffects() || !result_type->Is(AstType::None())));
400 :
401 : HValue* result = NULL;
402 : HAllocationMode allocation_mode(NOT_TENURED);
403 13333 : if (state.op() == Token::ADD && (left_type->Maybe(AstType::String()) ||
404 997 : right_type->Maybe(AstType::String())) &&
405 6137 : !left_type->Is(AstType::String()) && !right_type->Is(AstType::String())) {
406 : // For the generic add stub a fast case for string addition is performance
407 : // critical.
408 281 : if (left_type->Maybe(AstType::String())) {
409 189 : IfBuilder if_leftisstring(this);
410 189 : if_leftisstring.If<HIsStringAndBranch>(left);
411 189 : if_leftisstring.Then();
412 : {
413 : Push(BuildBinaryOperation(state.op(), left, right, AstType::String(),
414 : right_type, result_type,
415 189 : state.fixed_right_arg(), allocation_mode));
416 : }
417 189 : if_leftisstring.Else();
418 : {
419 : Push(BuildBinaryOperation(state.op(), left, right, left_type,
420 : right_type, result_type,
421 189 : state.fixed_right_arg(), allocation_mode));
422 : }
423 189 : if_leftisstring.End();
424 : result = Pop();
425 : } else {
426 92 : IfBuilder if_rightisstring(this);
427 92 : if_rightisstring.If<HIsStringAndBranch>(right);
428 92 : if_rightisstring.Then();
429 : {
430 : Push(BuildBinaryOperation(state.op(), left, right, left_type,
431 : AstType::String(), result_type,
432 92 : state.fixed_right_arg(), allocation_mode));
433 : }
434 92 : if_rightisstring.Else();
435 : {
436 : Push(BuildBinaryOperation(state.op(), left, right, left_type,
437 : right_type, result_type,
438 92 : state.fixed_right_arg(), allocation_mode));
439 : }
440 92 : if_rightisstring.End();
441 : result = Pop();
442 : }
443 : } else {
444 : result = BuildBinaryOperation(state.op(), left, right, left_type,
445 : right_type, result_type,
446 5575 : state.fixed_right_arg(), allocation_mode);
447 : }
448 :
449 : // If we encounter a generic argument, the number conversion is
450 : // observable, thus we cannot afford to bail out after the fact.
451 11712 : if (!state.HasSideEffects()) {
452 5083 : result = EnforceNumberType(result, result_type);
453 : }
454 :
455 5856 : return result;
456 : }
457 :
458 :
459 6372 : Handle<Code> BinaryOpICStub::GenerateCode() {
460 6372 : return DoGenerateCode(this);
461 : }
462 :
463 :
464 : template <>
465 3867 : HValue* CodeStubGraphBuilder<BinaryOpWithAllocationSiteStub>::BuildCodeStub() {
466 : BinaryOpICState state = casted_stub()->state();
467 :
468 : HValue* allocation_site = GetParameter(Descriptor::kAllocationSite);
469 : HValue* left = GetParameter(Descriptor::kLeft);
470 : HValue* right = GetParameter(Descriptor::kRight);
471 :
472 3867 : AstType* left_type = state.GetLeftType();
473 3867 : AstType* right_type = state.GetRightType();
474 3867 : AstType* result_type = state.GetResultType();
475 : HAllocationMode allocation_mode(allocation_site);
476 :
477 : return BuildBinaryOperation(state.op(), left, right, left_type, right_type,
478 : result_type, state.fixed_right_arg(),
479 3867 : allocation_mode);
480 : }
481 :
482 :
483 3867 : Handle<Code> BinaryOpWithAllocationSiteStub::GenerateCode() {
484 3867 : return DoGenerateCode(this);
485 : }
486 :
487 :
488 : template <>
489 13756 : HValue* CodeStubGraphBuilder<ToBooleanICStub>::BuildCodeInitializedStub() {
490 : ToBooleanICStub* stub = casted_stub();
491 41268 : IfBuilder if_true(this);
492 13756 : if_true.If<HBranch>(GetParameter(Descriptor::kArgument), stub->hints());
493 13756 : if_true.Then();
494 13756 : if_true.Return(graph()->GetConstantTrue());
495 13756 : if_true.Else();
496 13756 : if_true.End();
497 27512 : return graph()->GetConstantFalse();
498 : }
499 :
500 21342 : Handle<Code> ToBooleanICStub::GenerateCode() { return DoGenerateCode(this); }
501 :
502 : } // namespace internal
503 : } // namespace v8
|