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