Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(171)

Side by Side Diff: src/x64/lithium-codegen-x64.cc

Issue 18014003: Add X32 port into V8 (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 the V8 project authors. All rights reserved. 1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 } 58 }
59 59
60 private: 60 private:
61 LCodeGen* codegen_; 61 LCodeGen* codegen_;
62 LPointerMap* pointers_; 62 LPointerMap* pointers_;
63 Safepoint::DeoptMode deopt_mode_; 63 Safepoint::DeoptMode deopt_mode_;
64 }; 64 };
65 65
66 66
67 #define __ masm()-> 67 #define __ masm()->
68 #define __k __
69 #define __q __
70 #define __n __
71
68 72
69 bool LCodeGen::GenerateCode() { 73 bool LCodeGen::GenerateCode() {
70 LPhase phase("Z_Code generation", chunk()); 74 LPhase phase("Z_Code generation", chunk());
71 ASSERT(is_unused()); 75 ASSERT(is_unused());
72 status_ = GENERATING; 76 status_ = GENERATING;
73 77
74 // Open a frame scope to indicate that there is a frame on the stack. The 78 // Open a frame scope to indicate that there is a frame on the stack. The
75 // MANUAL indicates that the scope shouldn't actually generate code to set up 79 // MANUAL indicates that the scope shouldn't actually generate code to set up
76 // the frame (that is done in GeneratePrologue). 80 // the frame (that is done in GeneratePrologue).
77 FrameScope frame_scope(masm_, StackFrame::MANUAL); 81 FrameScope frame_scope(masm_, StackFrame::MANUAL);
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 #endif 138 #endif
135 139
136 // Strict mode functions need to replace the receiver with undefined 140 // Strict mode functions need to replace the receiver with undefined
137 // when called as functions (without an explicit receiver 141 // when called as functions (without an explicit receiver
138 // object). rcx is zero for method calls and non-zero for function 142 // object). rcx is zero for method calls and non-zero for function
139 // calls. 143 // calls.
140 if (!info_->is_classic_mode() || info_->is_native()) { 144 if (!info_->is_classic_mode() || info_->is_native()) {
141 Label ok; 145 Label ok;
142 __ testq(rcx, rcx); 146 __ testq(rcx, rcx);
143 __ j(zero, &ok, Label::kNear); 147 __ j(zero, &ok, Label::kNear);
148 #ifndef V8_TARGET_ARCH_X32
144 // +1 for return address. 149 // +1 for return address.
145 int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize; 150 int receiver_offset = (scope()->num_parameters() + 1) * kPointerSize;
151 #else
152 int receiver_offset = 1 * kHWRegSize +
153 scope()->num_parameters() * kPointerSize;
154 #endif
146 __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex); 155 __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex);
147 __ movq(Operand(rsp, receiver_offset), kScratchRegister); 156 __ movq(Operand(rsp, receiver_offset), kScratchRegister);
148 __ bind(&ok); 157 __ bind(&ok);
149 } 158 }
150 } 159 }
151 160
152 info()->set_prologue_offset(masm_->pc_offset()); 161 info()->set_prologue_offset(masm_->pc_offset());
153 if (NeedsEagerFrame()) { 162 if (NeedsEagerFrame()) {
154 ASSERT(!frame_is_built_); 163 ASSERT(!frame_is_built_);
155 frame_is_built_ = true; 164 frame_is_built_ = true;
156 __ push(rbp); // Caller's frame pointer. 165 __ push(rbp); // Caller's frame pointer.
157 __ movq(rbp, rsp); 166 __ movq(rbp, rsp);
158 __ push(rsi); // Callee's context. 167 __ push(rsi); // Callee's context.
159 if (info()->IsStub()) { 168 if (info()->IsStub()) {
160 __ Push(Smi::FromInt(StackFrame::STUB)); 169 __ Push(Smi::FromInt(StackFrame::STUB));
161 } else { 170 } else {
162 __ push(rdi); // Callee's JS function. 171 __ push(rdi); // Callee's JS function.
163 } 172 }
164 info()->AddNoFrameRange(0, masm_->pc_offset()); 173 info()->AddNoFrameRange(0, masm_->pc_offset());
165 } 174 }
166 175
167 // Reserve space for the stack slots needed by the code. 176 // Reserve space for the stack slots needed by the code.
168 int slots = GetStackSlotCount(); 177 int slots = GetStackSlotCount();
169 if (slots > 0) { 178 if (slots > 0) {
170 if (FLAG_debug_code) { 179 if (FLAG_debug_code) {
171 __ subq(rsp, Immediate(slots * kPointerSize)); 180 __ subq(rsp, Immediate(slots * kPointerSize));
172 __ push(rax); 181 __ push(rax);
173 __ Set(rax, slots); 182 __ Set(rax, slots);
174 __ movq(kScratchRegister, kSlotsZapValue, RelocInfo::NONE64); 183 __n movq(kScratchRegister, kSlotsZapValue, RelocInfo::NONE64);
175 Label loop; 184 Label loop;
176 __ bind(&loop); 185 __ bind(&loop);
177 __ movq(MemOperand(rsp, rax, times_pointer_size, 0), 186 __ movq(MemOperand(rsp, rax, times_pointer_size, 0),
178 kScratchRegister); 187 kScratchRegister);
179 __ decl(rax); 188 __ decl(rax);
180 __ j(not_zero, &loop); 189 __ j(not_zero, &loop);
181 __ pop(rax); 190 __ pop(rax);
182 } else { 191 } else {
183 __ subq(rsp, Immediate(slots * kPointerSize)); 192 __ subq(rsp, Immediate(slots * kPointerSize));
184 #ifdef _MSC_VER 193 #ifdef _MSC_VER
(...skipping 939 matching lines...) Expand 10 before | Expand all | Expand 10 after
1124 DeoptimizeIf(overflow, instr->environment()); 1133 DeoptimizeIf(overflow, instr->environment());
1125 } 1134 }
1126 return; 1135 return;
1127 } 1136 }
1128 1137
1129 uint32_t divisor_abs = abs(divisor); 1138 uint32_t divisor_abs = abs(divisor);
1130 if (IsPowerOf2(divisor_abs)) { 1139 if (IsPowerOf2(divisor_abs)) {
1131 int32_t power = WhichPowerOf2(divisor_abs); 1140 int32_t power = WhichPowerOf2(divisor_abs);
1132 if (divisor < 0) { 1141 if (divisor < 0) {
1133 __ movsxlq(result, dividend); 1142 __ movsxlq(result, dividend);
1134 __ neg(result); 1143 __k neg(result);
1135 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { 1144 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
1136 DeoptimizeIf(zero, instr->environment()); 1145 DeoptimizeIf(zero, instr->environment());
1137 } 1146 }
1138 __ sar(result, Immediate(power)); 1147 __k sar(result, Immediate(power));
1139 } else { 1148 } else {
1140 if (!result.is(dividend)) { 1149 if (!result.is(dividend)) {
1141 __ movl(result, dividend); 1150 __ movl(result, dividend);
1142 } 1151 }
1143 __ sarl(result, Immediate(power)); 1152 __ sarl(result, Immediate(power));
1144 } 1153 }
1145 } else { 1154 } else {
1146 Register reg1 = ToRegister(instr->temp()); 1155 Register reg1 = ToRegister(instr->temp());
1147 Register reg2 = ToRegister(instr->result()); 1156 Register reg2 = ToRegister(instr->result());
1148 1157
1149 // Find b which: 2^b < divisor_abs < 2^(b+1). 1158 // Find b which: 2^b < divisor_abs < 2^(b+1).
1150 unsigned b = 31 - CompilerIntrinsics::CountLeadingZeros(divisor_abs); 1159 unsigned b = 31 - CompilerIntrinsics::CountLeadingZeros(divisor_abs);
1151 unsigned shift = 32 + b; // Precision +1bit (effectively). 1160 unsigned shift = 32 + b; // Precision +1bit (effectively).
1152 double multiplier_f = 1161 double multiplier_f =
1153 static_cast<double>(static_cast<uint64_t>(1) << shift) / divisor_abs; 1162 static_cast<double>(static_cast<uint64_t>(1) << shift) / divisor_abs;
1154 int64_t multiplier; 1163 int64_t multiplier;
1155 if (multiplier_f - floor(multiplier_f) < 0.5) { 1164 if (multiplier_f - floor(multiplier_f) < 0.5) {
1156 multiplier = static_cast<int64_t>(floor(multiplier_f)); 1165 multiplier = static_cast<int64_t>(floor(multiplier_f));
1157 } else { 1166 } else {
1158 multiplier = static_cast<int64_t>(floor(multiplier_f)) + 1; 1167 multiplier = static_cast<int64_t>(floor(multiplier_f)) + 1;
1159 } 1168 }
1160 // The multiplier is a uint32. 1169 // The multiplier is a uint32.
1161 ASSERT(multiplier > 0 && 1170 ASSERT(multiplier > 0 &&
1162 multiplier < (static_cast<int64_t>(1) << 32)); 1171 multiplier < (static_cast<int64_t>(1) << 32));
1163 // The multiply is int64, so sign-extend to r64. 1172 // The multiply is int64, so sign-extend to r64.
1164 __ movsxlq(reg1, dividend); 1173 __ movsxlq(reg1, dividend);
1165 if (divisor < 0 && 1174 if (divisor < 0 &&
1166 instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { 1175 instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
1167 __ neg(reg1); 1176 __k neg(reg1);
1168 DeoptimizeIf(zero, instr->environment()); 1177 DeoptimizeIf(zero, instr->environment());
1169 } 1178 }
1170 __ movq(reg2, multiplier, RelocInfo::NONE64); 1179 __n movq(reg2, multiplier, RelocInfo::NONE64);
1171 // Result just fit in r64, because it's int32 * uint32. 1180 // Result just fit in r64, because it's int32 * uint32.
1172 __ imul(reg2, reg1); 1181 __k imul(reg2, reg1);
1173 1182
1174 __ addq(reg2, Immediate(1 << 30)); 1183 __k addq(reg2, Immediate(1 << 30));
1175 __ sar(reg2, Immediate(shift)); 1184 __k sar(reg2, Immediate(shift));
1176 } 1185 }
1177 } 1186 }
1178 1187
1179 1188
1180 void LCodeGen::DoDivI(LDivI* instr) { 1189 void LCodeGen::DoDivI(LDivI* instr) {
1181 if (!instr->is_flooring() && instr->hydrogen()->HasPowerOf2Divisor()) { 1190 if (!instr->is_flooring() && instr->hydrogen()->HasPowerOf2Divisor()) {
1182 Register dividend = ToRegister(instr->left()); 1191 Register dividend = ToRegister(instr->left());
1183 int32_t divisor = 1192 int32_t divisor =
1184 HConstant::cast(instr->hydrogen()->right())->Integer32Value(); 1193 HConstant::cast(instr->hydrogen()->right())->Integer32Value();
1185 int32_t test_value = 0; 1194 int32_t test_value = 0;
(...skipping 343 matching lines...) Expand 10 before | Expand all | Expand 10 after
1529 XMMRegister res = ToDoubleRegister(instr->result()); 1538 XMMRegister res = ToDoubleRegister(instr->result());
1530 double v = instr->value(); 1539 double v = instr->value();
1531 uint64_t int_val = BitCast<uint64_t, double>(v); 1540 uint64_t int_val = BitCast<uint64_t, double>(v);
1532 // Use xor to produce +0.0 in a fast and compact way, but avoid to 1541 // Use xor to produce +0.0 in a fast and compact way, but avoid to
1533 // do so if the constant is -0.0. 1542 // do so if the constant is -0.0.
1534 if (int_val == 0) { 1543 if (int_val == 0) {
1535 __ xorps(res, res); 1544 __ xorps(res, res);
1536 } else { 1545 } else {
1537 Register tmp = ToRegister(instr->temp()); 1546 Register tmp = ToRegister(instr->temp());
1538 __ Set(tmp, int_val); 1547 __ Set(tmp, int_val);
1539 __ movq(res, tmp); 1548 __k movq(res, tmp);
1540 } 1549 }
1541 } 1550 }
1542 1551
1543 1552
1544 void LCodeGen::DoConstantT(LConstantT* instr) { 1553 void LCodeGen::DoConstantT(LConstantT* instr) {
1545 Handle<Object> value = instr->value(); 1554 Handle<Object> value = instr->value();
1546 AllowDeferredHandleDereference smi_check; 1555 AllowDeferredHandleDereference smi_check;
1547 if (value->IsSmi()) { 1556 if (value->IsSmi()) {
1548 __ Move(ToRegister(instr->result()), value); 1557 __ Move(ToRegister(instr->result()), value);
1549 } else { 1558 } else {
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
1617 __ cmpq(kScratchRegister, FieldOperand(object, 1626 __ cmpq(kScratchRegister, FieldOperand(object,
1618 JSDate::kCacheStampOffset)); 1627 JSDate::kCacheStampOffset));
1619 __ j(not_equal, &runtime, Label::kNear); 1628 __ j(not_equal, &runtime, Label::kNear);
1620 __ movq(result, FieldOperand(object, JSDate::kValueOffset + 1629 __ movq(result, FieldOperand(object, JSDate::kValueOffset +
1621 kPointerSize * index->value())); 1630 kPointerSize * index->value()));
1622 __ jmp(&done); 1631 __ jmp(&done);
1623 } 1632 }
1624 __ bind(&runtime); 1633 __ bind(&runtime);
1625 __ PrepareCallCFunction(2); 1634 __ PrepareCallCFunction(2);
1626 __ movq(arg_reg_1, object); 1635 __ movq(arg_reg_1, object);
1627 __ movq(arg_reg_2, index, RelocInfo::NONE64); 1636 __n movq(arg_reg_2, index, RelocInfo::NONE64);
1628 __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2); 1637 __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
1629 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 1638 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
1630 __ bind(&done); 1639 __ bind(&done);
1631 } 1640 }
1632 } 1641 }
1633 1642
1634 1643
1635 void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) { 1644 void LCodeGen::DoSeqStringSetChar(LSeqStringSetChar* instr) {
1636 Register string = ToRegister(instr->string()); 1645 Register string = ToRegister(instr->string());
1637 Register index = ToRegister(instr->index()); 1646 Register index = ToRegister(instr->index());
(...skipping 804 matching lines...) Expand 10 before | Expand all | Expand 10 after
2442 Label* map_check) { 2451 Label* map_check) {
2443 { 2452 {
2444 PushSafepointRegistersScope scope(this); 2453 PushSafepointRegistersScope scope(this);
2445 InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>( 2454 InstanceofStub::Flags flags = static_cast<InstanceofStub::Flags>(
2446 InstanceofStub::kNoFlags | InstanceofStub::kCallSiteInlineCheck); 2455 InstanceofStub::kNoFlags | InstanceofStub::kCallSiteInlineCheck);
2447 InstanceofStub stub(flags); 2456 InstanceofStub stub(flags);
2448 2457
2449 __ push(ToRegister(instr->value())); 2458 __ push(ToRegister(instr->value()));
2450 __ PushHeapObject(instr->function()); 2459 __ PushHeapObject(instr->function());
2451 2460
2461 #ifndef V8_TARGET_ARCH_X32
2452 static const int kAdditionalDelta = 10; 2462 static const int kAdditionalDelta = 10;
2463 #else
2464 // Actual size for X32.
2465 static const int kAdditionalDelta = 16;
2466 #endif
2453 int delta = 2467 int delta =
2454 masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta; 2468 masm_->SizeOfCodeGeneratedSince(map_check) + kAdditionalDelta;
2455 ASSERT(delta >= 0); 2469 ASSERT(delta >= 0);
2456 __ push_imm32(delta); 2470 __ push_imm32(delta);
2457 2471
2458 // We are pushing three values on the stack but recording a 2472 // We are pushing three values on the stack but recording a
2459 // safepoint with two arguments because stub is going to 2473 // safepoint with two arguments because stub is going to
2460 // remove the third argument from the stack before jumping 2474 // remove the third argument from the stack before jumping
2461 // to instanceof builtin on the slow path. 2475 // to instanceof builtin on the slow path.
2462 CallCodeGeneric(stub.GetCode(isolate()), 2476 CallCodeGeneric(stub.GetCode(isolate()),
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
2535 no_frame_start = masm_->pc_offset(); 2549 no_frame_start = masm_->pc_offset();
2536 } 2550 }
2537 if (instr->has_constant_parameter_count()) { 2551 if (instr->has_constant_parameter_count()) {
2538 __ Ret((ToInteger32(instr->constant_parameter_count()) + 1) * kPointerSize, 2552 __ Ret((ToInteger32(instr->constant_parameter_count()) + 1) * kPointerSize,
2539 rcx); 2553 rcx);
2540 } else { 2554 } else {
2541 Register reg = ToRegister(instr->parameter_count()); 2555 Register reg = ToRegister(instr->parameter_count());
2542 // The argument count parameter is a smi 2556 // The argument count parameter is a smi
2543 __ SmiToInteger32(reg, reg); 2557 __ SmiToInteger32(reg, reg);
2544 Register return_addr_reg = reg.is(rcx) ? rbx : rcx; 2558 Register return_addr_reg = reg.is(rcx) ? rbx : rcx;
2545 __ pop(return_addr_reg); 2559 __k pop(return_addr_reg);
2546 __ shl(reg, Immediate(kPointerSizeLog2)); 2560 __ shl(reg, Immediate(kPointerSizeLog2));
2547 __ addq(rsp, reg); 2561 __ addq(rsp, reg);
2548 __ jmp(return_addr_reg); 2562 __ jmp(return_addr_reg);
2549 } 2563 }
2550 if (no_frame_start != -1) { 2564 if (no_frame_start != -1) {
2551 info_->AddNoFrameRange(no_frame_start, masm_->pc_offset()); 2565 info_->AddNoFrameRange(no_frame_start, masm_->pc_offset());
2552 } 2566 }
2553 } 2567 }
2554 2568
2555 2569
(...skipping 302 matching lines...) Expand 10 before | Expand all | Expand 10 after
2858 2872
2859 void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) { 2873 void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
2860 Register arguments = ToRegister(instr->arguments()); 2874 Register arguments = ToRegister(instr->arguments());
2861 Register result = ToRegister(instr->result()); 2875 Register result = ToRegister(instr->result());
2862 2876
2863 if (instr->length()->IsConstantOperand() && 2877 if (instr->length()->IsConstantOperand() &&
2864 instr->index()->IsConstantOperand()) { 2878 instr->index()->IsConstantOperand()) {
2865 int const_index = ToInteger32(LConstantOperand::cast(instr->index())); 2879 int const_index = ToInteger32(LConstantOperand::cast(instr->index()));
2866 int const_length = ToInteger32(LConstantOperand::cast(instr->length())); 2880 int const_length = ToInteger32(LConstantOperand::cast(instr->length()));
2867 int index = (const_length - const_index) + 1; 2881 int index = (const_length - const_index) + 1;
2882 #ifndef V8_TARGET_ARCH_X32
2868 __ movq(result, Operand(arguments, index * kPointerSize)); 2883 __ movq(result, Operand(arguments, index * kPointerSize));
2884 #else
2885 __ movl(result, Operand(arguments, index * kPointerSize +
2886 2 * kHWRegSize - 2 * kPointerSize));
2887 #endif
2869 } else { 2888 } else {
2870 Register length = ToRegister(instr->length()); 2889 Register length = ToRegister(instr->length());
2871 // There are two words between the frame pointer and the last argument. 2890 // There are two words between the frame pointer and the last argument.
2872 // Subtracting from length accounts for one of them add one more. 2891 // Subtracting from length accounts for one of them add one more.
2873 if (instr->index()->IsRegister()) { 2892 if (instr->index()->IsRegister()) {
2874 __ subl(length, ToRegister(instr->index())); 2893 __ subl(length, ToRegister(instr->index()));
2875 } else { 2894 } else {
2876 __ subl(length, ToOperand(instr->index())); 2895 __ subl(length, ToOperand(instr->index()));
2877 } 2896 }
2897 #ifndef V8_TARGET_ARCH_X32
2878 __ movq(result, 2898 __ movq(result,
2879 Operand(arguments, length, times_pointer_size, kPointerSize)); 2899 Operand(arguments, length, times_pointer_size, kPointerSize));
2900 #else
2901 // PC and FP are with kHWRegSize.
2902 __ movl(result,
2903 Operand(arguments, length, times_pointer_size,
2904 2 * kHWRegSize - 1 *kPointerSize));
2905 #endif
2880 } 2906 }
2881 } 2907 }
2882 2908
2883 2909
2884 void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) { 2910 void LCodeGen::DoLoadKeyedExternalArray(LLoadKeyed* instr) {
2885 ElementsKind elements_kind = instr->elements_kind(); 2911 ElementsKind elements_kind = instr->elements_kind();
2886 LOperand* key = instr->key(); 2912 LOperand* key = instr->key();
2887 if (!key->IsConstantOperand()) { 2913 if (!key->IsConstantOperand()) {
2888 Register key_reg = ToRegister(key); 2914 Register key_reg = ToRegister(key);
2889 // Even though the HLoad/StoreKeyed (in this case) instructions force 2915 // Even though the HLoad/StoreKeyed (in this case) instructions force
2890 // the input representation for the key to be an integer, the input 2916 // the input representation for the key to be an integer, the input
2891 // gets replaced during bound check elimination with the index argument 2917 // gets replaced during bound check elimination with the index argument
2892 // to the bounds check, which can be tagged, so that case must be 2918 // to the bounds check, which can be tagged, so that case must be
2893 // handled here, too. 2919 // handled here, too.
2894 if (instr->hydrogen()->key()->representation().IsSmi()) { 2920 if (instr->hydrogen()->key()->representation().IsSmi()) {
2895 __ SmiToInteger64(key_reg, key_reg); 2921 __ SmiToInteger64(key_reg, key_reg);
2896 } else if (instr->hydrogen()->IsDehoisted()) { 2922 } else if (instr->hydrogen()->IsDehoisted()) {
2897 // Sign extend key because it could be a 32 bit negative value 2923 // Sign extend key because it could be a 32 bit negative value
2898 // and the dehoisted address computation happens in 64 bits 2924 // and the dehoisted address computation happens in 64 bits
2899 __ movsxlq(key_reg, key_reg); 2925 __k movsxlq(key_reg, key_reg);
2900 } 2926 }
2901 } 2927 }
2902 Operand operand(BuildFastArrayOperand( 2928 Operand operand(BuildFastArrayOperand(
2903 instr->elements(), 2929 instr->elements(),
2904 key, 2930 key,
2905 elements_kind, 2931 elements_kind,
2906 0, 2932 0,
2907 instr->additional_index())); 2933 instr->additional_index()));
2908 2934
2909 if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { 2935 if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
2962 Register key_reg = ToRegister(key); 2988 Register key_reg = ToRegister(key);
2963 // Even though the HLoad/StoreKeyed instructions force the input 2989 // Even though the HLoad/StoreKeyed instructions force the input
2964 // representation for the key to be an integer, the input gets replaced 2990 // representation for the key to be an integer, the input gets replaced
2965 // during bound check elimination with the index argument to the bounds 2991 // during bound check elimination with the index argument to the bounds
2966 // check, which can be tagged, so that case must be handled here, too. 2992 // check, which can be tagged, so that case must be handled here, too.
2967 if (instr->hydrogen()->key()->representation().IsSmi()) { 2993 if (instr->hydrogen()->key()->representation().IsSmi()) {
2968 __ SmiToInteger64(key_reg, key_reg); 2994 __ SmiToInteger64(key_reg, key_reg);
2969 } else if (instr->hydrogen()->IsDehoisted()) { 2995 } else if (instr->hydrogen()->IsDehoisted()) {
2970 // Sign extend key because it could be a 32 bit negative value 2996 // Sign extend key because it could be a 32 bit negative value
2971 // and the dehoisted address computation happens in 64 bits 2997 // and the dehoisted address computation happens in 64 bits
2972 __ movsxlq(key_reg, key_reg); 2998 __k movsxlq(key_reg, key_reg);
2973 } 2999 }
2974 } 3000 }
2975 3001
2976 if (instr->hydrogen()->RequiresHoleCheck()) { 3002 if (instr->hydrogen()->RequiresHoleCheck()) {
2977 int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag + 3003 int offset = FixedDoubleArray::kHeaderSize - kHeapObjectTag +
2978 sizeof(kHoleNanLower32); 3004 sizeof(kHoleNanLower32);
2979 Operand hole_check_operand = BuildFastArrayOperand( 3005 Operand hole_check_operand = BuildFastArrayOperand(
2980 instr->elements(), 3006 instr->elements(),
2981 key, 3007 key,
2982 FAST_DOUBLE_ELEMENTS, 3008 FAST_DOUBLE_ELEMENTS,
(...skipping 21 matching lines...) Expand all
3004 // Even though the HLoad/StoreKeyedFastElement instructions force 3030 // Even though the HLoad/StoreKeyedFastElement instructions force
3005 // the input representation for the key to be an integer, the input 3031 // the input representation for the key to be an integer, the input
3006 // gets replaced during bound check elimination with the index 3032 // gets replaced during bound check elimination with the index
3007 // argument to the bounds check, which can be tagged, so that 3033 // argument to the bounds check, which can be tagged, so that
3008 // case must be handled here, too. 3034 // case must be handled here, too.
3009 if (instr->hydrogen()->key()->representation().IsSmi()) { 3035 if (instr->hydrogen()->key()->representation().IsSmi()) {
3010 __ SmiToInteger64(key_reg, key_reg); 3036 __ SmiToInteger64(key_reg, key_reg);
3011 } else if (instr->hydrogen()->IsDehoisted()) { 3037 } else if (instr->hydrogen()->IsDehoisted()) {
3012 // Sign extend key because it could be a 32 bit negative value 3038 // Sign extend key because it could be a 32 bit negative value
3013 // and the dehoisted address computation happens in 64 bits 3039 // and the dehoisted address computation happens in 64 bits
3014 __ movsxlq(key_reg, key_reg); 3040 __k movsxlq(key_reg, key_reg);
3015 } 3041 }
3016 } 3042 }
3017 3043
3018 // Load the result. 3044 // Load the result.
3019 __ movq(result, 3045 __ movq(result,
3020 BuildFastArrayOperand(instr->elements(), 3046 BuildFastArrayOperand(instr->elements(),
3021 key, 3047 key,
3022 FAST_ELEMENTS, 3048 FAST_ELEMENTS,
3023 FixedArray::kHeaderSize - kHeapObjectTag, 3049 FixedArray::kHeaderSize - kHeapObjectTag,
3024 instr->additional_index())); 3050 instr->additional_index()));
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
3079 3105
3080 Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize(); 3106 Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
3081 CallCode(ic, RelocInfo::CODE_TARGET, instr); 3107 CallCode(ic, RelocInfo::CODE_TARGET, instr);
3082 } 3108 }
3083 3109
3084 3110
3085 void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) { 3111 void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
3086 Register result = ToRegister(instr->result()); 3112 Register result = ToRegister(instr->result());
3087 3113
3088 if (instr->hydrogen()->from_inlined()) { 3114 if (instr->hydrogen()->from_inlined()) {
3089 __ lea(result, Operand(rsp, -2 * kPointerSize)); 3115 __q lea(result, Operand(rsp, -2 * kPointerSize));
3090 } else { 3116 } else {
3091 // Check for arguments adapter frame. 3117 // Check for arguments adapter frame.
3092 Label done, adapted; 3118 Label done, adapted;
3093 __ movq(result, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); 3119 __ movq(result, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
3094 __ Cmp(Operand(result, StandardFrameConstants::kContextOffset), 3120 __ Cmp(Operand(result, StandardFrameConstants::kContextOffset),
3095 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); 3121 Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
3096 __ j(equal, &adapted, Label::kNear); 3122 __ j(equal, &adapted, Label::kNear);
3097 3123
3098 // No arguments adaptor frame. 3124 // No arguments adaptor frame.
3099 __ movq(result, rbp); 3125 __ movq(result, rbp);
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
3201 __ push(receiver); 3227 __ push(receiver);
3202 __ movq(receiver, length); 3228 __ movq(receiver, length);
3203 3229
3204 // Loop through the arguments pushing them onto the execution 3230 // Loop through the arguments pushing them onto the execution
3205 // stack. 3231 // stack.
3206 Label invoke, loop; 3232 Label invoke, loop;
3207 // length is a small non-negative integer, due to the test above. 3233 // length is a small non-negative integer, due to the test above.
3208 __ testl(length, length); 3234 __ testl(length, length);
3209 __ j(zero, &invoke, Label::kNear); 3235 __ j(zero, &invoke, Label::kNear);
3210 __ bind(&loop); 3236 __ bind(&loop);
3237 #ifndef V8_TARGET_ARCH_X32
3211 __ push(Operand(elements, length, times_pointer_size, 1 * kPointerSize)); 3238 __ push(Operand(elements, length, times_pointer_size, 1 * kPointerSize));
3239 #else
3240 __ Push(Operand(elements, length, times_pointer_size,
3241 2 * kHWRegSize - 1 * kPointerSize));
3242 #endif
3212 __ decl(length); 3243 __ decl(length);
3213 __ j(not_zero, &loop); 3244 __ j(not_zero, &loop);
3214 3245
3215 // Invoke the function. 3246 // Invoke the function.
3216 __ bind(&invoke); 3247 __ bind(&invoke);
3217 ASSERT(instr->HasPointerMap()); 3248 ASSERT(instr->HasPointerMap());
3218 LPointerMap* pointers = instr->pointer_map(); 3249 LPointerMap* pointers = instr->pointer_map();
3219 RecordPosition(pointers->position()); 3250 RecordPosition(pointers->position());
3220 SafepointGenerator safepoint_generator( 3251 SafepointGenerator safepoint_generator(
3221 this, pointers, Safepoint::kLazyDeopt); 3252 this, pointers, Safepoint::kLazyDeopt);
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
3304 // is available to write to at this point. 3335 // is available to write to at this point.
3305 if (dont_adapt_arguments) { 3336 if (dont_adapt_arguments) {
3306 __ Set(rax, arity); 3337 __ Set(rax, arity);
3307 } 3338 }
3308 3339
3309 // Invoke function. 3340 // Invoke function.
3310 __ SetCallKind(rcx, call_kind); 3341 __ SetCallKind(rcx, call_kind);
3311 if (function.is_identical_to(info()->closure())) { 3342 if (function.is_identical_to(info()->closure())) {
3312 __ CallSelf(); 3343 __ CallSelf();
3313 } else { 3344 } else {
3345 #ifndef V8_TARGET_ARCH_X32
3314 __ call(FieldOperand(rdi, JSFunction::kCodeEntryOffset)); 3346 __ call(FieldOperand(rdi, JSFunction::kCodeEntryOffset));
3347 #else
3348 __ Call(FieldOperand(rdi, JSFunction::kCodeEntryOffset));
3349 #endif
3315 } 3350 }
3316 3351
3317 // Set up deoptimization. 3352 // Set up deoptimization.
3318 RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0); 3353 RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT, 0);
3319 } else { 3354 } else {
3320 // We need to adapt arguments. 3355 // We need to adapt arguments.
3321 SafepointGenerator generator( 3356 SafepointGenerator generator(
3322 this, pointers, Safepoint::kLazyDeopt); 3357 this, pointers, Safepoint::kLazyDeopt);
3323 ParameterCount count(arity); 3358 ParameterCount count(arity);
3324 ParameterCount expected(formal_parameter_count); 3359 ParameterCount expected(formal_parameter_count);
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
3377 CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr); 3412 CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
3378 // Set the pointer to the new heap number in tmp. 3413 // Set the pointer to the new heap number in tmp.
3379 if (!tmp.is(rax)) { 3414 if (!tmp.is(rax)) {
3380 __ movq(tmp, rax); 3415 __ movq(tmp, rax);
3381 } 3416 }
3382 3417
3383 // Restore input_reg after call to runtime. 3418 // Restore input_reg after call to runtime.
3384 __ LoadFromSafepointRegisterSlot(input_reg, input_reg); 3419 __ LoadFromSafepointRegisterSlot(input_reg, input_reg);
3385 3420
3386 __ bind(&allocated); 3421 __ bind(&allocated);
3387 __ movq(tmp2, FieldOperand(input_reg, HeapNumber::kValueOffset)); 3422 __k movq(tmp2, FieldOperand(input_reg, HeapNumber::kValueOffset));
3388 __ shl(tmp2, Immediate(1)); 3423 __k shl(tmp2, Immediate(1));
3389 __ shr(tmp2, Immediate(1)); 3424 __k shr(tmp2, Immediate(1));
3390 __ movq(FieldOperand(tmp, HeapNumber::kValueOffset), tmp2); 3425 __k movq(FieldOperand(tmp, HeapNumber::kValueOffset), tmp2);
3391 __ StoreToSafepointRegisterSlot(input_reg, tmp); 3426 __ StoreToSafepointRegisterSlot(input_reg, tmp);
3392 3427
3393 __ bind(&done); 3428 __ bind(&done);
3394 } 3429 }
3395 3430
3396 3431
3397 void LCodeGen::EmitIntegerMathAbs(LMathAbs* instr) { 3432 void LCodeGen::EmitIntegerMathAbs(LMathAbs* instr) {
3398 Register input_reg = ToRegister(instr->value()); 3433 Register input_reg = ToRegister(instr->value());
3399 __ testl(input_reg, input_reg); 3434 __ testl(input_reg, input_reg);
3400 Label is_positive; 3435 Label is_positive;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
3446 3481
3447 void LCodeGen::DoMathFloor(LMathFloor* instr) { 3482 void LCodeGen::DoMathFloor(LMathFloor* instr) {
3448 XMMRegister xmm_scratch = xmm0; 3483 XMMRegister xmm_scratch = xmm0;
3449 Register output_reg = ToRegister(instr->result()); 3484 Register output_reg = ToRegister(instr->result());
3450 XMMRegister input_reg = ToDoubleRegister(instr->value()); 3485 XMMRegister input_reg = ToDoubleRegister(instr->value());
3451 3486
3452 if (CpuFeatures::IsSupported(SSE4_1)) { 3487 if (CpuFeatures::IsSupported(SSE4_1)) {
3453 CpuFeatureScope scope(masm(), SSE4_1); 3488 CpuFeatureScope scope(masm(), SSE4_1);
3454 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { 3489 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
3455 // Deoptimize if minus zero. 3490 // Deoptimize if minus zero.
3456 __ movq(output_reg, input_reg); 3491 __k movq(output_reg, input_reg);
3457 __ subq(output_reg, Immediate(1)); 3492 __k subq(output_reg, Immediate(1));
3458 DeoptimizeIf(overflow, instr->environment()); 3493 DeoptimizeIf(overflow, instr->environment());
3459 } 3494 }
3460 __ roundsd(xmm_scratch, input_reg, Assembler::kRoundDown); 3495 __ roundsd(xmm_scratch, input_reg, Assembler::kRoundDown);
3461 __ cvttsd2si(output_reg, xmm_scratch); 3496 __ cvttsd2si(output_reg, xmm_scratch);
3462 __ cmpl(output_reg, Immediate(0x80000000)); 3497 __ cmpl(output_reg, Immediate(0x80000000));
3463 DeoptimizeIf(equal, instr->environment()); 3498 DeoptimizeIf(equal, instr->environment());
3464 } else { 3499 } else {
3465 Label negative_sign, done; 3500 Label negative_sign, done;
3466 // Deoptimize on unordered. 3501 // Deoptimize on unordered.
3467 __ xorps(xmm_scratch, xmm_scratch); // Zero the register. 3502 __ xorps(xmm_scratch, xmm_scratch); // Zero the register.
3468 __ ucomisd(input_reg, xmm_scratch); 3503 __ ucomisd(input_reg, xmm_scratch);
3469 DeoptimizeIf(parity_even, instr->environment()); 3504 DeoptimizeIf(parity_even, instr->environment());
3470 __ j(below, &negative_sign, Label::kNear); 3505 __ j(below, &negative_sign, Label::kNear);
3471 3506
3472 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { 3507 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
3473 // Check for negative zero. 3508 // Check for negative zero.
3474 Label positive_sign; 3509 Label positive_sign;
3475 __ j(above, &positive_sign, Label::kNear); 3510 __ j(above, &positive_sign, Label::kNear);
3476 __ movmskpd(output_reg, input_reg); 3511 __ movmskpd(output_reg, input_reg);
3477 __ testq(output_reg, Immediate(1)); 3512 __k testq(output_reg, Immediate(1));
3478 DeoptimizeIf(not_zero, instr->environment()); 3513 DeoptimizeIf(not_zero, instr->environment());
3479 __ Set(output_reg, 0); 3514 __ Set(output_reg, 0);
3480 __ jmp(&done); 3515 __ jmp(&done);
3481 __ bind(&positive_sign); 3516 __ bind(&positive_sign);
3482 } 3517 }
3483 3518
3484 // Use truncating instruction (OK because input is positive). 3519 // Use truncating instruction (OK because input is positive).
3485 __ cvttsd2si(output_reg, input_reg); 3520 __ cvttsd2si(output_reg, input_reg);
3486 // Overflow is signalled with minint. 3521 // Overflow is signalled with minint.
3487 __ cmpl(output_reg, Immediate(0x80000000)); 3522 __ cmpl(output_reg, Immediate(0x80000000));
(...skipping 16 matching lines...) Expand all
3504 3539
3505 3540
3506 void LCodeGen::DoMathRound(LMathRound* instr) { 3541 void LCodeGen::DoMathRound(LMathRound* instr) {
3507 const XMMRegister xmm_scratch = xmm0; 3542 const XMMRegister xmm_scratch = xmm0;
3508 Register output_reg = ToRegister(instr->result()); 3543 Register output_reg = ToRegister(instr->result());
3509 XMMRegister input_reg = ToDoubleRegister(instr->value()); 3544 XMMRegister input_reg = ToDoubleRegister(instr->value());
3510 static int64_t one_half = V8_INT64_C(0x3FE0000000000000); // 0.5 3545 static int64_t one_half = V8_INT64_C(0x3FE0000000000000); // 0.5
3511 static int64_t minus_one_half = V8_INT64_C(0xBFE0000000000000); // -0.5 3546 static int64_t minus_one_half = V8_INT64_C(0xBFE0000000000000); // -0.5
3512 3547
3513 Label done, round_to_zero, below_one_half, do_not_compensate, restore; 3548 Label done, round_to_zero, below_one_half, do_not_compensate, restore;
3514 __ movq(kScratchRegister, one_half, RelocInfo::NONE64); 3549 __k movq(kScratchRegister, one_half, RelocInfo::NONE64);
3515 __ movq(xmm_scratch, kScratchRegister); 3550 __ movq(xmm_scratch, kScratchRegister);
3516 __ ucomisd(xmm_scratch, input_reg); 3551 __ ucomisd(xmm_scratch, input_reg);
3517 __ j(above, &below_one_half); 3552 __ j(above, &below_one_half);
3518 3553
3519 // CVTTSD2SI rounds towards zero, since 0.5 <= x, we use floor(0.5 + x). 3554 // CVTTSD2SI rounds towards zero, since 0.5 <= x, we use floor(0.5 + x).
3520 __ addsd(xmm_scratch, input_reg); 3555 __ addsd(xmm_scratch, input_reg);
3521 __ cvttsd2si(output_reg, xmm_scratch); 3556 __ cvttsd2si(output_reg, xmm_scratch);
3522 // Overflow is signalled with minint. 3557 // Overflow is signalled with minint.
3523 __ cmpl(output_reg, Immediate(0x80000000)); 3558 __ cmpl(output_reg, Immediate(0x80000000));
3524 __ RecordComment("D2I conversion overflow"); 3559 __ RecordComment("D2I conversion overflow");
3525 DeoptimizeIf(equal, instr->environment()); 3560 DeoptimizeIf(equal, instr->environment());
3526 __ jmp(&done); 3561 __ jmp(&done);
3527 3562
3528 __ bind(&below_one_half); 3563 __ bind(&below_one_half);
3529 __ movq(kScratchRegister, minus_one_half, RelocInfo::NONE64); 3564 __k movq(kScratchRegister, minus_one_half, RelocInfo::NONE64);
3530 __ movq(xmm_scratch, kScratchRegister); 3565 __ movq(xmm_scratch, kScratchRegister);
3531 __ ucomisd(xmm_scratch, input_reg); 3566 __ ucomisd(xmm_scratch, input_reg);
3532 __ j(below_equal, &round_to_zero); 3567 __ j(below_equal, &round_to_zero);
3533 3568
3534 // CVTTSD2SI rounds towards zero, we use ceil(x - (-0.5)) and then 3569 // CVTTSD2SI rounds towards zero, we use ceil(x - (-0.5)) and then
3535 // compare and compensate. 3570 // compare and compensate.
3536 __ movq(kScratchRegister, input_reg); // Back up input_reg. 3571 __k movq(kScratchRegister, input_reg); // Back up input_reg.
3537 __ subsd(input_reg, xmm_scratch); 3572 __ subsd(input_reg, xmm_scratch);
3538 __ cvttsd2si(output_reg, input_reg); 3573 __ cvttsd2si(output_reg, input_reg);
3539 // Catch minint due to overflow, and to prevent overflow when compensating. 3574 // Catch minint due to overflow, and to prevent overflow when compensating.
3540 __ cmpl(output_reg, Immediate(0x80000000)); 3575 __ cmpl(output_reg, Immediate(0x80000000));
3541 __ RecordComment("D2I conversion overflow"); 3576 __ RecordComment("D2I conversion overflow");
3542 DeoptimizeIf(equal, instr->environment()); 3577 DeoptimizeIf(equal, instr->environment());
3543 3578
3544 __ cvtlsi2sd(xmm_scratch, output_reg); 3579 __ cvtlsi2sd(xmm_scratch, output_reg);
3545 __ ucomisd(input_reg, xmm_scratch); 3580 __ ucomisd(input_reg, xmm_scratch);
3546 __ j(equal, &restore, Label::kNear); 3581 __ j(equal, &restore, Label::kNear);
3547 __ subl(output_reg, Immediate(1)); 3582 __ subl(output_reg, Immediate(1));
3548 // No overflow because we already ruled out minint. 3583 // No overflow because we already ruled out minint.
3549 __ bind(&restore); 3584 __ bind(&restore);
3550 __ movq(input_reg, kScratchRegister); // Restore input_reg. 3585 __k movq(input_reg, kScratchRegister); // Restore input_reg.
3551 __ jmp(&done); 3586 __ jmp(&done);
3552 3587
3553 __ bind(&round_to_zero); 3588 __ bind(&round_to_zero);
3554 // We return 0 for the input range [+0, 0.5[, or [-0.5, 0.5[ if 3589 // We return 0 for the input range [+0, 0.5[, or [-0.5, 0.5[ if
3555 // we can ignore the difference between a result of -0 and +0. 3590 // we can ignore the difference between a result of -0 and +0.
3556 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { 3591 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
3557 __ movq(output_reg, input_reg); 3592 __k movq(output_reg, input_reg);
3558 __ testq(output_reg, output_reg); 3593 __k testq(output_reg, output_reg);
3559 __ RecordComment("Minus zero"); 3594 __ RecordComment("Minus zero");
3560 DeoptimizeIf(negative, instr->environment()); 3595 DeoptimizeIf(negative, instr->environment());
3561 } 3596 }
3562 __ Set(output_reg, 0); 3597 __ Set(output_reg, 0);
3563 __ bind(&done); 3598 __ bind(&done);
3564 } 3599 }
3565 3600
3566 3601
3567 void LCodeGen::DoMathSqrt(LMathSqrt* instr) { 3602 void LCodeGen::DoMathSqrt(LMathSqrt* instr) {
3568 XMMRegister input_reg = ToDoubleRegister(instr->value()); 3603 XMMRegister input_reg = ToDoubleRegister(instr->value());
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
3659 // calling convention. 3694 // calling convention.
3660 #ifdef _WIN64 3695 #ifdef _WIN64
3661 ASSERT(ToRegister(instr->global_object()).is(rcx)); 3696 ASSERT(ToRegister(instr->global_object()).is(rcx));
3662 Register global_object = rcx; 3697 Register global_object = rcx;
3663 #else 3698 #else
3664 ASSERT(ToRegister(instr->global_object()).is(rdi)); 3699 ASSERT(ToRegister(instr->global_object()).is(rdi));
3665 Register global_object = rdi; 3700 Register global_object = rdi;
3666 #endif 3701 #endif
3667 3702
3668 static const int kSeedSize = sizeof(uint32_t); 3703 static const int kSeedSize = sizeof(uint32_t);
3704 #ifndef V8_TARGET_ARCH_X32
3669 STATIC_ASSERT(kPointerSize == 2 * kSeedSize); 3705 STATIC_ASSERT(kPointerSize == 2 * kSeedSize);
3706 #endif
3670 3707
3671 __ movq(global_object, 3708 __ movq(global_object,
3672 FieldOperand(global_object, GlobalObject::kNativeContextOffset)); 3709 FieldOperand(global_object, GlobalObject::kNativeContextOffset));
3673 static const int kRandomSeedOffset = 3710 static const int kRandomSeedOffset =
3674 FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize; 3711 FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize;
3675 __ movq(rbx, FieldOperand(global_object, kRandomSeedOffset)); 3712 __ movq(rbx, FieldOperand(global_object, kRandomSeedOffset));
3676 // rbx: FixedArray of the native context's random seeds 3713 // rbx: FixedArray of the native context's random seeds
3677 3714
3678 // Load state[0]. 3715 // Load state[0].
3679 __ movl(rax, FieldOperand(rbx, ByteArray::kHeaderSize)); 3716 __ movl(rax, FieldOperand(rbx, ByteArray::kHeaderSize));
(...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after
4077 // Even though the HLoad/StoreKeyedFastElement instructions force 4114 // Even though the HLoad/StoreKeyedFastElement instructions force
4078 // the input representation for the key to be an integer, the input 4115 // the input representation for the key to be an integer, the input
4079 // gets replaced during bound check elimination with the index 4116 // gets replaced during bound check elimination with the index
4080 // argument to the bounds check, which can be tagged, so that case 4117 // argument to the bounds check, which can be tagged, so that case
4081 // must be handled here, too. 4118 // must be handled here, too.
4082 if (instr->hydrogen()->key()->representation().IsSmi()) { 4119 if (instr->hydrogen()->key()->representation().IsSmi()) {
4083 __ SmiToInteger64(key_reg, key_reg); 4120 __ SmiToInteger64(key_reg, key_reg);
4084 } else if (instr->hydrogen()->IsDehoisted()) { 4121 } else if (instr->hydrogen()->IsDehoisted()) {
4085 // Sign extend key because it could be a 32 bit negative value 4122 // Sign extend key because it could be a 32 bit negative value
4086 // and the dehoisted address computation happens in 64 bits 4123 // and the dehoisted address computation happens in 64 bits
4087 __ movsxlq(key_reg, key_reg); 4124 __k movsxlq(key_reg, key_reg);
4088 } 4125 }
4089 } 4126 }
4090 Operand operand(BuildFastArrayOperand( 4127 Operand operand(BuildFastArrayOperand(
4091 instr->elements(), 4128 instr->elements(),
4092 key, 4129 key,
4093 elements_kind, 4130 elements_kind,
4094 0, 4131 0,
4095 instr->additional_index())); 4132 instr->additional_index()));
4096 4133
4097 if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { 4134 if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
4141 // Even though the HLoad/StoreKeyedFastElement instructions force 4178 // Even though the HLoad/StoreKeyedFastElement instructions force
4142 // the input representation for the key to be an integer, the 4179 // the input representation for the key to be an integer, the
4143 // input gets replaced during bound check elimination with the index 4180 // input gets replaced during bound check elimination with the index
4144 // argument to the bounds check, which can be tagged, so that case 4181 // argument to the bounds check, which can be tagged, so that case
4145 // must be handled here, too. 4182 // must be handled here, too.
4146 if (instr->hydrogen()->key()->representation().IsSmi()) { 4183 if (instr->hydrogen()->key()->representation().IsSmi()) {
4147 __ SmiToInteger64(key_reg, key_reg); 4184 __ SmiToInteger64(key_reg, key_reg);
4148 } else if (instr->hydrogen()->IsDehoisted()) { 4185 } else if (instr->hydrogen()->IsDehoisted()) {
4149 // Sign extend key because it could be a 32 bit negative value 4186 // Sign extend key because it could be a 32 bit negative value
4150 // and the dehoisted address computation happens in 64 bits 4187 // and the dehoisted address computation happens in 64 bits
4151 __ movsxlq(key_reg, key_reg); 4188 __k movsxlq(key_reg, key_reg);
4152 } 4189 }
4153 } 4190 }
4154 4191
4155 if (instr->NeedsCanonicalization()) { 4192 if (instr->NeedsCanonicalization()) {
4156 Label have_value; 4193 Label have_value;
4157 4194
4158 __ ucomisd(value, value); 4195 __ ucomisd(value, value);
4159 __ j(parity_odd, &have_value); // NaN. 4196 __ j(parity_odd, &have_value); // NaN.
4160 4197
4161 __ Set(kScratchRegister, BitCast<uint64_t>( 4198 __ Set(kScratchRegister, BitCast<uint64_t>(
4162 FixedDoubleArray::canonical_not_the_hole_nan_as_double())); 4199 FixedDoubleArray::canonical_not_the_hole_nan_as_double()));
4163 __ movq(value, kScratchRegister); 4200 __k movq(value, kScratchRegister);
4164 4201
4165 __ bind(&have_value); 4202 __ bind(&have_value);
4166 } 4203 }
4167 4204
4168 Operand double_store_operand = BuildFastArrayOperand( 4205 Operand double_store_operand = BuildFastArrayOperand(
4169 instr->elements(), 4206 instr->elements(),
4170 key, 4207 key,
4171 FAST_DOUBLE_ELEMENTS, 4208 FAST_DOUBLE_ELEMENTS,
4172 FixedDoubleArray::kHeaderSize - kHeapObjectTag, 4209 FixedDoubleArray::kHeaderSize - kHeapObjectTag,
4173 instr->additional_index()); 4210 instr->additional_index());
(...skipping 10 matching lines...) Expand all
4184 // Even though the HLoad/StoreKeyedFastElement instructions force 4221 // Even though the HLoad/StoreKeyedFastElement instructions force
4185 // the input representation for the key to be an integer, the 4222 // the input representation for the key to be an integer, the
4186 // input gets replaced during bound check elimination with the index 4223 // input gets replaced during bound check elimination with the index
4187 // argument to the bounds check, which can be tagged, so that case 4224 // argument to the bounds check, which can be tagged, so that case
4188 // must be handled here, too. 4225 // must be handled here, too.
4189 if (instr->hydrogen()->key()->representation().IsSmi()) { 4226 if (instr->hydrogen()->key()->representation().IsSmi()) {
4190 __ SmiToInteger64(key_reg, key_reg); 4227 __ SmiToInteger64(key_reg, key_reg);
4191 } else if (instr->hydrogen()->IsDehoisted()) { 4228 } else if (instr->hydrogen()->IsDehoisted()) {
4192 // Sign extend key because it could be a 32 bit negative value 4229 // Sign extend key because it could be a 32 bit negative value
4193 // and the dehoisted address computation happens in 64 bits 4230 // and the dehoisted address computation happens in 64 bits
4194 __ movsxlq(key_reg, key_reg); 4231 __k movsxlq(key_reg, key_reg);
4195 } 4232 }
4196 } 4233 }
4197 4234
4198 Operand operand = 4235 Operand operand =
4199 BuildFastArrayOperand(instr->elements(), 4236 BuildFastArrayOperand(instr->elements(),
4200 key, 4237 key,
4201 FAST_ELEMENTS, 4238 FAST_ELEMENTS,
4202 FixedArray::kHeaderSize - kHeapObjectTag, 4239 FixedArray::kHeaderSize - kHeapObjectTag,
4203 instr->additional_index()); 4240 instr->additional_index());
4204 if (instr->value()->IsRegister()) { 4241 if (instr->value()->IsRegister()) {
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
4395 DeferredStringCharFromCode* deferred = 4432 DeferredStringCharFromCode* deferred =
4396 new(zone()) DeferredStringCharFromCode(this, instr); 4433 new(zone()) DeferredStringCharFromCode(this, instr);
4397 4434
4398 ASSERT(instr->hydrogen()->value()->representation().IsInteger32()); 4435 ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
4399 Register char_code = ToRegister(instr->char_code()); 4436 Register char_code = ToRegister(instr->char_code());
4400 Register result = ToRegister(instr->result()); 4437 Register result = ToRegister(instr->result());
4401 ASSERT(!char_code.is(result)); 4438 ASSERT(!char_code.is(result));
4402 4439
4403 __ cmpl(char_code, Immediate(String::kMaxOneByteCharCode)); 4440 __ cmpl(char_code, Immediate(String::kMaxOneByteCharCode));
4404 __ j(above, deferred->entry()); 4441 __ j(above, deferred->entry());
4405 __ movsxlq(char_code, char_code); 4442 __k movsxlq(char_code, char_code);
4406 __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex); 4443 __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
4407 __ movq(result, FieldOperand(result, 4444 __ movq(result, FieldOperand(result,
4408 char_code, times_pointer_size, 4445 char_code, times_pointer_size,
4409 FixedArray::kHeaderSize)); 4446 FixedArray::kHeaderSize));
4410 __ CompareRoot(result, Heap::kUndefinedValueRootIndex); 4447 __ CompareRoot(result, Heap::kUndefinedValueRootIndex);
4411 __ j(equal, deferred->entry()); 4448 __ j(equal, deferred->entry());
4412 __ bind(deferred->exit()); 4449 __ bind(deferred->exit());
4413 } 4450 }
4414 4451
4415 4452
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
4467 LOperand* output = instr->result(); 4504 LOperand* output = instr->result();
4468 LOperand* temp = instr->temp(); 4505 LOperand* temp = instr->temp();
4469 4506
4470 __ LoadUint32(ToDoubleRegister(output), 4507 __ LoadUint32(ToDoubleRegister(output),
4471 ToRegister(input), 4508 ToRegister(input),
4472 ToDoubleRegister(temp)); 4509 ToDoubleRegister(temp));
4473 } 4510 }
4474 4511
4475 4512
4476 void LCodeGen::DoNumberTagI(LNumberTagI* instr) { 4513 void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
4514 #ifndef V8_TARGET_ARCH_X32
4477 LOperand* input = instr->value(); 4515 LOperand* input = instr->value();
4478 ASSERT(input->IsRegister() && input->Equals(instr->result())); 4516 ASSERT(input->IsRegister() && input->Equals(instr->result()));
4479 Register reg = ToRegister(input); 4517 Register reg = ToRegister(input);
4480 4518
4481 __ Integer32ToSmi(reg, reg); 4519 __ Integer32ToSmi(reg, reg);
4520 #else
4521 class DeferredNumberTagI: public LDeferredCode {
4522 public:
4523 DeferredNumberTagI(LCodeGen* codegen, LNumberTagI* instr)
4524 : LDeferredCode(codegen), instr_(instr) { }
4525 virtual void Generate() {
4526 codegen()->DoDeferredNumberTagI(instr_);
4527 }
4528 virtual LInstruction* instr() { return instr_; }
4529 private:
4530 LNumberTagI* instr_;
4531 };
4532
4533 LOperand* input = instr->value();
4534 ASSERT(input->IsRegister() && input->Equals(instr->result()));
4535 Register reg = ToRegister(input);
4536
4537 DeferredNumberTagI* deferred = new(zone()) DeferredNumberTagI(this, instr);
4538 __ Integer32ToSmi(reg, reg);
4539 __ j(overflow, deferred->entry());
4540 __ bind(deferred->exit());
4541 #endif
4482 } 4542 }
4483 4543
4484 4544
4545 #ifdef V8_TARGET_ARCH_X32
4546 void LCodeGen::DoDeferredNumberTagI(LNumberTagI* instr) {
4547 Label slow;
4548 Register reg = ToRegister(instr->value());
4549 Register tmp = reg.is(rax) ? kScratchRegister : rax;
4550
4551 // Preserve the value of all registers.
4552 PushSafepointRegistersScope scope(this);
4553
4554 Label done;
4555 // There was overflow, so bits 30 and 31 of the original integer
4556 // disagree. Try to allocate a heap number in new space and store
4557 // the value in there. If that fails, call the runtime system.
4558 __ SmiToInteger32(reg, reg);
4559 __ xorl(reg, Immediate(0x80000000));
4560 __ cvtlsi2sd(xmm0, reg);
4561
4562 if (FLAG_inline_new) {
4563 __ AllocateHeapNumber(reg, tmp, &slow);
4564 __ jmp(&done, Label::kNear);
4565 }
4566
4567 // Slow case: Call the runtime system to do the number allocation.
4568 __ bind(&slow);
4569
4570 // Put a valid pointer value in the stack slot where the result
4571 // register is stored, as this register is in the pointer map, but contains an
4572 // integer value.
4573 __ StoreToSafepointRegisterSlot(reg, Immediate(0));
4574 CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
4575 // Set the pointer to the new heap number in tmp.
4576 if (!reg.is(rax)) __ movl(reg, rax);
4577
4578 // Heap number allocated. Put the value in xmm0 into the value of the
4579 // allocated heap number.
4580 __ bind(&done);
4581 __ movsd(FieldOperand(reg, HeapNumber::kValueOffset), xmm0);
4582 __ StoreToSafepointRegisterSlot(reg, reg);
4583 }
4584 #endif
4585
4586
4485 void LCodeGen::DoNumberTagU(LNumberTagU* instr) { 4587 void LCodeGen::DoNumberTagU(LNumberTagU* instr) {
4486 class DeferredNumberTagU: public LDeferredCode { 4588 class DeferredNumberTagU: public LDeferredCode {
4487 public: 4589 public:
4488 DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr) 4590 DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr)
4489 : LDeferredCode(codegen), instr_(instr) { } 4591 : LDeferredCode(codegen), instr_(instr) { }
4490 virtual void Generate() { 4592 virtual void Generate() {
4491 codegen()->DoDeferredNumberTagU(instr_); 4593 codegen()->DoDeferredNumberTagU(instr_);
4492 } 4594 }
4493 virtual LInstruction* instr() { return instr_; } 4595 virtual LInstruction* instr() { return instr_; }
4494 private: 4596 private:
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
4579 Immediate(kHoleNanUpper32)); 4681 Immediate(kHoleNanUpper32));
4580 Label canonicalize; 4682 Label canonicalize;
4581 __ j(not_equal, &canonicalize); 4683 __ j(not_equal, &canonicalize);
4582 __ addq(rsp, Immediate(kDoubleSize)); 4684 __ addq(rsp, Immediate(kDoubleSize));
4583 __ Move(reg, factory()->the_hole_value()); 4685 __ Move(reg, factory()->the_hole_value());
4584 __ jmp(&done); 4686 __ jmp(&done);
4585 __ bind(&canonicalize); 4687 __ bind(&canonicalize);
4586 __ addq(rsp, Immediate(kDoubleSize)); 4688 __ addq(rsp, Immediate(kDoubleSize));
4587 __ Set(kScratchRegister, BitCast<uint64_t>( 4689 __ Set(kScratchRegister, BitCast<uint64_t>(
4588 FixedDoubleArray::canonical_not_the_hole_nan_as_double())); 4690 FixedDoubleArray::canonical_not_the_hole_nan_as_double()));
4589 __ movq(input_reg, kScratchRegister); 4691 __k movq(input_reg, kScratchRegister);
4590 } 4692 }
4591 4693
4592 __ bind(&no_special_nan_handling); 4694 __ bind(&no_special_nan_handling);
4593 DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr); 4695 DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr);
4594 if (FLAG_inline_new) { 4696 if (FLAG_inline_new) {
4595 __ AllocateHeapNumber(reg, tmp, deferred->entry()); 4697 __ AllocateHeapNumber(reg, tmp, deferred->entry());
4596 } else { 4698 } else {
4597 __ jmp(deferred->entry()); 4699 __ jmp(deferred->entry());
4598 } 4700 }
4599 __ bind(deferred->exit()); 4701 __ bind(deferred->exit());
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
4680 __ bind(&heap_number); 4782 __ bind(&heap_number);
4681 } 4783 }
4682 // Heap number to XMM conversion. 4784 // Heap number to XMM conversion.
4683 __ movsd(result_reg, FieldOperand(input_reg, HeapNumber::kValueOffset)); 4785 __ movsd(result_reg, FieldOperand(input_reg, HeapNumber::kValueOffset));
4684 if (deoptimize_on_minus_zero) { 4786 if (deoptimize_on_minus_zero) {
4685 XMMRegister xmm_scratch = xmm0; 4787 XMMRegister xmm_scratch = xmm0;
4686 __ xorps(xmm_scratch, xmm_scratch); 4788 __ xorps(xmm_scratch, xmm_scratch);
4687 __ ucomisd(xmm_scratch, result_reg); 4789 __ ucomisd(xmm_scratch, result_reg);
4688 __ j(not_equal, &done, Label::kNear); 4790 __ j(not_equal, &done, Label::kNear);
4689 __ movmskpd(kScratchRegister, result_reg); 4791 __ movmskpd(kScratchRegister, result_reg);
4690 __ testq(kScratchRegister, Immediate(1)); 4792 __k testq(kScratchRegister, Immediate(1));
4691 DeoptimizeIf(not_zero, env); 4793 DeoptimizeIf(not_zero, env);
4692 } 4794 }
4693 __ jmp(&done, Label::kNear); 4795 __ jmp(&done, Label::kNear);
4694 } else { 4796 } else {
4695 ASSERT(mode == NUMBER_CANDIDATE_IS_SMI); 4797 ASSERT(mode == NUMBER_CANDIDATE_IS_SMI);
4696 } 4798 }
4697 4799
4698 // Smi to XMM conversion 4800 // Smi to XMM conversion
4699 __ bind(&load_smi); 4801 __ bind(&load_smi);
4700 __ SmiToInteger32(kScratchRegister, input_reg); 4802 __ SmiToInteger32(kScratchRegister, input_reg);
(...skipping 17 matching lines...) Expand all
4718 __ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex); 4820 __ CompareRoot(input_reg, Heap::kUndefinedValueRootIndex);
4719 DeoptimizeIf(not_equal, instr->environment()); 4821 DeoptimizeIf(not_equal, instr->environment());
4720 __ Set(input_reg, 0); 4822 __ Set(input_reg, 0);
4721 __ jmp(&done, Label::kNear); 4823 __ jmp(&done, Label::kNear);
4722 4824
4723 __ bind(&heap_number); 4825 __ bind(&heap_number);
4724 4826
4725 __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset)); 4827 __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
4726 __ cvttsd2siq(input_reg, xmm0); 4828 __ cvttsd2siq(input_reg, xmm0);
4727 __ Set(kScratchRegister, V8_UINT64_C(0x8000000000000000)); 4829 __ Set(kScratchRegister, V8_UINT64_C(0x8000000000000000));
4728 __ cmpq(input_reg, kScratchRegister); 4830 __k cmpq(input_reg, kScratchRegister);
4729 DeoptimizeIf(equal, instr->environment()); 4831 DeoptimizeIf(equal, instr->environment());
4730 } else { 4832 } else {
4731 // Deoptimize if we don't have a heap number. 4833 // Deoptimize if we don't have a heap number.
4732 DeoptimizeIf(not_equal, instr->environment()); 4834 DeoptimizeIf(not_equal, instr->environment());
4733 4835
4734 XMMRegister xmm_temp = ToDoubleRegister(instr->temp()); 4836 XMMRegister xmm_temp = ToDoubleRegister(instr->temp());
4735 __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset)); 4837 __ movsd(xmm0, FieldOperand(input_reg, HeapNumber::kValueOffset));
4736 __ cvttsd2si(input_reg, xmm0); 4838 __ cvttsd2si(input_reg, xmm0);
4737 __ cvtlsi2sd(xmm_temp, input_reg); 4839 __ cvtlsi2sd(xmm_temp, input_reg);
4738 __ ucomisd(xmm0, xmm_temp); 4840 __ ucomisd(xmm0, xmm_temp);
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
4807 LOperand* result = instr->result(); 4909 LOperand* result = instr->result();
4808 ASSERT(result->IsRegister()); 4910 ASSERT(result->IsRegister());
4809 4911
4810 XMMRegister input_reg = ToDoubleRegister(input); 4912 XMMRegister input_reg = ToDoubleRegister(input);
4811 Register result_reg = ToRegister(result); 4913 Register result_reg = ToRegister(result);
4812 4914
4813 if (instr->truncating()) { 4915 if (instr->truncating()) {
4814 // Performs a truncating conversion of a floating point number as used by 4916 // Performs a truncating conversion of a floating point number as used by
4815 // the JS bitwise operations. 4917 // the JS bitwise operations.
4816 __ cvttsd2siq(result_reg, input_reg); 4918 __ cvttsd2siq(result_reg, input_reg);
4817 __ movq(kScratchRegister, 4919 __k movq(kScratchRegister,
4818 V8_INT64_C(0x8000000000000000), 4920 V8_INT64_C(0x8000000000000000),
4819 RelocInfo::NONE64); 4921 RelocInfo::NONE64);
4820 __ cmpq(result_reg, kScratchRegister); 4922 __k cmpq(result_reg, kScratchRegister);
4821 DeoptimizeIf(equal, instr->environment()); 4923 DeoptimizeIf(equal, instr->environment());
4822 } else { 4924 } else {
4823 __ cvttsd2si(result_reg, input_reg); 4925 __ cvttsd2si(result_reg, input_reg);
4824 __ cvtlsi2sd(xmm0, result_reg); 4926 __ cvtlsi2sd(xmm0, result_reg);
4825 __ ucomisd(xmm0, input_reg); 4927 __ ucomisd(xmm0, input_reg);
4826 DeoptimizeIf(not_equal, instr->environment()); 4928 DeoptimizeIf(not_equal, instr->environment());
4827 DeoptimizeIf(parity_even, instr->environment()); // NaN. 4929 DeoptimizeIf(parity_even, instr->environment()); // NaN.
4828 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) { 4930 if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
4829 Label done; 4931 Label done;
4830 // The integer converted back is equal to the original. We 4932 // The integer converted back is equal to the original. We
(...skipping 810 matching lines...) Expand 10 before | Expand all | Expand 10 after
5641 __ negl(index); 5743 __ negl(index);
5642 // Index is now equal to out of object property index plus 1. 5744 // Index is now equal to out of object property index plus 1.
5643 __ movq(object, FieldOperand(object, 5745 __ movq(object, FieldOperand(object,
5644 index, 5746 index,
5645 times_pointer_size, 5747 times_pointer_size,
5646 FixedArray::kHeaderSize - kPointerSize)); 5748 FixedArray::kHeaderSize - kPointerSize));
5647 __ bind(&done); 5749 __ bind(&done);
5648 } 5750 }
5649 5751
5650 5752
5753 #undef __n
5754 #undef __q
5755 #undef __k
5651 #undef __ 5756 #undef __
5652 5757
5653 } } // namespace v8::internal 5758 } } // namespace v8::internal
5654 5759
5655 #endif // V8_TARGET_ARCH_X64 5760 #endif // V8_TARGET_ARCH_X64
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698